Skip to content

RISC-V trap出入口笔记

rust
#[repr(C)]
pub struct TrapContext {
    /// general regs[0..31]
    pub x: [usize; 32],
    /// CSR sstatus      
    pub sstatus: Sstatus,
    /// CSR sepc
    pub sepc: usize,
}

trap入口

asm
0000000080200010 <__alltraps>:
// 交换用户栈和内核栈的寄存器位置,这里的sp是用户栈指针,执行后变成了内核栈指针,原本的用户栈现在存放到了sscratch寄存器中
80200010: 73 11 01 14  	csrrw	sp, sscratch, sp
// 分配一个陷阱帧 大小为0x100 0x110=272
80200014: 6d 71        	addi	sp, sp, -0x110
// 下面的sd指令都是在保存CPU当前的寄存器状态,将状态保存到刚刚分配的陷阱帧里
80200016: 06 e4        	sd	ra, 0x8(sp)  //保存返回地址寄存器
80200018: 0e ec        	sd	gp, 0x18(sp) //保存全局指针gp
8020001a: 16 f4        	sd	t0, 0x28(sp) //临时寄存器t0-t6
8020001c: 1a f8        	sd	t1, 0x30(sp) //保存寄存器s0-s11
8020001e: 1e fc        	sd	t2, 0x38(sp) //参数/返回值寄存器 a0-a7
80200020: a2 e0        	sd	s0, 0x40(sp)
80200022: a6 e4        	sd	s1, 0x48(sp)
80200024: aa e8        	sd	a0, 0x50(sp)
80200026: ae ec        	sd	a1, 0x58(sp)
80200028: b2 f0        	sd	a2, 0x60(sp)
8020002a: b6 f4        	sd	a3, 0x68(sp)
8020002c: ba f8        	sd	a4, 0x70(sp)
8020002e: be fc        	sd	a5, 0x78(sp)
80200030: 42 e1        	sd	a6, 0x80(sp)
80200032: 46 e5        	sd	a7, 0x88(sp)
80200034: 4a e9        	sd	s2, 0x90(sp)
80200036: 4e ed        	sd	s3, 0x98(sp)
80200038: 52 f1        	sd	s4, 0xa0(sp)
8020003a: 56 f5        	sd	s5, 0xa8(sp)
8020003c: 5a f9        	sd	s6, 0xb0(sp)
8020003e: 5e fd        	sd	s7, 0xb8(sp)
80200040: e2 e1        	sd	s8, 0xc0(sp)
80200042: e6 e5        	sd	s9, 0xc8(sp)
80200044: ea e9        	sd	s10, 0xd0(sp)
80200046: ee ed        	sd	s11, 0xd8(sp)
80200048: f2 f1        	sd	t3, 0xe0(sp)
8020004a: f6 f5        	sd	t4, 0xe8(sp)
8020004c: fa f9        	sd	t5, 0xf0(sp)
8020004e: fe fd        	sd	t6, 0xf8(sp)
80200050: f3 22 00 10  	csrr	t0, sstatus //读取S状态寄存器到t0
80200054: 73 23 10 14  	csrr	t1, sepc    //读取S异常程序计数器到t1
80200058: 16 e2        	sd	t0, 0x100(sp)   //保存t0到陷阱帧
8020005a: 1a e6        	sd	t1, 0x108(sp)   //保存t1到陷阱帧
8020005c: f3 23 00 14  	csrr	t2, sscratch//将sscratch,也就是用户栈指针读取到t2中
80200060: 1e e8        	sd	t2, 0x10(sp)    //将t2,也就是用户栈指针保存到陷阱帧中
80200062: 0a 85        	mv	a0, sp          //a0寄存器是参数寄存器,将sp赋给a0作为参数传递给接下来的函数
80200064: 97 10 00 00  	auipc	ra, 0x1     //接下来两个指令是跳转到trap_handler去执行陷阱指令
80200068: e7 80 a0 ed  	jalr	-0x126(ra) <trap_handler>

trap出口

asm
000000008020006c <__restore>:
8020006c: 2a 81        	mv	sp, a0          //返回值一般都在a0中,所以这里将a0的陷阱帧地址放到sp寄存器中,sp现在是内核上下文
8020006e: 92 62        	ld	t0, 0x100(sp)   //将sstatus加载到t0
80200070: 32 63        	ld	t1, 0x108(sp)   //将S异常程序计数器加载到t1
80200072: c2 63        	ld	t2, 0x10(sp)    //将sscratch加载到t2
80200074: 73 90 02 10  	csrw	sstatus, t0 //写入sstatus
80200078: 73 10 13 14  	csrw	sepc, t1    //写入sepc
8020007c: 73 90 03 14  	csrw	sscratch, t2//写入sscratch
80200080: a2 60        	ld	ra, 0x8(sp)     //开始恢复通用寄存器
80200082: e2 61        	ld	gp, 0x18(sp)
80200084: a2 72        	ld	t0, 0x28(sp)
80200086: 42 73        	ld	t1, 0x30(sp)
80200088: e2 73        	ld	t2, 0x38(sp)
8020008a: 06 64        	ld	s0, 0x40(sp)
8020008c: a6 64        	ld	s1, 0x48(sp)
8020008e: 46 65        	ld	a0, 0x50(sp)
80200090: e6 65        	ld	a1, 0x58(sp)
80200092: 06 76        	ld	a2, 0x60(sp)
80200094: a6 76        	ld	a3, 0x68(sp)
80200096: 46 77        	ld	a4, 0x70(sp)
80200098: e6 77        	ld	a5, 0x78(sp)
8020009a: 0a 68        	ld	a6, 0x80(sp)
8020009c: aa 68        	ld	a7, 0x88(sp)
8020009e: 4a 69        	ld	s2, 0x90(sp)
802000a0: ea 69        	ld	s3, 0x98(sp)
802000a2: 0a 7a        	ld	s4, 0xa0(sp)
802000a4: aa 7a        	ld	s5, 0xa8(sp)
802000a6: 4a 7b        	ld	s6, 0xb0(sp)
802000a8: ea 7b        	ld	s7, 0xb8(sp)
802000aa: 0e 6c        	ld	s8, 0xc0(sp)
802000ac: ae 6c        	ld	s9, 0xc8(sp)
802000ae: 4e 6d        	ld	s10, 0xd0(sp)
802000b0: ee 6d        	ld	s11, 0xd8(sp)
802000b2: 0e 7e        	ld	t3, 0xe0(sp)
802000b4: ae 7e        	ld	t4, 0xe8(sp)
802000b6: 4e 7f        	ld	t5, 0xf0(sp)
802000b8: ee 7f        	ld	t6, 0xf8(sp)         //通用寄存器恢复结束
802000ba: 51 61        	addi	sp, sp, 0x110    //给sp加上0x100,详情请见`__alltraps`的第二条指令。用于释放陷阱帧分配的内核空间
802000bc: 73 11 01 14  	csrrw	sp, sscratch, sp //互换sp和sscratch,这里的sp是内核栈指针,sscratch是用户栈指针,命令完成后将会是用户栈为主导
802000c0: 73 00 20 10  	sret                     //跳转到sepc指向的指令恢复执行,并根据sstatus恢复特权级和使能状态