文章写的有点墨迹, 我在梳理梳理一下, 其实,就是一个技巧: 内核栈里面放入的是用户态的数据。还有一点, 用户的程序放到一个位置, 内核去这个位置读取数据

之前都是内核态,用的栈也是内核态的sp.

  extern "C" {
        //汇编地址的入口
        fn __restore(cx_addr: usize);
    }
    unsafe {
        // 触发汇编的调用
        __restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
            APP_BASE_ADDRESS,
            USER_STACK.get_sp(),
        )) as *const _ as usize);
    }

参数参数是KERNEL_STACK.push_context的返回地址 。放到寄存器a0中(这个是规范)
看看KERNEL_STACK.push_context 返回的是啥?

impl KernelStack {
    fn get_sp(&self) -> usize {
        self.data.as_ptr() as usize + KERNEL_STACK_SIZE
    }
    pub fn push_context(&self, cx: TrapContext) -> &'static mut TrapContext {
        let cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
        unsafe {
            *cx_ptr = cx;
        }
        unsafe { cx_ptr.as_mut().unwrap() }
    }
}

返回的是一个固定内存地址减去一个上下文环境这么大的地址。从这个地址存入的是一个用户态的上下文。
再看看risc-v 汇编怎么用这个a0

__restore:    
    #sp 是内核栈减去一个app地址。之后的内存空间的都是用户态的上下文数据
    #sp 是内核的栈,只不过里面放的用户态的数据,后面取出来的也是用户态数据而已。    
    mv sp, a0
    # restore sstatus/sepc
    ld t0, 32*8(sp)
    ld t1, 33*8(sp)
    ld t2, 2*8(sp)
    csrw sstatus, t0
    csrw sepc, t1
    csrw sscratch, t2 #用户态的数据
    ld x1, 1*8(sp)
    ld x3, 3*8(sp)
    .set n, 5
    .rept 27
        LOAD_GP %n
        .set n, n+1
    .endr
       #sp的空间是自己维护,用完了在还原, 好让下一个app,可以在用这个地址空间
    addi sp, sp, 34*8
    #sscratch 指向了用户栈.
    csrrw sp, sscratch, sp
    #触发用户态执行了, pc=sepc,也会用到SPP。
    sret

程序跳到sepc处执行, 这里存的是啥?sepc 是啥时候设置的呢?

 pub fn app_init_context(entry: usize, sp: usize) -> Self {
        let mut sstatus = sstatus::read(); // CSR sstatus
        sstatus.set_spp(SPP::User); //previous privilege mode: user mode
        let mut cx = Self {
            x: [0; 32],
            sstatus,
            sepc: entry, // entry point of app
        };
        cx.set_sp(sp); // app's user stack pointer
        cx // return initial Trap Context of app
    }

entry是一定的固定的地址。这个地址放的是啥?一个程序,触发系统调用的程序的。exit(main)

#[no_mangle]
#[link_section = ".text.entry"]
pub extern "C" fn _start() -> ! {
    clear_bss();
    exit(main());//这里相当于一个模版, 可以退出一个程序,啥程序,你可以写一个main程序
    panic!("unreachable after sys_exit!");
}

#[linkage = "weak"]
#[no_mangle]
fn main() -> i32 {
    panic!("Cannot find main!");//大哥让你覆盖, 过来覆盖我吧。
}

一个用户的app,哥来覆盖你。 大哥也叫main这个名字。

#![no_std]
#![no_main]

#[macro_use]
extern crate user_lib;

#[no_mangle]
fn main() -> i32 {
    println!("Hello, world!");
    0
}

exit 这个程序怎么实现的呢?

pub fn sys_exit(exit_code: i32) -> isize {
    syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0])
}

触发了系统调用。回到了中断向量表。

fn syscall(id: usize, args: [usize; 3]) -> isize {
    let mut ret: isize;
    unsafe {
        asm!(
            "ecall",
            inlateout("a0") args[0] => ret,  // 原来的 "x10"
            in("a1") args[1],  // 原来的 "x11"
            in("a2") args[2],  // 原来的 "x12"
            in("a7") id        // 原来的 "x17"
        );
    }
    ret
}

启动的时候,已经配置了。

pub fn init() {
    extern "C" {
        fn __alltraps();
    }
    unsafe {
        //这里只是准备了数据, 但是,并没有切换到过去
        stvec::write(__alltraps as usize, TrapMode::Direct);
    }
}

执行__alltraps的逻辑

__alltraps:
    #sp用户栈, 换了一下,sp就是内核栈了。
    csrrw sp, sscratch, sp
    # 从那个固定的栈地址存储的是用户态的代码。
    addi sp, sp, -34*8
    # save general-purpose registers
    sd x1, 1*8(sp)
    # skip sp(x2), we will save it later
    sd x3, 3*8(sp)
    # skip tp(x4), application does not use it
    # save x5~x31
    .set n, 5
    .rept 27
        SAVE_GP %n
        .set n, n+1
    .endr
    
    csrr t0, sstatus
    csrr t1, sepc
    sd t0, 32*8(sp)
    sd t1, 33*8(sp)
    # read user stack from sscratch and save it on the kernel stack
    csrr t2, sscratch
    sd t2, 2*8(sp)
    #内核态的栈顶,但是,里面存的用户态的信息。 
    mv a0, sp
    call trap_handler

在看看trap_handler干了啥。scause,还是用户态。 所以,走到 Exception::UserEnvCall

#[no_mangle]
/// handle an interrupt, exception, or system call from user space
pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
    let scause = scause::read(); // get trap cause
    let stval = stval::read(); // get extra value
    println!("scause={}", scause.bits());
    match scause.cause() {
        Trap::Exception(Exception::UserEnvCall) => {
            cx.sepc += 4;//用户态的ret 哪一样。之后,执行真正的
            cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
        }
        Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => {
            println!("[kernel] PageFault in application, kernel killed it.");
            run_next_app();
        }
        Trap::Exception(Exception::IllegalInstruction) => {
            println!("[kernel] IllegalInstruction in application, kernel killed it.");
            run_next_app();
        }
        _ => {
            panic!(
                "Unsupported trap {:?}, stval = {:#x}!",
                scause.cause(),
                stval
            );
        }
    }
    cx
}

putao
8 声望1 粉丝

推动世界向前发展,改善民生。