不能在多个处理器上调用VMXON

时间:2019-11-13 19:45:30

标签: c linux linux-kernel virtualization hypervisor

我正在尝试在Intel芯片上构建自己的Linux虚拟机管理程序(5.0.x内核),但遇到了一个奇怪的问题。 每当我尝试在多个处理器上执行VMXON时,它都会失败。 我确保启用了VMX,我分配了一个对齐的页面,并将VMCS REV ID写入了VMXON区域,但是我不确定问题出在哪里。

这是我的代码:

vmx.c:

#include "vmx.h"

typedef struct vmstate {
    bool vmx_enabled;
    void *vmxon_region;
    phys_addr_t vmxon_physical;
    void *vmcs_region;
    phys_addr_t vmcs_physical;
} vmstate;

static DEFINE_PER_CPU(vmstate*, cpu_vms);

static inline int VMXON(phys_addr_t phys){
    uint8_t ret;
    // TODO: Signal VMX to PT, to avoid PT crashes (Processor Trace)
    __asm__ __volatile__ (
        "vmxon %[pa]; setna %[ret]"
        : [ret]"=rm"(ret)
        : [pa]"m"(phys)
        : "cc", "memory"
    );
    return ret;
}

static inline void VMXOFF(void){
    __asm__ __volatile__("vmxoff" : : : "cc");
}

static void enable_vmx_operation_cr(void){
    // Enable 14th bit in CR4
    __write_cr4(__read_cr4() | 0x2000);
}

static void disable_vmx_operation_cr(void){
    __write_cr4(__read_cr4() & ~(0x2000));
}

static vmstate *create_vmstate(void){
    vmstate *vms = kzalloc(sizeof(vmstate), GFP_KERNEL);
    vms->vmx_enabled = false;
    return vms;
}

static void alloc_vmxon_region(vmstate *vms){
    // TODO: respect physical width as set by the IA32_VMX_BASIC[48] bit for 32bit support
    uint32_t vmcs_revid = 0;
    uint32_t hi = 0;

    void *vmxon_region = kmalloc(4096, GFP_KERNEL);

    rdmsr_safe(MSR_IA32_VMX_BASIC, &vmcs_revid, &hi);
    memcpy(vmxon_region, &vmcs_revid, 4);

    vms->vmxon_region = vmxon_region;
    vms->vmxon_physical = virt_to_phys(vmxon_region);
}

static void teardown_vmstate(vmstate *vms){
    if(vms->vmxon_region)
        kfree(vms->vmxon_region);

}

void vmx_teardown(void){
    int i;
    vmstate* vms;
    for_each_possible_cpu(i){
        vms = per_cpu(cpu_vms, i);

        if(vms->vmx_enabled == true) {
            VMXOFF();
            vms->vmx_enabled = false;
        }
        disable_vmx_operation_cr();
        teardown_vmstate(vms);
        kfree(vms);
    }
}

int vmx_setup(void){
    int i;
    vmstate* vms;
    printk(KERN_INFO "NUM CPUS: %d", num_possible_cpus());

    for_each_possible_cpu(i) {
        // Allocate vmstate for every processor
        per_cpu(cpu_vms, i) = create_vmstate();
        vms = per_cpu(cpu_vms, i);

        alloc_vmxon_region(vms);

        enable_vmx_operation_cr();
        if(VMXON(vms->vmxon_physical)){
            printk(KERN_ALERT "VMXON operation failed!");
            vms->vmx_enabled = false;   
        }
        else
            vms->vmx_enabled = true;
    }
    for_each_possible_cpu(i){
        vms = per_cpu(cpu_vms, i);
        if(vms->vmx_enabled == false) {
            printk(KERN_ALERT "Tearing down after VMXON fail!");
            vmx_teardown();
            return -1;
        }
    }
    return 0;
}

vmx_setup由设备打开文件操作调用:

static int hyper_dev_open(struct inode* inode, struct file *filep){
        int err;
        printk(KERN_INFO "Enabling VMX operation!\n");
        if((err = vmx_setup()))
                return err;
        return 0;
}

当我在另一个处理器上执行VMXON时,进位标志设置为1,零标志为0。 但是,如果我在VMXON()之后添加VMXOFF(),则驱动程序可以工作,因此不会并行启用两个VMX操作。

任何建议都会有所帮助:)

1 个答案:

答案 0 :(得分:1)

for_each_possible_cpu仅在可用的CPU上进行迭代;它不会改变执行顺序并依次在每个CPU上运行。整个循环在单个CPU上运行。

因此,您试图在同一CPU上重复执行vmxon,这就是失败的原因。