luojia65 4 лет назад
Родитель
Сommit
d0f6ed587d

+ 2 - 0
Cargo.toml

@@ -1,4 +1,6 @@
 [workspace]
 members = [
     "rustsbi",
+    "soc/k210",
+    "soc/qemu",
 ]

+ 10 - 0
soc/k210/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "rustsbi-k210"
+version = "0.1.0"
+authors = ["luojia65 <[email protected]>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustsbi = { path = "../../rustsbi" }

+ 11 - 0
soc/k210/src/main.rs

@@ -0,0 +1,11 @@
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+    loop {}
+}
+
+// todo

+ 7 - 0
soc/qemu/.cargo/config.toml

@@ -0,0 +1,7 @@
+[build]
+target = "riscv64imac-unknown-none-elf"
+
+[target.riscv64imac-unknown-none-elf]
+rustflags = [
+    "-C", "link-arg=-Tlink.ld",
+]

+ 19 - 0
soc/qemu/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "rustsbi-qemu"
+version = "0.1.0"
+authors = ["luojia65 <[email protected]>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustsbi = { path = "../../rustsbi" }
+
+linked_list_allocator = "0.8"
+lazy_static = { version = "1", features = ["spin_no_std"] }
+spin = "0.5"
+riscv = { version = "0.6", features = ["inline-asm"] }
+
+# 这几个其实不用,应该使用对应的hal库实现
+embedded-hal = "1.0.0-alpha.1"
+nb = "1"

+ 18 - 0
soc/qemu/build.rs

@@ -0,0 +1,18 @@
+use std::env;
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    // Put the linker script somewhere the linker can find it
+    fs::File::create(out_dir.join("link.ld"))
+        .unwrap()
+        .write_all(include_bytes!("link.ld"))
+        .unwrap();
+    println!("cargo:rustc-link-search={}", out_dir.display());
+
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-changed=link.ld");
+}

+ 13 - 0
soc/qemu/justfile

@@ -0,0 +1,13 @@
+target := "riscv64imac-unknown-none-elf"
+mode := "debug"
+build-path := "../../target/" + target + "/" + mode + "/"
+m-firmware-file := build-path + "rustsbi-qemu"
+m-bin-file := build-path + "rustsbi-qemu.bin"
+
+objcopy := "rust-objcopy --binary-architecture=riscv64"
+
+build: firmware
+    @{{objcopy}} {{m-firmware-file}} --strip-all -O binary {{m-bin-file}}
+
+firmware:
+    @cargo build --target={{target}}

+ 86 - 0
soc/qemu/link.ld

@@ -0,0 +1,86 @@
+MEMORY {
+    /* 存储单元的物理地址 */
+    SRAM : ORIGIN = 0x80000000, LENGTH = 2M
+}
+
+PROVIDE(_stext = 0x80000000);
+PROVIDE(_heap_size = 128K);
+PROVIDE(_hart_stack_size = 64K);
+PROVIDE(_max_hart_id = 7); /* todo */
+
+REGION_ALIAS("REGION_TEXT", SRAM);
+REGION_ALIAS("REGION_RODATA", SRAM);
+REGION_ALIAS("REGION_DATA", SRAM);
+REGION_ALIAS("REGION_BSS", SRAM);
+REGION_ALIAS("REGION_HEAP", SRAM);
+REGION_ALIAS("REGION_STACK", SRAM);
+
+OUTPUT_ARCH(riscv)
+
+ENTRY(_start)
+
+PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
+
+SECTIONS
+{
+    /* .text 字段 */
+    .text _stext : {
+        /* 把 entry 函数放在最前面 */
+        *(.text.entry)
+        /* 要链接的文件的 .text 字段集中放在这里 */
+        *(.text .text.*)
+        _etext = .;
+    } > REGION_TEXT
+
+    /* .rodata 字段 */
+    .rodata : ALIGN(4) {
+        _srodata = .;
+        /* 要链接的文件的 .rodata 字段集中放在这里 */
+        *(.rodata .rodata.*)
+        . = ALIGN(4);
+        _erodata = .;
+    } > REGION_RODATA
+
+    /* .data 字段 */
+    .data : ALIGN(4) { 
+        _sidata = LOADADDR(.data);
+        _sdata = .;
+        /* Must be called __global_pointer$ for linker relaxations to work. */
+        PROVIDE(__global_pointer$ = . + 0x800);
+        /* 要链接的文件的 .data 字段集中放在这里 */
+        *(.sdata .sdata.* .sdata2 .sdata2.*);
+        *(.data .data.*)
+        . = ALIGN(4);
+        _edata = .;
+    } > REGION_DATA
+
+    /* .bss 字段 */
+    .bss (NOLOAD) : {
+        _sbss = .;
+        /* 要链接的文件的 .bss 字段集中放在这里 */
+        *(.sbss .bss .bss.*)
+        . = ALIGN(4);
+        _ebss = .;
+    } > REGION_BSS
+
+    .heap (NOLOAD) : {
+        _sheap = .;
+        . += _heap_size;
+        . = ALIGN(4);
+        _eheap = .;
+    } > REGION_HEAP
+
+    /* fictitious region that represents the memory available for the stack */
+    .stack (NOLOAD) : {
+        _estack = .;
+        . = _stack_start;
+        . = ALIGN(4);
+        _sstack = .;
+    } > REGION_STACK
+
+    /* Discard .eh_frame, we are not doing unwind on panic so it is not needed */
+    /DISCARD/ :
+    {
+        *(.eh_frame .eh_frame_hdr);
+    }
+}

