vmx.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 2,245 行 · 第 1/5 页

C
2,245
字号
}static void __restore_debug_registers(struct vcpu *v){    if ( v->arch.hvm_vcpu.flag_dr_dirty )        return;    v->arch.hvm_vcpu.flag_dr_dirty = 1;    write_debugreg(0, v->arch.guest_context.debugreg[0]);    write_debugreg(1, v->arch.guest_context.debugreg[1]);    write_debugreg(2, v->arch.guest_context.debugreg[2]);    write_debugreg(3, v->arch.guest_context.debugreg[3]);    write_debugreg(6, v->arch.guest_context.debugreg[6]);    /* DR7 is loaded from the VMCS. */}/* * DR7 is saved and restored on every vmexit.  Other debug registers only * need to be restored if their value is going to affect execution -- i.e., * if one of the breakpoints is enabled.  So mask out all bits that don't * enable some breakpoint functionality. */static void vmx_restore_dr(struct vcpu *v){    /* NB. __vmread() is not usable here, so we cannot read from the VMCS. */    if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )        __restore_debug_registers(v);}static void vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c){    uint32_t ev;    vmx_vmcs_enter(v);    c->cr0 = v->arch.hvm_vcpu.guest_cr[0];    c->cr2 = v->arch.hvm_vcpu.guest_cr[2];    c->cr3 = v->arch.hvm_vcpu.guest_cr[3];    c->cr4 = v->arch.hvm_vcpu.guest_cr[4];    c->msr_efer = v->arch.hvm_vcpu.guest_efer;    c->sysenter_cs = __vmread(GUEST_SYSENTER_CS);    c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP);    c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP);    c->pending_event = 0;    c->error_code = 0;    if ( ((ev = __vmread(VM_ENTRY_INTR_INFO)) & INTR_INFO_VALID_MASK) &&         hvm_event_needs_reinjection((ev >> 8) & 7, ev & 0xff) )    {        c->pending_event = ev;        c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE);    }    vmx_vmcs_exit(v);}static int vmx_restore_cr0_cr3(    struct vcpu *v, unsigned long cr0, unsigned long cr3){    unsigned long mfn = 0;    p2m_type_t p2mt;    if ( paging_mode_shadow(v->domain) )    {        if ( cr0 & X86_CR0_PG )        {            mfn = mfn_x(gfn_to_mfn(v->domain, cr3 >> PAGE_SHIFT, &p2mt));            if ( !p2m_is_ram(p2mt) || !get_page(mfn_to_page(mfn), v->domain) )            {                gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%lx\n", cr3);                return -EINVAL;            }        }        if ( hvm_paging_enabled(v) )            put_page(pagetable_get_page(v->arch.guest_table));        v->arch.guest_table = pagetable_from_pfn(mfn);    }    v->arch.hvm_vcpu.guest_cr[0] = cr0 | X86_CR0_ET;    v->arch.hvm_vcpu.guest_cr[3] = cr3;    return 0;}static int vmx_vmcs_restore(struct vcpu *v, struct hvm_hw_cpu *c){    int rc;    if ( c->pending_valid &&         ((c->pending_type == 1) || (c->pending_type > 6) ||          (c->pending_reserved != 0)) )    {        gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32".\n",                 c->pending_event);        return -EINVAL;    }    rc = vmx_restore_cr0_cr3(v, c->cr0, c->cr3);    if ( rc )        return rc;    vmx_vmcs_enter(v);    v->arch.hvm_vcpu.guest_cr[2] = c->cr2;    v->arch.hvm_vcpu.guest_cr[4] = c->cr4;    vmx_update_guest_cr(v, 0);    vmx_update_guest_cr(v, 2);    vmx_update_guest_cr(v, 4);    v->arch.hvm_vcpu.guest_efer = c->msr_efer;    vmx_update_guest_efer(v);    __vmwrite(GUEST_SYSENTER_CS, c->sysenter_cs);    __vmwrite(GUEST_SYSENTER_ESP, c->sysenter_esp);    __vmwrite(GUEST_SYSENTER_EIP, c->sysenter_eip);    __vmwrite(GUEST_DR7, c->dr7);    vmx_vmcs_exit(v);    paging_update_paging_modes(v);    if ( c->pending_valid )    {        gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",                 c->pending_event, c->error_code);        if ( hvm_event_needs_reinjection(c->pending_type, c->pending_vector) )        {            vmx_vmcs_enter(v);            __vmwrite(VM_ENTRY_INTR_INFO, c->pending_event);            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code);            vmx_vmcs_exit(v);        }    }    return 0;}static void vmx_save_cpu_state(struct vcpu *v, struct hvm_hw_cpu *data){#ifdef __x86_64__    struct vmx_msr_state *guest_state = &v->arch.hvm_vmx.msr_state;    unsigned long guest_flags = guest_state->flags;    data->shadow_gs = v->arch.hvm_vmx.shadow_gs;    data->msr_cstar = v->arch.hvm_vmx.cstar;    /* save msrs */    data->msr_flags        = guest_flags;    data->msr_lstar        = guest_state->msrs[VMX_INDEX_MSR_LSTAR];    data->msr_star         = guest_state->msrs[VMX_INDEX_MSR_STAR];    data->msr_syscall_mask = guest_state->msrs[VMX_INDEX_MSR_SYSCALL_MASK];#endif    data->tsc = hvm_get_guest_tsc(v);}static void vmx_load_cpu_state(struct vcpu *v, struct hvm_hw_cpu *data){#ifdef __x86_64__    struct vmx_msr_state *guest_state = &v->arch.hvm_vmx.msr_state;    /* restore msrs */    guest_state->flags = data->msr_flags & 7;    guest_state->msrs[VMX_INDEX_MSR_LSTAR]        = data->msr_lstar;    guest_state->msrs[VMX_INDEX_MSR_STAR]         = data->msr_star;    guest_state->msrs[VMX_INDEX_MSR_SYSCALL_MASK] = data->msr_syscall_mask;    v->arch.hvm_vmx.cstar     = data->msr_cstar;    v->arch.hvm_vmx.shadow_gs = data->shadow_gs;#endif    hvm_set_guest_tsc(v, data->tsc);}static void vmx_save_vmcs_ctxt(struct vcpu *v, struct hvm_hw_cpu *ctxt){    vmx_save_cpu_state(v, ctxt);    vmx_vmcs_save(v, ctxt);}static int vmx_load_vmcs_ctxt(struct vcpu *v, struct hvm_hw_cpu *ctxt){    vmx_load_cpu_state(v, ctxt);    if ( vmx_vmcs_restore(v, ctxt) )    {        gdprintk(XENLOG_ERR, "vmx_vmcs restore failed!\n");        domain_crash(v->domain);        return -EINVAL;    }    return 0;}static void vmx_fpu_enter(struct vcpu *v){    setup_fpu(v);    __vm_clear_bit(EXCEPTION_BITMAP, TRAP_no_device);    v->arch.hvm_vmx.host_cr0 &= ~X86_CR0_TS;    __vmwrite(HOST_CR0, v->arch.hvm_vmx.host_cr0);}static void vmx_fpu_leave(struct vcpu *v){    ASSERT(!v->fpu_dirtied);    ASSERT(read_cr0() & X86_CR0_TS);    if ( !(v->arch.hvm_vmx.host_cr0 & X86_CR0_TS) )    {        v->arch.hvm_vmx.host_cr0 |= X86_CR0_TS;        __vmwrite(HOST_CR0, v->arch.hvm_vmx.host_cr0);    }    /*     * If the guest does not have TS enabled then we must cause and handle an     * exception on first use of the FPU. If the guest *does* have TS enabled     * then this is not necessary: no FPU activity can occur until the guest     * clears CR0.TS, and we will initialise the FPU when that happens.     */    if ( !(v->arch.hvm_vcpu.guest_cr[0] & X86_CR0_TS) )    {        v->arch.hvm_vcpu.hw_cr[0] |= X86_CR0_TS;        __vmwrite(GUEST_CR0, v->arch.hvm_vcpu.hw_cr[0]);        __vm_set_bit(EXCEPTION_BITMAP, TRAP_no_device);    }}static void vmx_ctxt_switch_from(struct vcpu *v){    vmx_fpu_leave(v);    vmx_save_guest_msrs(v);    vmx_restore_host_msrs();    vmx_save_dr(v);    vpmu_save(v);}static void vmx_ctxt_switch_to(struct vcpu *v){    /* HOST_CR4 in VMCS is always mmu_cr4_features. Sync CR4 now. */    if ( unlikely(read_cr4() != mmu_cr4_features) )        write_cr4(mmu_cr4_features);    vmx_restore_guest_msrs(v);    vmx_restore_dr(v);    vpmu_load(v);}static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg,                                     struct segment_register *reg){    uint32_t attr = 0;    vmx_vmcs_enter(v);    switch ( seg )    {    case x86_seg_cs:        reg->sel   = __vmread(GUEST_CS_SELECTOR);        reg->limit = __vmread(GUEST_CS_LIMIT);        reg->base  = __vmread(GUEST_CS_BASE);        attr       = __vmread(GUEST_CS_AR_BYTES);        break;    case x86_seg_ds:        reg->sel   = __vmread(GUEST_DS_SELECTOR);        reg->limit = __vmread(GUEST_DS_LIMIT);        reg->base  = __vmread(GUEST_DS_BASE);        attr       = __vmread(GUEST_DS_AR_BYTES);        break;    case x86_seg_es:        reg->sel   = __vmread(GUEST_ES_SELECTOR);        reg->limit = __vmread(GUEST_ES_LIMIT);        reg->base  = __vmread(GUEST_ES_BASE);        attr       = __vmread(GUEST_ES_AR_BYTES);        break;    case x86_seg_fs:        reg->sel   = __vmread(GUEST_FS_SELECTOR);        reg->limit = __vmread(GUEST_FS_LIMIT);        reg->base  = __vmread(GUEST_FS_BASE);        attr       = __vmread(GUEST_FS_AR_BYTES);        break;    case x86_seg_gs:        reg->sel   = __vmread(GUEST_GS_SELECTOR);        reg->limit = __vmread(GUEST_GS_LIMIT);        reg->base  = __vmread(GUEST_GS_BASE);        attr       = __vmread(GUEST_GS_AR_BYTES);        break;    case x86_seg_ss:        reg->sel   = __vmread(GUEST_SS_SELECTOR);        reg->limit = __vmread(GUEST_SS_LIMIT);        reg->base  = __vmread(GUEST_SS_BASE);        attr       = __vmread(GUEST_SS_AR_BYTES);        break;    case x86_seg_tr:        reg->sel   = __vmread(GUEST_TR_SELECTOR);        reg->limit = __vmread(GUEST_TR_LIMIT);        reg->base  = __vmread(GUEST_TR_BASE);        attr       = __vmread(GUEST_TR_AR_BYTES);        break;    case x86_seg_gdtr:        reg->limit = __vmread(GUEST_GDTR_LIMIT);        reg->base  = __vmread(GUEST_GDTR_BASE);        break;    case x86_seg_idtr:        reg->limit = __vmread(GUEST_IDTR_LIMIT);        reg->base  = __vmread(GUEST_IDTR_BASE);        break;    case x86_seg_ldtr:        reg->sel   = __vmread(GUEST_LDTR_SELECTOR);        reg->limit = __vmread(GUEST_LDTR_LIMIT);        reg->base  = __vmread(GUEST_LDTR_BASE);        attr       = __vmread(GUEST_LDTR_AR_BYTES);        break;    default:        BUG();    }    vmx_vmcs_exit(v);    reg->attr.bytes = (attr & 0xff) | ((attr >> 4) & 0xf00);    /* Unusable flag is folded into Present flag. */    if ( attr & (1u<<16) )        reg->attr.fields.p = 0;}static void vmx_set_segment_register(struct vcpu *v, enum x86_segment seg,                                     struct segment_register *reg){    uint32_t attr;    attr = reg->attr.bytes;    attr = ((attr & 0xf00) << 4) | (attr & 0xff);    /* Not-present must mean unusable. */    if ( !reg->attr.fields.p )        attr |= (1u << 16);    /* VMX has strict consistency requirement for flag G. */    attr |= !!(reg->limit >> 20) << 15;    vmx_vmcs_enter(v);    switch ( seg )    {    case x86_seg_cs:        __vmwrite(GUEST_CS_SELECTOR, reg->sel);        __vmwrite(GUEST_CS_LIMIT, reg->limit);        __vmwrite(GUEST_CS_BASE, reg->base);        __vmwrite(GUEST_CS_AR_BYTES, attr);        break;    case x86_seg_ds:        __vmwrite(GUEST_DS_SELECTOR, reg->sel);        __vmwrite(GUEST_DS_LIMIT, reg->limit);        __vmwrite(GUEST_DS_BASE, reg->base);        __vmwrite(GUEST_DS_AR_BYTES, attr);        break;    case x86_seg_es:        __vmwrite(GUEST_ES_SELECTOR, reg->sel);        __vmwrite(GUEST_ES_LIMIT, reg->limit);        __vmwrite(GUEST_ES_BASE, reg->base);        __vmwrite(GUEST_ES_AR_BYTES, attr);        break;    case x86_seg_fs:        __vmwrite(GUEST_FS_SELECTOR, reg->sel);        __vmwrite(GUEST_FS_LIMIT, reg->limit);        __vmwrite(GUEST_FS_BASE, reg->base);        __vmwrite(GUEST_FS_AR_BYTES, attr);        break;    case x86_seg_gs:        __vmwrite(GUEST_GS_SELECTOR, reg->sel);        __vmwrite(GUEST_GS_LIMIT, reg->limit);        __vmwrite(GUEST_GS_BASE, reg->base);        __vmwrite(GUEST_GS_AR_BYTES, attr);        break;    case x86_seg_ss:        __vmwrite(GUEST_SS_SELECTOR, reg->sel);        __vmwrite(GUEST_SS_LIMIT, reg->limit);        __vmwrite(GUEST_SS_BASE, reg->base);        __vmwrite(GUEST_SS_AR_BYTES, attr);        break;    case x86_seg_tr:        __vmwrite(GUEST_TR_SELECTOR, reg->sel);        __vmwrite(GUEST_TR_LIMIT, reg->limit);        __vmwrite(GUEST_TR_BASE, reg->base);        /* VMX checks that the the busy flag (bit 1) is set. */        __vmwrite(GUEST_TR_AR_BYTES, attr | 2);        break;    case x86_seg_gdtr:        __vmwrite(GUEST_GDTR_LIMIT, reg->limit);        __vmwrite(GUEST_GDTR_BASE, reg->base);        break;    case x86_seg_idtr:        __vmwrite(GUEST_IDTR_LIMIT, reg->limit);        __vmwrite(GUEST_IDTR_BASE, reg->base);        break;    case x86_seg_ldtr:        __vmwrite(GUEST_LDTR_SELECTOR, reg->sel);        __vmwrite(GUEST_LDTR_LIMIT, reg->limit);        __vmwrite(GUEST_LDTR_BASE, reg->base);        __vmwrite(GUEST_LDTR_AR_BYTES, attr);        break;    default:        BUG();    }    vmx_vmcs_exit(v);}static void vmx_set_tsc_offset(struct vcpu *v, u64 offset){    vmx_vmcs_enter(v);    __vmwrite(TSC_OFFSET, offset);#if defined (__i386__)    __vmwrite(TSC_OFFSET_HIGH, offset >> 32);#endif    vmx_vmcs_exit(v);}void do_nmi(struct cpu_user_regs *);static void vmx_init_hypercall_page(struct domain *d, void *hypercall_page){    char *p;    int i;    for ( i = 0; i < (PAGE_SIZE / 32); i++ )    {        p = (char *)(hypercall_page + (i * 32));        *(u8  *)(p + 0) = 0xb8; /* mov imm32, %eax */        *(u32 *)(p + 1) = i;        *(u8  *)(p + 5) = 0x0f; /* vmcall */        *(u8  *)(p + 6) = 0x01;        *(u8  *)(p + 7) = 0xc1;        *(u8  *)(p + 8) = 0xc3; /* ret */    }    /* Don't support HYPERVISOR_iret at the moment */    *(u16 *)(hypercall_page + (__HYPERVISOR_iret * 32)) = 0x0b0f; /* ud2 */}static unsigned int vmx_get_interrupt_shadow(struct vcpu *v){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?