diff --git a/tools/libxc/xc_cpufeature.h b/tools/libxc/xc_cpufeature.h --- a/tools/libxc/xc_cpufeature.h +++ b/tools/libxc/xc_cpufeature.h @@ -98,6 +98,7 @@ #define X86_FEATURE_DCA (4*32+18) /* Direct Cache Access */ #define X86_FEATURE_SSE4_1 (4*32+19) /* Streaming SIMD Extensions 4.1 */ #define X86_FEATURE_SSE4_2 (4*32+20) /* Streaming SIMD Extensions 4.2 */ +#define X86_FEATURE_X2APIC (4*32+21) /* x2APIC */ #define X86_FEATURE_POPCNT (4*32+23) /* POPCNT instruction */ #define X86_FEATURE_AES (4*32+25) /* AES acceleration instructions */ #define X86_FEATURE_XSAVE (4*32+26) /* XSAVE/XRSTOR/XSETBV/XGETBV */ diff --git a/tools/libxc/xc_cpuid_x86.c b/tools/libxc/xc_cpuid_x86.c --- a/tools/libxc/xc_cpuid_x86.c +++ b/tools/libxc/xc_cpuid_x86.c @@ -169,12 +169,15 @@ const unsigned int *input, unsigned int *regs) { char brand[13]; - unsigned long pae; - int is_pae; + unsigned long pae, x2apic; + int is_pae, emul_x2apic; xc_get_hvm_param(xch, domid, HVM_PARAM_PAE_ENABLED, &pae); is_pae = !!pae; + xc_get_hvm_param(xch, domid, HVM_PARAM_EMUL_X2APIC, &x2apic); + emul_x2apic = !!x2apic; + switch ( input[0] ) { case 0x00000000: @@ -199,6 +202,9 @@ regs[2] |= bitmaskof(X86_FEATURE_HYPERVISOR); + if (emul_x2apic) + regs[2] |= bitmaskof(X86_FEATURE_X2APIC); + regs[3] &= (bitmaskof(X86_FEATURE_FPU) | bitmaskof(X86_FEATURE_VME) | bitmaskof(X86_FEATURE_DE) | diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -432,6 +432,7 @@ hvm_init_guest_time(d); d->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] = 1; + d->arch.hvm_domain.params[HVM_PARAM_EMUL_X2APIC] = 1; hvm_init_cacheattr_region_list(d); @@ -2189,6 +2190,11 @@ *msr_content = vcpu_vlapic(v)->hw.apic_base_msr; break; + case MSR_IA32_APICBASE_MSR ... MSR_IA32_APICBASE_MSR + 0x3ff: + if ( hvm_x2apic_msr_read(v, msr, msr_content) ) + goto gp_fault; + break; + case MSR_IA32_CR_PAT: *msr_content = v->arch.hvm_vcpu.pat_cr; break; @@ -2296,6 +2302,11 @@ vlapic_msr_set(vcpu_vlapic(v), msr_content); break; + case MSR_IA32_APICBASE_MSR ... MSR_IA32_APICBASE_MSR + 0x3ff: + if ( hvm_x2apic_msr_write(v, msr, msr_content) ) + goto gp_fault; + break; + case MSR_IA32_CR_PAT: if ( !pat_msr_set(&v->arch.hvm_vcpu.pat_cr, msr_content) ) goto gp_fault; diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -172,7 +172,12 @@ static int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda) { int result = 0; - uint8_t logical_id; + uint32_t logical_id; + + if ( vlapic_x2apic_mode(vlapic) ) { + logical_id = vlapic_get_reg(vlapic, APIC_LDR); + return logical_id & mda; + } logical_id = GET_xAPIC_LOGICAL_ID(vlapic_get_reg(vlapic, APIC_LDR)); @@ -392,7 +397,7 @@ int vlapic_ipi( struct vlapic *vlapic, uint32_t icr_low, uint32_t icr_high) { - unsigned int dest = GET_xAPIC_DEST_FIELD(icr_high); + unsigned int dest; unsigned int short_hand = icr_low & APIC_SHORT_MASK; unsigned int dest_mode = !!(icr_low & APIC_DEST_MASK); struct vlapic *target; @@ -401,6 +406,11 @@ HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "icr = 0x%08x:%08x", icr_high, icr_low); + if ( vlapic_x2apic_mode(vlapic) ) + dest = icr_high; + else + dest = GET_xAPIC_DEST_FIELD(icr_high); + if ( (icr_low & APIC_MODE_MASK) == APIC_DM_LOWEST ) { target = vlapic_lowest_prio(vlapic_domain(vlapic), vlapic, @@ -528,65 +538,42 @@ return X86EMUL_OKAY; } +int hvm_x2apic_msr_read(struct vcpu *v, unsigned int msr, uint64_t *msr_content) +{ + struct vlapic *vlapic = vcpu_vlapic(v); + uint32_t low, high = 0, offset = (msr - MSR_IA32_APICBASE_MSR) << 4; + + if ( !vlapic_x2apic_mode(vlapic) ) + return 1; + + vlapic_read_aligned(vlapic, offset, &low); + if ( offset == APIC_ICR ) + vlapic_read_aligned(vlapic, APIC_ICR2, &high); + + *msr_content = (((uint64_t)high) << 32) | low; + return 0; +} + static void vlapic_pt_cb(struct vcpu *v, void *data) { *(s_time_t *)data = hvm_get_guest_time(v); } -static int vlapic_write(struct vcpu *v, unsigned long address, - unsigned long len, unsigned long val) +static int vlapic_reg_write(struct vcpu *v, + unsigned int offset, unsigned long val) { struct vlapic *vlapic = vcpu_vlapic(v); - unsigned int offset = address - vlapic_base_address(vlapic); int rc = X86EMUL_OKAY; - if ( offset != 0xb0 ) - HVM_DBG_LOG(DBG_LEVEL_VLAPIC, - "offset 0x%x with length 0x%lx, and value is 0x%lx", - offset, len, val); - - /* - * According to the IA32 Manual, all accesses should be 32 bits. - * Some OSes do 8- or 16-byte accesses, however. - */ - val = (uint32_t)val; - if ( len != 4 ) - { - unsigned int tmp; - unsigned char alignment; - - gdprintk(XENLOG_INFO, "Notice: Local APIC write with len = %lx\n",len); - - alignment = offset & 0x3; - (void)vlapic_read_aligned(vlapic, offset & ~0x3, &tmp); - - switch ( len ) - { - case 1: - val = ((tmp & ~(0xff << (8*alignment))) | - ((val & 0xff) << (8*alignment))); - break; - - case 2: - if ( alignment & 1 ) - goto unaligned_exit_and_crash; - val = ((tmp & ~(0xffff << (8*alignment))) | - ((val & 0xffff) << (8*alignment))); - break; - - default: - gdprintk(XENLOG_ERR, "Local APIC write with len = %lx, " - "should be 4 instead\n", len); - goto exit_and_crash; - } - } - else if ( (offset & 0x3) != 0 ) - goto unaligned_exit_and_crash; - - offset &= ~0x3; - switch ( offset ) { + case APIC_ID: + if ( !vlapic_x2apic_mode(vlapic) ) + vlapic_set_reg(vlapic, APIC_ID, val); + else + rc = X86EMUL_UNHANDLEABLE; + break; + case APIC_TASKPRI: vlapic_set_reg(vlapic, APIC_TASKPRI, val & 0xff); break; @@ -596,11 +583,17 @@ break; case APIC_LDR: - vlapic_set_reg(vlapic, APIC_LDR, val & APIC_LDR_MASK); + if ( !vlapic_x2apic_mode(vlapic) ) + vlapic_set_reg(vlapic, APIC_LDR, val & APIC_LDR_MASK); + else + rc = X86EMUL_UNHANDLEABLE; break; case APIC_DFR: - vlapic_set_reg(vlapic, APIC_DFR, val | 0x0FFFFFFF); + if ( !vlapic_x2apic_mode(vlapic) ) + vlapic_set_reg(vlapic, APIC_DFR, val | 0x0FFFFFFF); + else + rc = X86EMUL_UNHANDLEABLE; break; case APIC_SPIV: @@ -628,7 +621,18 @@ break; case APIC_ESR: - /* Nothing to do. */ + if ( vlapic_x2apic_mode(vlapic) && val != 0 ) { + gdprintk(XENLOG_ERR, "Local APIC write ESR with non-zero %lx\n", + val); + rc = X86EMUL_UNHANDLEABLE; + } + break; + + case APIC_SELF_IPI: + if ( vlapic_x2apic_mode(vlapic) ) + vlapic_reg_write(v, APIC_ICR, 0x40000 | (val & 0xff)); + else + rc = X86EMUL_UNHANDLEABLE; break; case APIC_ICR: @@ -639,7 +643,9 @@ break; case APIC_ICR2: - vlapic_set_reg(vlapic, APIC_ICR2, val & 0xff000000); + if ( !vlapic_x2apic_mode(vlapic) ) + val &= 0xff000000; + vlapic_set_reg(vlapic, APIC_ICR2, val); break; case APIC_LVTT: /* LVT Timer Reg */ @@ -695,12 +701,67 @@ break; default: - gdprintk(XENLOG_DEBUG, - "Local APIC Write to read-only register 0x%x\n", offset); break; } + if (rc == X86EMUL_UNHANDLEABLE) + gdprintk(XENLOG_DEBUG, + "Local APIC Write wrong to register 0x%x\n", offset); + return rc; +} - return rc; +static int vlapic_write(struct vcpu *v, unsigned long address, + unsigned long len, unsigned long val) +{ + struct vlapic *vlapic = vcpu_vlapic(v); + unsigned int offset = address - vlapic_base_address(vlapic); + int rc = X86EMUL_OKAY; + + if ( offset != 0xb0 ) + HVM_DBG_LOG(DBG_LEVEL_VLAPIC, + "offset 0x%x with length 0x%lx, and value is 0x%lx", + offset, len, val); + + /* + * According to the IA32 Manual, all accesses should be 32 bits. + * Some OSes do 8- or 16-byte accesses, however. + */ + val = (uint32_t)val; + if ( len != 4 ) + { + unsigned int tmp; + unsigned char alignment; + + gdprintk(XENLOG_INFO, "Notice: Local APIC write with len = %lx\n",len); + + alignment = offset & 0x3; + (void)vlapic_read_aligned(vlapic, offset & ~0x3, &tmp); + + switch ( len ) + { + case 1: + val = ((tmp & ~(0xff << (8*alignment))) | + ((val & 0xff) << (8*alignment))); + break; + + case 2: + if ( alignment & 1 ) + goto unaligned_exit_and_crash; + val = ((tmp & ~(0xffff << (8*alignment))) | + ((val & 0xffff) << (8*alignment))); + break; + + default: + gdprintk(XENLOG_ERR, "Local APIC write with len = %lx, " + "should be 4 instead\n", len); + goto exit_and_crash; + } + } + else if ( (offset & 0x3) != 0 ) + goto unaligned_exit_and_crash; + + offset &= ~0x3; + + return vlapic_reg_write(v, offset, val); unaligned_exit_and_crash: gdprintk(XENLOG_ERR, "Unaligned LAPIC write len=0x%lx at offset=0x%x.\n", @@ -710,6 +771,27 @@ return rc; } +int hvm_x2apic_msr_write(struct vcpu *v, unsigned int msr, uint64_t msr_content) +{ + struct vlapic *vlapic = vcpu_vlapic(v); + uint32_t offset = (msr - MSR_IA32_APICBASE_MSR) << 4; + int rc; + + if ( !vlapic_x2apic_mode(vlapic) ) + return 1; + + if ( offset == APIC_ICR ) + if ( vlapic_reg_write(v, APIC_ICR2 , (uint32_t)(msr_content >> 32)) ) + return 1; + + rc = vlapic_reg_write(v, offset, (uint32_t)msr_content); + /* X86EMUL_RETRY for SIPI */ + if ( rc != X86EMUL_OKAY && rc != X86EMUL_RETRY ) + return 1; + + return 0; +} + static int vlapic_range(struct vcpu *v, unsigned long addr) { struct vlapic *vlapic = vcpu_vlapic(v); @@ -742,6 +824,12 @@ vlapic->hw.apic_base_msr = value; + if ( vlapic_x2apic_mode(vlapic) ) { + u32 id = vlapic_get_reg(vlapic, APIC_ID); + u32 ldr = ((id & ~0xf) << 16) | (1 << (id & 0xf)); + vlapic_set_reg(vlapic, APIC_LDR, ldr); + } + vmx_vlapic_msr_changed(vlapic_vcpu(vlapic)); HVM_DBG_LOG(DBG_LEVEL_VLAPIC, diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -364,4 +364,7 @@ ? (u32)__d->arch.incarnation : (u32)(v)->arch.hvm_vcpu.msr_tsc_aux; \ }) +int hvm_x2apic_msr_read(struct vcpu *v, unsigned int msr, uint64_t *msr_content); +int hvm_x2apic_msr_write(struct vcpu *v, unsigned int msr, uint64_t msr_content); + #endif /* __ASM_X86_HVM_HVM_H__ */ diff --git a/xen/include/asm-x86/hvm/vlapic.h b/xen/include/asm-x86/hvm/vlapic.h --- a/xen/include/asm-x86/hvm/vlapic.h +++ b/xen/include/asm-x86/hvm/vlapic.h @@ -51,6 +51,8 @@ #define vlapic_base_address(vlapic) \ ((vlapic)->hw.apic_base_msr & MSR_IA32_APICBASE_BASE) +#define vlapic_x2apic_mode(vlapic) \ + ((vlapic)->hw.apic_base_msr & MSR_IA32_APICBASE_EXTD) struct vlapic { struct hvm_hw_lapic hw; diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h --- a/xen/include/asm-x86/msr-index.h +++ b/xen/include/asm-x86/msr-index.h @@ -307,6 +307,7 @@ #define MSR_IA32_APICBASE_EXTD (1<<10) #define MSR_IA32_APICBASE_ENABLE (1<<11) #define MSR_IA32_APICBASE_BASE (0xfffff<<12) +#define MSR_IA32_APICBASE_MSR 0x800 #define MSR_IA32_UCODE_WRITE 0x00000079 #define MSR_IA32_UCODE_REV 0x0000008b diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h --- a/xen/include/public/hvm/params.h +++ b/xen/include/public/hvm/params.h @@ -124,6 +124,8 @@ */ #define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 -#define HVM_NR_PARAMS 20 +#define HVM_PARAM_EMUL_X2APIC 20 + +#define HVM_NR_PARAMS 21 #endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */