【问题标题】:can't call VMXON on more than 1 processor不能在超过 1 个处理器上调用 VMXON
【发布时间】:2019-11-13 19:45:30
【问题描述】:

我正在尝试在英特尔芯片上为 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 操作。

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

【问题讨论】:

    标签: c linux linux-kernel virtualization hypervisor


    【解决方案1】:

    for_each_possible_cpu 简单地迭代可用的 CPU;它不会将执行更改为依次在每个 CPU 上运行。整个循环在单个 CPU 上运行。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多