assembly Boot 代码内的一般保护故障,KVM内有wrmsr指令

mutmk8jj  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(99)

我正在尝试使用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模式。

  1. IA-32 e模式已启用,并在启用物理地址扩展(PAE)之前尝试启用分页.
  2. IA-32 e模式处于活动状态,并尝试禁用物理地址扩展(PAE)。
    1.如果当前CS在尝试激活IA-32 e模式时设置了L位。
    1.尝试激活IA-32 e模式时,如果TR包含16位TSS。
    所有前4个条件都满足。我不理解5点,我不认为我有任何TSS段集。
    为什么此说明会引起一般保护故障?
falq053o

falq053o1#

回答我自己的问题:
因此,经过几天的尝试,我试图通过cpuid指令查看是否支持长模式,显然cpuid指令返回零。
因此,cpuid指令的输出必须通过KVM_SET_CPUID2 ioctl进行设置。
我遵循的程序是首先使用KVM_GET_SUPPORTED_CPUID从主机获取cpuid函数的当前列表,然后使用KVM_SET_CPUID2 ioctl设置返回结构。下面的代码片段完成了这个任务。看起来,设置在guest上应该返回什么cpuid指令也可以启用这些功能。

nent = 128;
cpuid2 =
(struct kvm_cpuid2 *)
    malloc(sizeof(struct kvm_cpuid2)
           + nent * sizeof(struct kvm_cpuid_entry2));
cpuid2->nent = nent;
if(ioctl(kvm, KVM_GET_SUPPORTED_CPUID, cpuid2) < 0)
    fatal("cant get cpuid");

print_cpuid_output(cpuid2);

vm->vcpufd = ioctl(vm->fd, KVM_CREATE_VCPU, (unsigned long)0);
if(vm->vcpufd == -1)
    fatal("Cannot create vcpu\n");

if(ioctl(vm->vcpufd, KVM_SET_CPUID2, cpuid2) < 0)
    fatal("cannot set cpuid things\n");

相关问题