我正在尝试使用lret指令从ring0切换到ring3。因此,我推送ss,esp,cs,ip的正确值,并使用lret。
我使用qemu和gdb来调试这段代码,并发现一件奇怪的事情:
在lret instrunction之后,处理器成功切换到ring3(我在qemu中使用“info registers”来检查),ss,cs,esp的值都是正确的,并且eip指向ring3代码的第一条指令。
但是,无论ring3代码的第一条指令是什么(甚至nop指令),qemu都会重启(可能是一般保护错误?)
以下是代码,头文件来自this repo。引导加载程序将CPU切换到保护模式并将内核加载到0x100000,内核的虚拟地址从0x80100000开始。
#include "memlayout.h"
#include "mmu.h"
#include "asm.h"
.text
.globl _start
_start = V2P_WO(entry)
.globl entry
entry:
mov %cr4, %eax
or $(CR4_PSE), %eax
mov %eax, %cr4
mov $(V2P_WO(pgdir)), %eax
mov %eax, %cr3
mov %cr0, %eax
or $(CR0_PG|CR0_WP), %eax
mov %eax, %cr0
lgdt gdtdesc
mov $0x28, %ax
ltr %ax
mov $kernel_stack, %esp
push $0x23
push $user_stack
push $0x1b
push $user_task
lret
user_task:
nop
die:
jmp die
.data
.align 4096
tss:
.long 0
.long kernel_stack
.word 0x10, 0
.long 0, 0, 0, 0
.long pgdir
.long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.long 0, 0, 0, 0, 0, 0, 0, 0
gdt:
SEG_NULLASM
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
SEG_ASM_USER(STA_X|STA_R, 0x0, 0xffffffff)
SEG_ASM_USER(STA_W, 0x0, 0xffffffff)
.word 0, 0x1000
.byte 0x10, 0x89, 0x80, 0x80
gdtdesc:
.word (gdtdesc - gdt - 1)
.long gdt
.align 4096
pgdir: .long PTE_P|PTE_W|PTE_PS
.fill 511, 4
.long PTE_P|PTE_W|PTE_PS
.fill 511, 4
.fill 4096
kernel_stack:
.fill 4096
user_stack:
答案 0 :(得分:0)
哦,我发现了问题所在。访问冲突发生在分页上。