gdb调试xv6系统调用

22 阅读1分钟
b *0xe06              //打断点
hbreak *0x3ffffff000  //打硬件断点

image.png

(gdb) b *0xe06
Breakpoint 1 at 0xe06
(gdb) c
Continuing.

此时停在0xe06,将要执行ecall,可以看到此时pc正是0xe06,且stvec0x3ffffff000,这是trampoline.S的入口,uservec函数

stvec寄存器:trap的入口,xv6是Direct模式(另一种是Vectored),所有trap同一入口

(gdb) hbreak *0x3ffffff000
Hardware assisted breakpoint 2 at 0x3ffffff000
(gdb) c
Continuing.

此时停在csrw sscratch,a0,uservec的第一个指令。

sepc为ecall指令的地址,这是因为trap会保存用户程序的pc,以便系统调用结束正常执行用户程序

(gdb) print/x $sepc
$4 = 0xe06
(gdb) print/x $pc
$5 = 0x3ffffff000

到目前为止,都没有保存寄存器。采用汇编语言来进行保存正是为了防止编译器使用寄存器,从而覆盖未保存的寄存器

trapframe

前五个是内核事先存放在trapframe中的数据。比如第一个数据保存了kernel page table地址,这将会是trap处理代码将要加载到SATP寄存器的数值。

struct trapframe {
  /*   0 */ uint64 kernel_satp;   // kernel page table
  /*   8 */ uint64 kernel_sp;     // top of process's kernel stack
  /*  16 */ uint64 kernel_trap;   // usertrap()
  /*  24 */ uint64 epc;           // saved user program counter
  /*  32 */ uint64 kernel_hartid; // saved kernel tp
  /*  40 */ uint64 ra;
  // ...
  // ...
  // ...
  /* 280 */ uint64 t6;
};

uservec

在“用户页表 + S 模式 + 无栈可用的极限环境下,完成:
① 保存所有用户寄存器到 trapframe
② 从 trapframe 中取出未来要用的 kernel 信息(stack / satp / trap 函数 / hartid)
③ 切换到内核栈 + 内核页表,然后跳进 usertrap()