核心是进行 gdt 的初始化。
void
pmm_init(void) {
gdt_init();
}
TSS (task segment selector):任务段选择子。
设置 1 个 TSS,这样从用户态切换到内核态时可以获取正确的堆栈。
ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0);
这里的 stack0
是 1 个临时的内核堆栈。
uint8_t stack0[1024];
查看一下其具体的数值如下。
(gdb) p &stack0
$2 = (uint8_t (*)[1024]) 0x10f980 <stack0>
即,将 ts_esp0
的值设为 stack0
数组的尾部。
ts.ts_ss0 = KERNEL_DS;
ts_ss0
设为内核的数据段。
设置好之后的数据如下。
ts_esp0 = 1113472,
ts_ss0 = 16,
- ts_esp0:栈指针和段选择子;
- ts_ss0:在特权级中会有 1 个增量。
gdt[SEG_TSS] = SEG16(STS_T32A, (uint32_t)&ts, sizeof(ts), DPL_KERNEL);
gdt[SEG_TSS].sd_s = 0;
运行结果如下。
(gdb) p gdt[5]
$5 = {
sd_lim_15_0 = 104,
sd_base_15_0 = 63648,
sd_base_23_16 = 16,
sd_type = 9,
sd_s = 0,
sd_dpl = 0,
sd_p = 1,
sd_lim_19_16 = 0,
sd_avl = 0,
sd_rsv1 = 0,
sd_db = 1,
sd_g = 0,
sd_base_31_24 = 0
}
// reload all segment registers
lgdt(&gdt_pd);
即使用内联汇编来加载全局描述符表,并复位内核的数据、代码段寄存器的数值。
/* *
* lgdt - load the global descriptor table register and reset the
* data/code segement registers for kernel.
* */
static inline void
lgdt(struct pseudodesc *pd) {
asm volatile ("lgdt (%0)" :: "r" (pd));
asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
// reload cs
asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
}
反汇编的代码如下。
(gdb) b kern/mm/pmm.c:63
Breakpoint 3 at 0x10290a: file kern/mm/pmm.c, line 63.
(gdb) c
Continuing.
Breakpoint 3, lgdt (pd=0x10ea10 <gdt_pd>) at kern/mm/pmm.c:63
63 asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
(gdb) disassemble lgdt
Dump of assembler code for function lgdt:
0x001028fa <+ 0>: push %bp
0x001028fb <+ 1>: mov %sp,%bp
0x001028fd <+ 3>: mov 0x8(%di),%ax
0x00102900 <+ 6>: lgdtw (%bx,%si)
0x00102903 <+ 9>: mov $0x23,%ax
0x00102906 <+12>: add %al,(%bx,%si)
0x00102908 <+14>: mov %ax,%gs
=> 0x0010290a <+16>: mov $0x23,%ax
0x0010290d <+19>: add %al,(%bx,%si)
0x0010290f <+21>: mov %ax,%fs
0x00102911 <+23>: mov $0x10,%ax
0x00102914 <+26>: add %al,(%bx,%si)
0x00102916 <+28>: mov %ax,%es
0x00102918 <+30>: mov $0x10,%ax
0x0010291b <+33>: add %al,(%bx,%si)
0x0010291d <+35>: mov %ax,%ds
0x0010291f <+37>: mov $0x10,%ax
0x00102922 <+40>: add %al,(%bx,%si)
0x00102924 <+42>: mov %ax,%ss
0x00102926 <+44>: ljmp $0x10,$0x292d
0x0010292b <+49>: or %al,(%bx,%si)
0x0010292d <+51>: pop %bp
0x0010292e <+52>: ret
End of assembler dump.
// load the TSS
ltr(GD_TSS);
ltr 的实现如下。
static inline void
ltr(uint16_t sel) {
asm volatile ("ltr %0" :: "r" (sel));
}