我正在尝试使用KVM将处理器引导到长模式。我已经能够设置衍生并构建在this lwn example上的小型KVM管理程序。
现在,我正在尝试将处理器引导到长模式。到目前为止,我遵循的序列是。
1.使用KVM,将第一个eip初始化为0x 1000,我的代码从这里开始,而不是重置状态。
1.加载GDT,做一个长跳到32位蹦床代码。
1.在Trampoline中,在cr 4中启用PAE和PSE。在cr 3中加载页定向器地址,然后在cr 4中设置LM位。设置LM位生成一般保护故障(通过设置UP IDT然后查看调用的陷阱处理程序进行验证)
我的代码是:
__reset: // 0x1000
cli
mov $gdtr, %eax
lgdt (%eax)
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
jmp $0x08, $trampoline
trampoline:
movl $STACK_START, %esp
movl %esp, %ebp
// Set up the protected-mode data segment registers
movl $0x10, %eax // Our data segment selector
movl %eax, %ds // -> DS: Data Segment
movl %eax, %es // -> ES: Extra Segment
movl %eax, %ss // -> SS: Stack Segment
movl $0, %eax // Zero segments not ready for use
movl %eax, %fs // -> FS
movl %eax, %gs // -> GS
call fill_idt
movl $idt_descriptor, %eax
lidtl (%eax)
// 9.8.5 Initializing IA-32e Mode, pg 3153 intel-manual
// step-1 disable cr0.paging
// it is disabled already right now
// step-2 enable PAE.
movl %cr4, %eax
orl $(CR4_PAE | CR4_PSE), %eax
movl %eax, %cr4
// step-3 set cr2 to level-4 or level-5
// set cr3 to a pointer to pml4
movl $boot_p4, %eax
movl %eax, %cr3
// step-4 efer.lme = 1
// enable long mode and the NX bit
movl $MSR_EFER, %ecx
rdmsr
orl $(EFER_LM | EFER_NX | EFER_SCE), %eax
wrmsr // <------ IT FAULTS HERE. GENERAL PROTECTION FAULT
.p2align 4
NULL_DESC:
.quad 0x0000000000000000
CODE_DESC:
.word 0xFFFF // limit low
.word 0 // base low
.byte 0 // base middle
.byte 0b10011010 // access
.byte 0b11001111 // granularity
.byte 0 // base high
DATA_DESC:
.word 0xFFFF // data descriptor
.word 0 // limit low
.byte 0 // base low
.byte 0b10010010 // access
.byte 0b11001111 // granularity
.byte 0 // base high
gdtr:
.word gdtr - NULL_DESC - 1
.long NULL_DESC
我的寄存器在wrmsr
指令之前的状态是
rip = 0x000000000000189e // hlt instruction added just before wrmsr
rax = 0x0000000000000901
rbx = 0x00000000000020e0
rcx = 0x00000000c0000080
rdx = 0x0000000000000000
rsp = 0x000000000000b000
rbp = 0x000000000000b000
rdi = 0x000000000000a000
rsi = 0x0000000000000000
rflags = 0x0000000000000002
efer = 0x0000000000000000
cr0 = 0x0000000060000011
cr2 = 0x0000000000000000
cr3 = 0x0000000000003000
cr4 = 0x0000000000000030
根据英特尔手册“9.8.5初始化IA-32 e模式”一节,64位模式一致性检查在以下情况下会失败:
1.在启用分页时尝试启用或禁用IA-32 e模式。
- IA-32 e模式已启用,并在启用物理地址扩展(PAE)之前尝试启用分页.
- IA-32 e模式处于活动状态,并尝试禁用物理地址扩展(PAE)。
1.如果当前CS在尝试激活IA-32 e模式时设置了L位。
1.尝试激活IA-32 e模式时,如果TR包含16位TSS。
所有前4个条件都满足。我不理解5点,我不认为我有任何TSS段集。
为什么此说明会引起一般保护故障?
1条答案
按热度按时间falq053o1#
回答我自己的问题:
因此,经过几天的尝试,我试图通过cpuid指令查看是否支持长模式,显然cpuid指令返回零。
因此,cpuid指令的输出必须通过KVM_SET_CPUID2 ioctl进行设置。
我遵循的程序是首先使用KVM_GET_SUPPORTED_CPUID从主机获取cpuid函数的当前列表,然后使用KVM_SET_CPUID2 ioctl设置返回结构。下面的代码片段完成了这个任务。看起来,设置在guest上应该返回什么cpuid指令也可以启用这些功能。