+ 7 - 0
soc/qemu/src/hal.rs

@@ -0,0 +1,7 @@
+// Ref: MeowSBI
+
+mod ns16550a;
+pub use ns16550a::Ns16550a;
+
+mod clint;
+pub use clint::Clint;

+ 71 - 0
soc/qemu/src/hal/clint.rs

@@ -0,0 +1,71 @@
+// 这部分其实是运行时提供的,不应该做到实现库里面
+
+pub struct Clint {
+    base: usize,
+}
+
+impl Clint {
+    pub fn new(base: *mut u8) -> Clint {
+        Clint {
+            base: base as usize,
+        }
+    }
+
+    pub fn get_mtime(&self) -> u64 {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::read_volatile(base.add(0xbff8) as *mut u64)
+        }
+    }
+
+    pub fn set_timer(&mut self, hart_id: usize, instant: u64) {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::write_volatile((base.offset(0x4000) as *mut u64).add(hart_id), instant);
+        }
+    }
+
+    pub fn send_soft(&mut self, hart_id: usize) {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::write_volatile((base as *mut u32).add(hart_id), 1);
+        }
+    }
+
+    // pub fn clear_soft(&mut self, hart_id: usize) {
+    //     unsafe {
+    //         let base = self.base as *mut u8;
+    //         core::ptr::write_volatile((base as *mut u32).add(hart_id), 0);
+    //     }
+    // }
+}
+
+use rustsbi::{HartMask, Ipi, Timer};
+
+impl Ipi for Clint {
+    fn max_hart_id(&self) -> usize {
+        let ans: usize;
+        unsafe {
+            asm!("
+                lui     {ans}, %hi(_max_hart_id)
+                add     {ans}, {ans}, %lo(_max_hart_id)
+            ", ans = out(reg) ans)
+        };
+        ans
+    }
+
+    fn send_ipi_many(&mut self, hart_mask: HartMask) {
+        for i in 0..=self.max_hart_id() {
+            if hart_mask.has_bit(i) {
+                self.send_soft(i);
+            }
+        }
+    }
+}
+
+impl Timer for Clint {
+    fn set_timer(&mut self, time_value: u64) {
+        let this_mhartid = riscv::register::mhartid::read();
+        self.set_timer(this_mhartid, time_value);
+    }
+}

+ 94 - 0
soc/qemu/src/hal/ns16550a.rs

@@ -0,0 +1,94 @@
+use core::convert::Infallible;
+use core::ptr::{read_volatile, write_volatile};
+use embedded_hal::serial::{Read, Write};
+
+pub struct Ns16550a {
+    base: usize,
+    shift: usize,
+}
+
+impl Ns16550a {
+    pub fn new(base: usize, shift: usize, clk: u64, baud: u64) -> Self {
+        // init process; ref: MeowSBI/utils/uart.rs
+        unsafe {
+            write_volatile((base + (offsets::LCR << shift)) as *mut u8, 0x80); // DLAB
+
+            let latch = clk / (16 * baud);
+            write_volatile((base + (offsets::DLL << shift)) as *mut u8, latch as u8);
+            write_volatile(
+                (base + (offsets::DLH << shift)) as *mut u8,
+                (latch >> 8) as u8,
+            );
+
+            write_volatile((base + (offsets::LCR << shift)) as *mut u8, 3); // WLEN8 & !DLAB
+
+            write_volatile((base + (offsets::MCR << shift)) as *mut u8, 0);
+            write_volatile((base + (offsets::IER << shift)) as *mut u8, 0);
+            write_volatile((base + (offsets::FCR << shift)) as *mut u8, 0x7); // FIFO enable + FIFO reset
+
+            // No interrupt for now
+        }
+        // init finished
+        Self { base, shift }
+    }
+}
+
+impl Read<u8> for Ns16550a {
+    // 其实是可能出错的,overrun啊,这些
+    type Error = Infallible;
+
+    fn try_read(&mut self) -> nb::Result<u8, Self::Error> {
+        let pending =
+            unsafe { read_volatile((self.base + (offsets::LSR << self.shift)) as *const u8) }
+                & masks::DR;
+        if pending != 0 {
+            let word =
+                unsafe { read_volatile((self.base + (offsets::RBR << self.shift)) as *const u8) };
+            Ok(word)
+        } else {
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
+impl Write<u8> for Ns16550a {
+    type Error = Infallible;
+
+    fn try_write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
+        // 写,但是不刷新
+        unsafe { write_volatile((self.base + (offsets::THR << self.shift)) as *mut u8, word) };
+        Ok(())
+    }
+
+    fn try_flush(&mut self) -> nb::Result<(), Self::Error> {
+        let pending =
+            unsafe { read_volatile((self.base + (offsets::LSR << self.shift)) as *const u8) }
+                & masks::THRE;
+        if pending != 0 {
+            // 发送已经结束了
+            Ok(())
+        } else {
+            // 发送还没有结束,继续等
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
+mod offsets {
+    pub const RBR: usize = 0x0;
+    pub const THR: usize = 0x0;
+
+    pub const IER: usize = 0x1;
+    pub const FCR: usize = 0x2;
+    pub const LCR: usize = 0x3;
+    pub const MCR: usize = 0x4;
+    pub const LSR: usize = 0x5;
+
+    pub const DLL: usize = 0x0;
+    pub const DLH: usize = 0x1;
+}
+
+mod masks {
+    pub const THRE: u8 = 1 << 5;
+    pub const DR: u8 = 1;
+}

+ 338 - 0
soc/qemu/src/main.rs

@@ -0,0 +1,338 @@
+#![no_std]
+#![no_main]
+#![feature(naked_functions)]
+#![feature(alloc_error_handler)]
+#![feature(llvm_asm)]
+#![feature(asm)]
+#![feature(global_asm)]
+
+mod hal;
+
+use core::alloc::Layout;
+use core::panic::PanicInfo;
+use linked_list_allocator::LockedHeap;
+
+use rustsbi::{println, enter_privileged};
+
+use riscv::register::{
+    mcause::{self, Exception, Interrupt, Trap},
+    mepc, mhartid, mideleg, mie, mip,
+    mstatus::{self, MPP},
+    mtval,
+    mtvec::{self, TrapMode},
+};
+
+#[global_allocator]
+static ALLOCATOR: LockedHeap = LockedHeap::empty();
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+    println!("{}", info);
+    loop {}
+}
+
+#[alloc_error_handler]
+fn oom(_layout: Layout) -> ! {
+    loop {}
+}
+
+// #[export_name = "_mp_hook"]
+pub extern "C" fn _mp_hook() -> bool {
+    mhartid::read() == 0
+}
+
+#[export_name = "_start"]
+#[link_section = ".text.entry"] // this is stable
+#[naked]
+fn main() -> ! {
+    unsafe {
+        llvm_asm!(
+            "
+        csrr    a2, mhartid
+        lui     t0, %hi(_max_hart_id)
+        add     t0, t0, %lo(_max_hart_id)
+        bgtu    a2, t0, _start_abort
+        la      sp, _stack_start
+        lui     t0, %hi(_hart_stack_size)
+        add     t0, t0, %lo(_hart_stack_size)
+    .ifdef __riscv_mul
+        mul     t0, a2, t0
+    .else
+        beqz    a2, 2f  // Jump if single-hart
+        mv      t1, a2
+        mv      t2, t0
+    1:
+        add     t0, t0, t2
+        addi    t1, t1, -1
+        bnez    t1, 1b
+    2:
+    .endif
+        sub     sp, sp, t0
+        csrw    mscratch, zero
+        j _start_success
+        
+    _start_abort:
+        wfi
+        j _start_abort
+    _start_success:
+        
+    "
+        )
+    };
+
+    if _mp_hook() {
+        // init
+    }
+
+    /* setup trap */
+
+    extern "C" {
+        fn _start_trap();
+    }
+    unsafe {
+        mtvec::write(_start_trap as usize, TrapMode::Direct);
+    }
+
+    /* main function start */
+
+    extern "C" {
+        static mut _sheap: u8;
+        static _heap_size: u8;
+    }
+    if mhartid::read() == 0 {
+        let sheap = unsafe { &mut _sheap } as *mut _ as usize;
+        let heap_size = unsafe { &_heap_size } as *const u8 as usize;
+        unsafe {
+            ALLOCATOR.lock().init(sheap, heap_size);
+        }
+
+        // 其实这些参数不用提供,直接通过pac库生成
+        let serial = hal::Ns16550a::new(0x10000000, 0, 11_059_200, 115200);
+
+        // use through macro
+        use rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal;
+        init_legacy_stdio_embedded_hal(serial);
+
+        let clint = hal::Clint::new(0x2000000 as *mut u8);
+        use rustsbi::init_ipi;
+        init_ipi(clint);
+        // todo: do not create two instances
+        let clint = hal::Clint::new(0x2000000 as *mut u8);
+        use rustsbi::init_timer;
+        init_timer(clint);
+
+        println!("[rustsbi] Version 0.1.0");
+
+        println!("{}", rustsbi::LOGO);
+        println!("[rustsbi] Kernel entry: 0x80200000");
+    }
+
+    // 把S的中断全部委托给S层
+    unsafe {
+        mideleg::set_sext();
+        mideleg::set_stimer();
+        mideleg::set_ssoft();
+        mie::set_mext();
+        // 不打开mie::set_mtimer
+        mie::set_msoft();
+    }
+
+    extern "C" {
+        fn _s_mode_start();
+    }
+    unsafe {
+        mepc::write(_s_mode_start as usize);
+        mstatus::set_mpp(MPP::Supervisor);
+        enter_privileged(mhartid::read(), 0x2333333366666666);
+    }
+}
+
+global_asm!(
+        "
+_s_mode_start:
+    .option push
+    .option norelax
+1:
+    auipc ra, %pcrel_hi(1f)
+    ld ra, %pcrel_lo(1b)(ra)
+    jr ra
+    .align  3
+1:
+    .dword 0x80200000
+.option pop
+");
+
+global_asm!(
+    "
+    .equ REGBYTES, 8
+    .macro STORE reg, offset
+        sd  \\reg, \\offset*REGBYTES(sp)
+    .endm
+    .macro LOAD reg, offset
+        ld  \\reg, \\offset*REGBYTES(sp)
+    .endm
+    .section .text
+    .global _start_trap
+    .p2align 2
+_start_trap:
+    csrrw   sp, mscratch, sp
+    bnez    sp, 1f
+    /* from M level, load sp */
+    csrrw   sp, mscratch, zero
+1:
+    addi    sp, sp, -16 * REGBYTES
+    STORE   ra, 0
+    STORE   t0, 1
+    STORE   t1, 2
+    STORE   t2, 3
+    STORE   t3, 4
+    STORE   t4, 5
+    STORE   t5, 6
+    STORE   t6, 7
+    STORE   a0, 8
+    STORE   a1, 9
+    STORE   a2, 10
+    STORE   a3, 11
+    STORE   a4, 12
+    STORE   a5, 13
+    STORE   a6, 14
+    STORE   a7, 15
+    mv      a0, sp
+    call    _start_trap_rust
+    LOAD    ra, 0
+    LOAD    t0, 1
+    LOAD    t1, 2
+    LOAD    t2, 3
+    LOAD    t3, 4
+    LOAD    t4, 5
+    LOAD    t5, 6
+    LOAD    t6, 7
+    LOAD    a0, 8
+    LOAD    a1, 9
+    LOAD    a2, 10
+    LOAD    a3, 11
+    LOAD    a4, 12
+    LOAD    a5, 13
+    LOAD    a6, 14
+    LOAD    a7, 15
+    addi    sp, sp, 16 * REGBYTES
+    csrrw   sp, mscratch, sp
+    mret
+"
+);
+
+// #[doc(hidden)]
+// #[export_name = "_mp_hook"]
+// pub extern "Rust" fn _mp_hook() -> bool {
+//     match mhartid::read() {
+//         0 => true,
+//         _ => loop {
+//             unsafe { riscv::asm::wfi() }
+//         },
+//     }
+// }
+
+#[allow(unused)]
+struct TrapFrame {
+    ra: usize,
+    t0: usize,
+    t1: usize,
+    t2: usize,
+    t3: usize,
+    t4: usize,
+    t5: usize,
+    t6: usize,
+    a0: usize,
+    a1: usize,
+    a2: usize,
+    a3: usize,
+    a4: usize,
+    a5: usize,
+    a6: usize,
+    a7: usize,
+}
+
+#[export_name = "_start_trap_rust"]
+extern "C" fn start_trap_rust(trap_frame: &mut TrapFrame) {
+    let cause = mcause::read().cause();
+    match cause {
+        Trap::Exception(Exception::SupervisorEnvCall) => {
+            let params = [trap_frame.a0, trap_frame.a1, trap_frame.a2, trap_frame.a3];
+            // 调用rust_sbi库的处理函数
+            let ans = rustsbi::ecall(trap_frame.a7, trap_frame.a6, params);
+            // 把返回值送还给TrapFrame
+            trap_frame.a0 = ans.error;
+            trap_frame.a1 = ans.value;
+            // 跳过ecall指令
+            mepc::write(mepc::read().wrapping_add(4));
+        }
+        Trap::Interrupt(Interrupt::MachineSoft) => {
+            // 机器软件中断返回给S层
+            unsafe {
+                mip::set_ssoft();
+                mie::clear_msoft();
+            }
+        }
+        Trap::Interrupt(Interrupt::MachineTimer) => {
+            // 机器时间中断返回给S层
+            unsafe {
+                mip::set_stimer();
+                mie::clear_mtimer();
+            }
+        }
+        Trap::Exception(Exception::IllegalInstruction) => {
+            #[inline]
+            unsafe fn get_vaddr_u32(vaddr: usize) -> u32 {
+                let mut ans: u32;
+                llvm_asm!("
+                    li      t0, (1 << 17)
+                    mv      t1, $1
+                    csrrs   t0, mstatus, t0
+                    lwu     t1, 0(t1)
+                    csrw    mstatus, t0
+                    mv      $0, t1
+                "
+                    :"=r"(ans) 
+                    :"r"(vaddr)
+                    :"t0", "t1");
+                ans
+            }
+            let vaddr = mepc::read();
+            let ins = unsafe { get_vaddr_u32(vaddr) };
+            if ins & 0xFFFFF07F == 0xC0102073 {
+                // rdtime
+                let rd = ((ins >> 7) & 0b1_1111) as u8;
+                // todo: one instance only
+                let clint = hal::Clint::new(0x2000000 as *mut u8);
+                let time_usize = clint.get_mtime() as usize;
+                match rd {
+                    10 => trap_frame.a0 = time_usize,
+                    11 => trap_frame.a1 = time_usize,
+                    12 => trap_frame.a2 = time_usize,
+                    13 => trap_frame.a3 = time_usize,
+                    14 => trap_frame.a4 = time_usize,
+                    15 => trap_frame.a5 = time_usize,
+                    16 => trap_frame.a6 = time_usize,
+                    17 => trap_frame.a7 = time_usize,
+                    5 => trap_frame.t0 = time_usize,
+                    6 => trap_frame.t1 = time_usize,
+                    7 => trap_frame.t2 = time_usize,
+                    28 => trap_frame.t3 = time_usize,
+                    29 => trap_frame.t4 = time_usize,
+                    30 => trap_frame.t5 = time_usize,
+                    31 => trap_frame.t6 = time_usize,
+                    _ => panic!("invalid target"),
+                }
+                mepc::write(mepc::read().wrapping_add(4)); // 跳过指令
+            } else {
+                panic!("invalid instruction, mepc: {:016x?}", mepc::read());
+            }
+        }
+        cause => panic!(
+            "Unhandled exception! mcause: {:?}, mepc: {:016x?}, mtval: {:016x?}",
+            cause,
+            mepc::read(),
+            mtval::read()
+        ),
+    }
+}