修改xv6为多线程系统,trampoline写入栈触发Store/AMO页错误
问题
我正在将xv6修改为多线程系统,为了让每个线程找到唯一的TRAMPFRAME虚拟地址,需要在trampoline的uservec段中执行计算操作,具体是用栈临时存储寄存器,修改后的汇编代码如下:
uservec: # # trap.c sets stvec to point here, so # traps from user space start here, # in supervisor mode, but with a # user page table. # # store intermediate registers on the stack addi sp, sp, -32 sd a0, 0(sp) sd a1, 8(sp) sd a2, 16(sp)
现在遇到的问题是:每次从用户空间返回时(仍使用用户页表),执行这段代码写入栈都会触发错误,陷入无限循环。我检查了sp指向地址的权限,该地址已在用户页表中映射,相关代码如下:
首个进程初始化代码:
uvmfirst(p->mm.pagetable, initcode, sizeof(initcode)); p->sz = PGSIZE; // prepare for the very first "return" from kernel to user. t->trapframe->epc = 0; // user program counter t->trapframe->sp = PGSIZE; // user stack pointer
uvmfirst函数实现:
void uvmfirst(pagetable_t pagetable, uchar *src, uint sz) { char *mem; if(sz >= PGSIZE) panic("uvmfirst: more than a page"); mem = kalloc(); memset(mem, 0, PGSIZE); mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); memmove(mem, src, sz); }
确认权限设置正确,但写入操作仍然失败,对原版xv6做相同修改也出现该问题,请问原因是什么?
原因分析
问题核心在于**uservec执行时CPU处于supervisor模式,但栈指针sp指向的是用户栈地址**。
根据RISC-V架构的权限规则:用户页表项的PTE_U标志仅允许CPU在用户模式(U-mode)下访问对应页面;当CPU处于supervisor模式(S-mode)时,会直接忽略PTE_U位,此时即使页表项包含PTE_W权限,也无法对用户页表中的页面执行写入操作——这会直接触发页错误,而页错误又会再次进入trap流程,最终导致无限循环。
你在uservec中尝试写入用户栈,但uservec本身是在S-mode下执行的,自然会触发权限错误。
解决办法
不能在uservec中直接使用用户栈存储寄存器,应该改用trampoline页自身的栈空间。trampoline页被映射到所有进程的同一个虚拟地址(通常为TRAMPOLINE),其页表项具备S-mode可读写权限,且没有PTE_U限制,适合在S-mode下临时存储数据。
具体实现时,可以在trampoline的汇编代码中预留一段固定的栈空间,或者利用trampoline页末尾的空闲区域作为临时栈,这样就能避开用户页的权限限制。
内容的提问来源于stack exchange,提问作者Pico marando

