|
|
|
@ -4,19 +4,22 @@
|
|
|
|
|
.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 字节的空间
|
|
|
|
|
# 交换 sp 和 sscratch 寄存器的值
|
|
|
|
|
# sscratch在陷入之前是用户空间 次高的页, 是trap context
|
|
|
|
|
# 这里和sp交换之后, sscratch变成了用户栈顶, sp变成了trap context的位置
|
|
|
|
|
csrrw sp, sscratch, sp
|
|
|
|
|
|
|
|
|
|
# 保存通用寄存器
|
|
|
|
|
sd x1, 1*8(sp) # 保存寄存器 x1 的值 (这一步是为了跳过x0寄存器, 方便下面循环)
|
|
|
|
|
# 跳过 sp(x2),后面会再次保存
|
|
|
|
@ -25,36 +28,57 @@ __alltraps: # __alltraps 符号的实现
|
|
|
|
|
# 保存 x5~x31
|
|
|
|
|
.set n, 5 # 定义变量 n 的初始值为 5
|
|
|
|
|
.rept 27 # 循环 27 次
|
|
|
|
|
SAVE_GP %n # 保存寄存器 x_n 的值到栈空间中
|
|
|
|
|
SAVE_GP %n # 保存寄存器 x_n 的值到trap context中
|
|
|
|
|
.set n, n+1 # 将 n 加 1
|
|
|
|
|
.endr # 结束指令块
|
|
|
|
|
# 我们可以自由使用 t0/t1/t2,因为它们已保存在内核栈上
|
|
|
|
|
|
|
|
|
|
# 我们可以自由使用 t0/t1/t2,因为它们已保存在 trap context中
|
|
|
|
|
# 保存CSR寄存器
|
|
|
|
|
csrr t0, sstatus # 读取 sstatus 寄存器的值
|
|
|
|
|
csrr t1, sepc # 读取 sepc 寄存器的值
|
|
|
|
|
sd t0, 32*8(sp) # 保存 sstatus 寄存器的值到栈空间中
|
|
|
|
|
sd t1, 33*8(sp) # 保存 sepc 寄存器的值到栈空间中
|
|
|
|
|
# 从 sscratch 中读取用户栈,并将其保存到内核栈中
|
|
|
|
|
sd t0, 32*8(sp) # 保存 sstatus 寄存器的值到trap context中
|
|
|
|
|
sd t1, 33*8(sp) # 保存 sepc 寄存器的值到trap context中
|
|
|
|
|
|
|
|
|
|
# 从 sscratch 中读取用户栈,并将其保存到trap context中
|
|
|
|
|
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 分发函数
|
|
|
|
|
sd t2, 2*8(sp) # 保存用户栈的地址到 trap context中
|
|
|
|
|
|
|
|
|
|
# 以上 通过用户空间 就保存了所有的寄存器, 他们保存在 用户空间的次高虚拟页
|
|
|
|
|
# 下面开始恢复 用户空间和 内核栈 并跳转到处理函数, 这是在内核初始化该应用的时候, 设置好的
|
|
|
|
|
|
|
|
|
|
# 等下需要加载内核的地址空间, 它是 KERNEL_SPACE.exclusive_access().token(), 他保存在了trap context中
|
|
|
|
|
ld t0, 34*8(sp)
|
|
|
|
|
|
|
|
|
|
# 等下jmp 到我们自己的trap 处理函数 他是 trap_handler as usize(这里直接as usize 转为地址, 还可以跳转到这个地址, 是因为我们是在切换内存空间之后jmp的)
|
|
|
|
|
ld t1, 36*8(sp)
|
|
|
|
|
|
|
|
|
|
# 切换栈, 上面 sp 是 trap context, 现在换成 trap context内保存的 用户内核栈
|
|
|
|
|
ld sp, 35*8(sp)
|
|
|
|
|
|
|
|
|
|
# 切换页表
|
|
|
|
|
csrw satp, t0
|
|
|
|
|
sfence.vma
|
|
|
|
|
|
|
|
|
|
# 防止编译期使用相对位置call, 这时候, 已经进入内核的地址空间了, 防止编译器在链接的时候, 对call进行相对位置偏移call, 那肯定是不对的
|
|
|
|
|
jr t1
|
|
|
|
|
|
|
|
|
|
__restore: # __restore 符号的实现
|
|
|
|
|
# case1: 开始运行应用程序
|
|
|
|
|
# case2: 从处理完中断后返回 U 级别
|
|
|
|
|
# 这个是有 trap_return 调用的, 调用的时候把 trap_cx_ptr(每个用户程序都统一的地址TRAP_CONTEXT), 和 user_satp传进来
|
|
|
|
|
|
|
|
|
|
# 恢复用户地址空间
|
|
|
|
|
csrw satp, a1
|
|
|
|
|
sfence.vma
|
|
|
|
|
|
|
|
|
|
# 注释, 栈顶已经由 switch 恢复了
|
|
|
|
|
# mv sp, a0 # 将 a0 中保存的内核栈地址赋值给 sp 这个首次进入是在内核进入首次进入sp 这里会被修改为KERNEL_STACK_SIZE, 被接管
|
|
|
|
|
# sscratch当做临时寄存器 保存是用户空间 次高的页, 也就是是trap context
|
|
|
|
|
csrw sscratch, a0
|
|
|
|
|
# 把trap context 也copy到sp中, 下面会进行恢复
|
|
|
|
|
mv sp, a0
|
|
|
|
|
|
|
|
|
|
# 恢复 sstatus/sepc
|
|
|
|
|
ld t0, 32*8(sp) # 从栈空间中读取 sstatus 寄存器的值
|
|
|
|
|
ld t1, 33*8(sp) # 从栈空间中读取 sepc 寄存器的值
|
|
|
|
|
ld t2, 2*8(sp) # 从栈空间中读取用户栈的栈顶地址
|
|
|
|
|
ld t0, 32*8(sp) # 从trap context中读取 sstatus 寄存器的值
|
|
|
|
|
ld t1, 33*8(sp) # 从trap context中读取 sepc 寄存器的值
|
|
|
|
|
csrw sstatus, t0 # 恢复 sstatus 寄存器的值
|
|
|
|
|
csrw sepc, t1 # 恢复 sepc 寄存器的值
|
|
|
|
|
csrw sscratch, t2 # 将栈指针 用户栈 的值 临时保存到 sscratch 寄存器中
|
|
|
|
|
# 现在 sp 指向内核栈,sscratch 指向用户栈
|
|
|
|
|
|
|
|
|
|
# 恢复通用寄存器,除了 sp/tp 以外的寄存器
|
|
|
|
|
# 跳过x0
|
|
|
|
@ -68,8 +92,6 @@ __restore: # __restore 符号的实现
|
|
|
|
|
.set n, n+1 # 将 n 加 1
|
|
|
|
|
.endr # 结束指令块
|
|
|
|
|
|
|
|
|
|
# 现在 sp 指向内核栈,sscratch 指向用户栈, 释放内核栈中的中的 TrapContext, 陷入已经执行完成, 即将返回用户态 不需要这个上下文了
|
|
|
|
|
addi sp, sp, 34*8 # 释放 sizeof<TrapContext>
|
|
|
|
|
|
|
|
|
|
csrrw sp, sscratch, sp # 交换 sp内核栈 和 sscratch用户栈 寄存器的值, 交换之后sscratch保存的是内核栈顶, sp是用户栈 USER_STACK(栈底) 的栈顶
|
|
|
|
|
# 恢复用户栈, 他在 trap context 中
|
|
|
|
|
ld sp, 2*8(sp)
|
|
|
|
|
sret # 返回指令, 根据sstatus(陷入/异常之前的特权级) 和 sepc(陷入/异常 之前的pc)返回
|