From 564506fe09a4475160274c243039c8e2b2eaeb43 Mon Sep 17 00:00:00 2001 From: zhangxinyu <840317537@qq.com> Date: Thu, 18 May 2023 18:44:47 +0800 Subject: [PATCH] =?UTF-8?q?run=5Fnext=5Fapp=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch2/os/src/batch.rs | 69 ++++++++++++++++++++++++++++++++++++++-- ch2/os/src/main.rs | 2 ++ ch2/os/src/trap/mod.rs | 3 +- ch2/os/src/trap/trap.S | 71 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 ch2/os/src/trap/trap.S diff --git a/ch2/os/src/batch.rs b/ch2/os/src/batch.rs index c8c4d72..cd88811 100644 --- a/ch2/os/src/batch.rs +++ b/ch2/os/src/batch.rs @@ -1,6 +1,9 @@ +use core::arch::asm; use lazy_static::*; +use riscv::register::mcause::Trap; use crate::println; use crate::sync::UPSafeCell; +use crate::trap::TrapContext; const USER_STACK_SIZE: usize = 4096 * 2; // 栈大小为8kb const KERNEL_STACK_SIZE: usize = 4096 * 2; @@ -10,7 +13,9 @@ const MAX_APP_NUM: usize = 16; // 系统最大支持的运行程序数量 const APP_BASE_ADDRESS: usize = 0x80400000; // 载入的app的起始的地址 const APP_SIZE_LIMIT: usize = 0x20000; // app的最大的二进制文件能够使用的大小 -// 在此之后应用使用UserStack, 而内核使用KernelStack, entry.asm设置的64k启动栈不再被使用 +// 在此之后 应用使用UserStack, 而内核使用KernelStack, entry.asm设置的64k启动栈不再被使用(首次运行run_next_app时被接管了 mv sp, a0) +// KERNEL_STACK 保存的是 Trap Context + static KERNEL_STACK: [u8; KERNEL_STACK_SIZE] = [0; KERNEL_STACK_SIZE]; static USER_STACK: [u8; USER_STACK_SIZE] = [0; USER_STACK_SIZE]; @@ -52,13 +57,38 @@ impl AppManager{ println!("[kernel] num_app = {}", self.num_app); for i in 0..self.num_app { println!( - "[kernel] app_{} [{:#x}, {:#x})", + "[kernel] app_{} ({:#x}, {:#x})", i, self.app_start_lis[i], self.app_start_lis[i + 1] ); } } + + // 把app_idx位置的app 加载到指定的内存APP_BASE_ADDRESS位置 + unsafe fn load_app(&self, app_idx: usize){ + if app_idx >= self.num_app{ + panic!("All applications completed!") + } + // 清空app执行区域的内存 + core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, APP_SIZE_LIMIT).fill(0); + + // 得到指定app_idx位置的数据(下一个位置的开始 - 需要运行app的开始 = 需要运行app的内存大小 + let app_src = core::slice::from_raw_parts(self.app_start_lis[app_idx] as *const u8, + self.app_start_lis[app_idx + 1] - self.app_start_lis[app_idx]); + + // 把app的代码 copy到指定的运行的区域 + let app_len = app_src.len(); + if app_len > APP_SIZE_LIMIT { + panic!("app memory overrun!") + } + core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, app_len) + .copy_from_slice(app_src); + + // 刷新cache + asm!("fence.i"); + } + } @@ -66,6 +96,41 @@ pub fn init() { APP_MANAGER.exclusive_access().show_app_info(); } +pub fn run_next_app() -> *mut TrapContext{ + // 运行一个新的app + // 把需要执行的指定app, 加载到执行位置APP_BASE_ADDRESS + // app_manager 需要drop 或者在一个作用域中, 因为这个函数不会返回, 后面直接进入用户态了 + { + let mut app_manager = APP_MANAGER.exclusive_access(); + + unsafe{ + app_manager.load_app(app_manager.current_app); + } + app_manager.current_app += 1; + } + + extern "C" { + fn __restore(trap_context_ptr: usize); + } + + unsafe { + // 得到用户栈的栈顶 + let user_stack_top = USER_STACK.as_ptr() as usize + USER_STACK_SIZE; + // 得到用户trap的上下文以及寄存器状态 + let user_trap_context = TrapContext::app_init_context(APP_BASE_ADDRESS, user_stack_top); + + // 把用户trap copy到内核栈, 并把内核栈栈顶返回 + let kernel_stack_top = KERNEL_STACK.as_ptr() as usize + KERNEL_STACK_SIZE; // 现在栈顶和栈底都在一个内存位置 + let kernel_trap_context_ptr = (kernel_stack_top - core::mem::size_of::()) as * mut TrapContext; // 为trap context 分配栈空间 + + unsafe { + *kernel_trap_context_ptr = user_trap_context; + // 返回现在的内核栈顶 + kernel_trap_context_ptr + } + } +} + diff --git a/ch2/os/src/main.rs b/ch2/os/src/main.rs index 4933aed..38b837a 100644 --- a/ch2/os/src/main.rs +++ b/ch2/os/src/main.rs @@ -10,6 +10,7 @@ pub mod lang_items; pub mod sbi; pub mod batch; pub mod sync; +pub mod trap; // 汇编脚本引入, 调整内核的内存布局之后, 会跳入到 rust_main中执行 global_asm!(include_str!("entry.asm")); @@ -35,6 +36,7 @@ pub fn rust_main(){ println!("boot_stack_top_bound: {:#x}, boot_stack_lower_bound: {:#x}", boot_stack_top_bound as usize, boot_stack_lower_bound as usize); batch::init(); + batch::run_next_app(); } /// ## 初始化bss段 diff --git a/ch2/os/src/trap/mod.rs b/ch2/os/src/trap/mod.rs index 019f068..bc6c71e 100644 --- a/ch2/os/src/trap/mod.rs +++ b/ch2/os/src/trap/mod.rs @@ -1 +1,2 @@ -mod context; \ No newline at end of file +mod context; +pub use context::TrapContext; diff --git a/ch2/os/src/trap/trap.S b/ch2/os/src/trap/trap.S new file mode 100644 index 0000000..66444bd --- /dev/null +++ b/ch2/os/src/trap/trap.S @@ -0,0 +1,71 @@ +.altmacro +.macro SAVE_GP n + sd x\n, \n*8(sp) // 将寄存器 x_n 的值保存到栈空间中 +.endm +.macro LOAD_GP n + ld x\n, \n*8(sp) // 从栈空间中加载寄存器 x_n 的值 +.endm +.section .text // 进入 .text 段 +.globl __alltraps // 声明全局符号 __alltraps +.globl __restore // 声明全局符号 __restore +.align 2 // 对齐到 2^2 = 4 字节 + +__alltraps: // __alltraps 符号的实现 + csrrw sp, sscratch, sp // 交换 sp 和 sscratch 寄存器的值 + # 现在 sp 指向内核栈,sscratch 指向用户栈 + # 在内核栈上分配一个 TrapContext + addi sp, sp, -34*8 // 分配 34*8 字节的空间 + # 保存通用寄存器 + sd x1, 1*8(sp) // 保存寄存器 x1 的值 (这一步是为了跳过x0寄存器, 方便下面循环) + # 跳过 sp(x2),后面会再次保存 + sd x3, 3*8(sp) // 保存寄存器 x3 的值 (这一步是为了跳过x4寄存器, 方便下面循环) + # 跳过 tp(x4),应用程序不使用该寄存器 + # 保存 x5~x31 + .set n, 5 // 定义变量 n 的初始值为 5 + .rept 27 // 循环 27 次 + SAVE_GP %n // 保存寄存器 x_n 的值到栈空间中 + .set n, n+1 // 将 n 加 1 + .endr // 结束指令块 + # 我们可以自由使用 t0/t1/t2,因为它们已保存在内核栈上 + csrr t0, sstatus // 读取 sstatus 寄存器的值 + csrr t1, sepc // 读取 sepc 寄存器的值 + sd t0, 32*8(sp) // 保存 sstatus 寄存器的值到栈空间中 + sd t1, 33*8(sp) // 保存 sepc 寄存器的值到栈空间中 + # 从 sscratch 中读取用户栈,并将其保存到内核栈中 + csrr t2, sscratch // 读取 sscratch 寄存器的值 + sd t2, 2*8(sp) // 保存用户栈的地址到内核栈 trap context中 + # 设置 trap_handler(cx: &mut TrapContext) 的输入参数 + mv a0, sp // 将 TrapContext 的地址赋值给 a0 + call trap_handler // 调用 trap_handler 函# 数 + +__restore: // __restore 符号的实现 + # case1: 开始运行应用程序 + # case2: 从处理完中断后返回 U 级别 + mv sp, a0 // 将 a0 中保存的内核栈地址赋值给 sp 这个首次进入是在内核进入首次进入sp 这里会被修改为KERNEL_STACK_SIZE, 被接管 + + # 恢复 sstatus/sepc + ld t0, 32*8(sp) // 从栈空间中读取 sstatus 寄存器的值 + ld t1, 33*8(sp) // 从栈空间中读取 sepc 寄存器的值 + ld t2, 2*8(sp) // 从栈空间中读取用户栈的栈顶地址 + csrw sstatus, t0 // 恢复 sstatus 寄存器的值 + csrw sepc, t1 // 恢复 sepc 寄存器的值 + csrw sscratch, t2 // 将栈指针 用户栈 的值 临时保存到 sscratch 寄存器中 + # 现在 sp 指向内核栈,sscratch 指向用户栈 + + # 恢复通用寄存器,除了 sp/tp 以外的寄存器 + # 跳过x0 + ld x1, 1*8(sp) // 从栈空间中读取寄存器 x1 的值 + # 跳过x2 + ld x3, 3*8(sp) // 从栈空间中读取寄存器 x3 的值 + # 循环恢复 + .set n, 5 // 定义变量 n 的初始值为 5 + .rept 27 // 循环 27 次 + LOAD_GP %n // 从栈空间中加载寄存器 x_n 的值 + .set n, n+1 // 将 n 加 1 + .endr // 结束指令块 + + # 现在 sp 指向内核栈,sscratch 指向用户栈, 释放内核栈中的中的 TrapContext, 陷入已经执行完成, 即将返回用户态 不需要这个上下文了 + addi sp, sp, 34*8 // 释放 sizeof + + csrrw sp, sscratch, sp // 交换 sp内核栈 和 sscratch用户栈 寄存器的值, 交换之后sscratch保存的是内核栈顶, sp是用户栈 USER_STACK(栈底) 的栈顶 + sret // 返回指令, 根据sstatus(陷入/异常之前的特权级) 和 sepc(陷入/异常 之前的pc)返回 \ No newline at end of file