|
|
| 批处理
|
|
| 特权级
|
|
| user/bin
|
有几个应用
| hello_world:
|
bad_address:
| 访问一个非法的物理地址,测试批处理系统是否会被该错误影响
|
|
| power:
|
|
应用中
|
|
| #[macro_use]
|
| extern crate user_lib;
|
|
|
|
| user/lib.rs
|
| 定义_start
|
3#[linkage = "weak"]
|
| main是weak
|
| 因而bin和这里虽然都有main
|
| 但bin的会覆盖掉这个
|
|
| 封装了一下syscall
|
装成标准库
|
|
|
| user/syscall
|
ABI 或者系统调用
| 应用运行用户态U
|
| ecall
|
| 调用批处理系统提供的接口
|
| 触发Environment call from U-mode 的异常
|
|
|
| Trap 进入 S 模式
|
执行批处理系统针对这个exception提供的服务程序
|
|
|
| 用俩
|
|
syscall
| rust内联汇编
|
| 符合riscv要求
|
| 以寄存器 a0~a2/x10-x12 来保存系统调用的参数
|
| 寄存器 a7/x17 保存 syscall ID
|
| 返回值通过寄存器 a0/x10 传递给局部变量 ret
|
|
|
|
|
| os/src/link_app.S
|
| cargo build时,os/build.rs生成
|
|
|
| 第一个.section .data是一个64bit整数数组
|
| align 3 8bytes/64bit对齐
|
| quad表示8 bytes/64bit
|
| 写入了数组长度3
|
| 和3个app的起始地址
|
|
| 第二个.section .data插入app们的bin
|
|
|
| os/src/batch.rs
|
| AppManager
|
| app管理
|
| lazy_static定义延迟初始化static
|
| UPSafeCell,只允许一个内部可变引用
|
|
| 找到link_app.S中提供的符号_num_app
|
| 解析app数量,和开头address
|
|
|
| load_app
|
| fence.i
|
| 清理i-cache
|
| d-cache数据缓存
|
cpu 访存
|
| i-cache指令缓存
|
cpu 取指令
|
|
| 这里修改了cpu取指的内存区域,需要刷新i-cache
|
|
| 清空app程序加载区
|
| from_raw_parts_mut
|
|
| 将APP_BASE_ADDRESS转为&mut [u8]
|
长度APP_SIZE_LIMIT
|
| 填零
|
|
|
| 获取app的bin
|
| from_raw_parts
|
| 从start拿到end
|
|
| 拷贝app.bin到target地址
|
| dst是
|
APP_BASE_ADDRESS开始
|
长度app_src.len()
|
|
|
|
| 特权级切换
|
|
|
| S特权级
|
Supervisor Mode
|
os使用
|
|
|
|
| CSR
|
| Control and Status Register,控制与状态寄存器
|
| U-Mode下大部分CSR不可直接操作
|
Trap下CSR使用
| CSR 名
|
该 CSR 与 Trap 相关的功能
|
| sstatus
|
SPP 等字段给出 Trap 发生之前 CPU 处在哪个特权级(S/U)等信息
|
| sepc
|
当 Trap 是一个异常的时候,记录 Trap 发生之前执行的最后一条指令的地址
|
| scause
|
描述 Trap 的原因
|
| stval
|
给出 Trap 附加信息
|
| stvec
|
控制 Trap 处理代码的入口地址
| 64 位的 CSR,在中断使能interrupt enable的情况下,保存了中断处理的入口地址
|
| MODE 位于 [1:0],长度为 2 bits;
|
| BASE 位于 [63:2],长度为 62 bits。
|
|
| MODE = 0
|
| stvec Direct 模式,
|
| 进入 S 模式的 Trap 无论原因如何,
|
| 处理 Trap 的入口地址都是 BASE<<2 ,
|
| CPU 会跳转到这个地方进行异常处理。
|
|
|
|
|
| sret
|
| CPU 按照 sstatus 的 SPP 字段设置特权级 U 或者 S ;
|
| CPU 跳转到 sepc 指向的那条instruction,然后继续执行。
|
|
|
|
|
|
|
| app启动,初始化用户态context,切换到用户态执行app
|
|
| app发起syscall,切换到os中处理
|
|
| app出错,os杀死app并加载next app
|
|
| app执行结束,os加载下一个app
|
|
|
| trap
|
从U进S的过程
|
|
|
|
进入trap处理之前
|
| 通过os的专用stack。保存原控制流reg状态
|
| 全局变量形式在.bss段里
|
| .bss应该负责存储全0初始化
|
| objdump看了一下在.rodata里
|
| ds说是llvm干的,认为const不可变,所以.rodata
|
| 而且.rodata可以处理对齐要求,但.bss不能
|
|
Q
|
|
| KernelStack
|
| UserStack
|
| 获得stack top的address
|
| stack向下增长(这个下有歧义,看图其实是向上,因为高地址在下面,向上就是向低地址增长)
|
| 有图three-piece,所以是x86的情况
|
|
|
| 所以stack top是data开始地址+USER_STACK_SIZE
|
|
| 每次sp-8
|
|
| self.data.as_ptr() as usize + USER_STACK_SIZE
|
|
|
| 换stack
|
|
|
|
|
|
|
硬件会
|
| sstatus
|
SPP 字段会被修改为 CPU 当前的特权级(U/S)。
|
|
| sepc
|
被修改为 Trap 处理完成后默认会执行的下一条instruction的address。
|
|
| scause/stval
|
分别会被修改成这次 Trap 的原因以及相关的附加信息。
|
|
| stvec
|
CPU 会跳转到stvec所设置的 Trap 处理entry address,
|
|
| 将当前特权级设置为 S ,
|
| 然后从Trap 处理入口地址处开始执行。
|
|
完成trap,sret
| CPU 按照 sstatus 的 SPP 字段设置特权级 U 或者 S ;
|
| CPU 跳转到 sepc 指向的那条instruction,然后继续执行。
|
|
|
|
|
|
trap上下文结构体
|
| 包含所有reg,x0~x31
|
| +sstatus, spec
|
|
| x0
|
硬编码0,不会变化
|
| x4/tp
|
除非手动出于特殊原因使用,一般不会用,无需保存
|
|
| csr部分
|
进入trap立即覆盖掉 scause/stval/sstatus/sepc 全部或者部分
|
|
|
scause/stval 在 Trap 处理的第一时间就被使用或者是在其他地方保存下来了,因此它没有被修改并造成不良影响的风险。
|
|
|
sstatus/sepc 在 Trap 处理的全程有意义(在 Trap 控制流最后 sret 的时候还用到了它们),而且确实会出现 Trap 嵌套的情况使得它们的值被覆盖掉。
|
|
|
|
|
trap保存和恢复过程
|
| stvec::write
|
设置Direct mode
|
|
|
指向__alltraps的address
|
|
|
|
src/trop/trap.S
|
| __alltraps
|
| 将trap context保存到os stack上
|
| 跳转到使用rust编写的trap_handler
|
|
| align 2^2 4bytes对齐,risc-v特权级规范要求
|
交换sscratch和sp
| 现在sp指向os stack
|
| sscratch指向user stack
|
|
| addi sp, sp, -34*8
|
预分配-38*8字节stack frame
|
|
| 使用sd保存x1,x3,x5-x27
|
| 使用了类似循环宏SAVE_GP来保存大头
|
| 在 trap.S 开头加上 .altmacro 才能正常使用 .rept 命令
|
|
| 跳过x0,x4/tp,x2/sp
|
|
| 因为sp现在指向os stack
|
|
| 保存sp对于时候恢复到U-Mode没有意义
|
|
|
| sstatus给t0/x5
|
| sepc给t1/x6
|
| 继续保存到 os stack
|
|
| sscratch保存到t2/x7
|
这玩意儿现在是U-Mode的sp
|
| 继续保存
|
|
|
| sp/x2给a0/x10
|
根据riscv调用规范,trap_handler的第一个参数是a0/x10
|
|
|
|
trap_handler需要应用程序传来的syscall ID和对应参数
|
|
|
|
由于这些reg的value可能被修改
|
|
|
|
所以不能让trap_handler直接reg value
|
|
|
|
因而使用stack保存的值
|
|
|
| ====指令记录====
|
| addi
|
rd, rs1, imm
|
|
|
rs1+立即数imm保存进rd
|
|
|
例子
| addi sp, sp, -34*8
|
| sp分配38*8字节,因为向下增长,所以-
|
|
|
| sd
|
rs2, offset(rs1)
|
|
|
将rs2 reg数据存储到指定内存地址offset(rs1)
|
|
|
就是rs1+offset的内存地址
|
|
|
例子
| sd x1, 1*8(sp)
|
x1存入sp + 8的内存地址
|
|
|
|
|
| mv
|
rd, rs
|
|
|
伪指令,实际是addi实现
|
|
|
将rs的值复制给rd
|
|
|
例子
| mv a0, sp
|
| 将sp/x2的值复制给a0/x10
|
|
|
|
| TODO
|
| riscv
|
| 已记录
|
| csrr
|
| csrrw
|
|
| __restore
|
| trap_handler返回后调用
|
| 保存在os stack上的trap context 恢复reg
|
| 通过sret回到U继续执行保存在sepc里的下一条instruction
|
|
| sp在__restore作用不同的时候,有可能会改变
|
| 所以先从a0读入sp
|
| sp目前指向os stack
|
| ld从stack top的trap context恢复csr和reg注意顺序
|
| 回收trap context占用stack内存
|
addi sp, sp, 34*8
|
|
| 交换sscratch和sp
|
| sp指向U-Mode stack
|
| sscratch指向S-Mode stack
|
|
| sret回到U-Mode继续运行sepc里的下一条instruction
|
|
|
|
|
|
|
| trap/mod.rs
|
| syscall/mod.rs
|
| syscall/fs.rs
|
|
| 处理trap_handler
|
cx原样返回,因而a0的值一直是os stack top
|
传入的sp的值
|
|
|
根据scause保存的trap原因进行处理
| 发现trap来自U 特权级的 Environment Call
|
| 就是syscall
|
|
|
|
| 因为这个是通过ecall instruction触发的syscall
|
| ecall调用后sepc会设置为ecall instruction所在的address
|
| 为了从ecall的下一条instruction开始执行
|
| +4
|
| __restore在sret后会从+4的sepc开始执行
|
|
|
| cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
|
| a0会变化,这里的a0是cx上下文中,也就是os stack里保存的a0的值,并不是当前reg a0会发生变化
|
| 从trap context取出
|
| syscall ID,a7/x17
|
| syscall三个参数,a0~a2/x10-x12
|
| 传给syscall
|
|
|
| 处理其他错误--
|
|
|
| 调用sys_call
|
只调用
|
|
| sys_write/sys_exit
|
比较直观
|
|
|
| next_app
|
|
| 应用结束或出错,进入下一个
|
| load下一个app, copy到0x80400000
|
load_app
|
|
| 跳转到应用程序入口点 0x80400000;
|
设置sepc
|
| 将stack切换到User-Mode
|
走__restore流程
|
| 恢复sscratch指向os stack
|
|
| 从S-Mode切换到U-Mode
|
设置sstatus
|
|
os stack上压入一个为启动app特殊构造的trap context
| TrapContext
|
| sstatus的SPP设置为User
|
| sepc修改为app入口点entry
|
| sp寄存器为设定的一个stack pointer
|
|
|
再复用__restore
| 17 __restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
|
| 18 APP_BASE_ADDRESS,
|
| 19 USER_STACK.get_sp(),
|
| 20 )) as *const _ as usize);
|
|
| 使用push_context在os上压入一个trap context,
|
| 作为__restore的参数
|
| 因为sp <- a0将参数给到sp,在__restore里sp保存的是os stack top
|
|
|
|
|
|
|
|
Generated
2025-05-05 04:17:35 +0000
25bcfca-dirty
2025-05-04 14:47:56 +0000