diff --git a/core/include/compiler.h b/core/include/compiler.h index 14428de0..1711d5d2 100644 --- a/core/include/compiler.h +++ b/core/include/compiler.h @@ -43,7 +43,7 @@ // a non-POD structure. // We have to use 1 instead of 0 so the compiler doesn't generate an error. #define offsetof(type, mem) \ - ((uint32)((char *)&((const type *)1)->mem - (char *)((const type *)1))) + ((uint32_t)((char *)&((const type *)1)->mem - (char *)((const type *)1))) #define ALWAYS_INLINE __attribute__ ((always_inline)) #define NOINLINE __attribute__ ((noinline)) diff --git a/core/include/cpu.h b/core/include/cpu.h index 1db58557..bc91cd58 100644 --- a/core/include/cpu.h +++ b/core/include/cpu.h @@ -84,6 +84,7 @@ struct hstate { uint64_t dr7; // CR0 bool cr0_ts; + uint64_t _pat; }; struct hstate_compare { diff --git a/core/include/hax_core_interface.h b/core/include/hax_core_interface.h index d974ce2f..3f1fd83b 100644 --- a/core/include/hax_core_interface.h +++ b/core/include/hax_core_interface.h @@ -45,6 +45,7 @@ int vcpu_put_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_get_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_set_regs(struct vcpu_t *vcpu, struct vcpu_state_t *vs); int vcpu_get_regs(struct vcpu_t *vcpu, struct vcpu_state_t *vs); +int vcpu_get_state_size(struct vcpu_t *vcpu); void vcpu_debug(struct vcpu_t *vcpu, struct hax_debug_t *debug); void * get_vcpu_host(struct vcpu_t *vcpu); diff --git a/core/include/vcpu.h b/core/include/vcpu.h index dc103dcd..3d3bbc46 100644 --- a/core/include/vcpu.h +++ b/core/include/vcpu.h @@ -173,6 +173,7 @@ struct vcpu_t { struct vm_t *vm; struct hax_mmu *mmu; struct vcpu_state_t *state; + uint64_t _cr8; struct hax_tunnel *tunnel; uint8_t *io_buf; struct hax_page *vmcs_page; @@ -262,6 +263,7 @@ int vcpu_get_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_put_fpu(struct vcpu_t *vcpu, struct fx_layout *fl); int vcpu_get_msr(struct vcpu_t *vcpu, uint64_t entry, uint64_t *val); int vcpu_put_msr(struct vcpu_t *vcpu, uint64_t entry, uint64_t val); +int vcpu_get_state_size(struct vcpu_t *vcpu); void vcpu_debug(struct vcpu_t *vcpu, struct hax_debug_t *debug); /* The declaration for OS wrapper code */ diff --git a/core/include/vm.h b/core/include/vm.h index 08e33849..41fc2875 100644 --- a/core/include/vm.h +++ b/core/include/vm.h @@ -62,6 +62,7 @@ struct vm_t { uint64_t flags; #define VM_FEATURES_FASTMMIO_BASIC 0x1 #define VM_FEATURES_FASTMMIO_EXTRA 0x2 +#define VM_FEATURES_CR8 0x4 uint32_t features; int vm_id; #define VPID_SEED_BITS 64 diff --git a/core/vcpu.c b/core/vcpu.c index 4adc99b3..85fe18da 100644 --- a/core/vcpu.c +++ b/core/vcpu.c @@ -1229,6 +1229,9 @@ void vcpu_save_host_state(struct vcpu_t *vcpu) vmwrite(vcpu, HOST_EFER, hstate->_efer); } + hstate->_pat = ia32_rdmsr(IA32_CR_PAT); + vmwrite(vcpu, HOST_PAT, hstate->_pat); + #ifdef HAX_ARCH_X86_64 vmwrite(vcpu, HOST_CS_SELECTOR, get_kernel_cs()); #else @@ -1395,15 +1398,15 @@ static void fill_common_vmcs(struct vcpu_t *vcpu) #ifdef HAX_ARCH_X86_64 exit_ctls = EXIT_CONTROL_HOST_ADDR_SPACE_SIZE | EXIT_CONTROL_LOAD_EFER | - EXIT_CONTROL_SAVE_DEBUG_CONTROLS; + EXIT_CONTROL_SAVE_DEBUG_CONTROLS | EXIT_CONTROL_LOAD_PAT; #endif #ifdef HAX_ARCH_X86_32 if (is_compatible()) { exit_ctls = EXIT_CONTROL_HOST_ADDR_SPACE_SIZE | EXIT_CONTROL_LOAD_EFER | - EXIT_CONTROL_SAVE_DEBUG_CONTROLS; + EXIT_CONTROL_SAVE_DEBUG_CONTROLS | EXIT_CONTROL_LOAD_PAT; } else { - exit_ctls = EXIT_CONTROL_SAVE_DEBUG_CONTROLS; + exit_ctls = EXIT_CONTROL_SAVE_DEBUG_CONTROLS | EXIT_CONTROL_LOAD_PAT; } #endif @@ -1475,6 +1478,9 @@ static void fill_common_vmcs(struct vcpu_t *vcpu) vmwrite(vcpu, HOST_EFER, ia32_rdmsr(IA32_EFER)); } + vmwrite(vcpu, HOST_PAT, ia32_rdmsr(IA32_CR_PAT)); + vmwrite(vcpu, GUEST_PAT, vcpu->cr_pat); + WRITE_CONTROLS(vcpu, VMX_ENTRY_CONTROLS, entry_ctls); vmwrite(vcpu, VMX_PAGE_FAULT_ERROR_CODE_MASK, 0); @@ -2070,6 +2076,8 @@ static void vmwrite_cr(struct vcpu_t *vcpu) entry_ctls &= ~ENTRY_CONTROL_LOAD_EFER; } + entry_ctls |= ENTRY_CONTROL_LOAD_PAT; + if (pcpu_ctls != vmx(vcpu, pcpu_ctls)) { vmx(vcpu, pcpu_ctls) = pcpu_ctls; vcpu->pcpu_ctls_dirty = 1; @@ -2575,7 +2583,7 @@ static void handle_cpuid_virtual(struct vcpu_t *vcpu, uint32_t a, uint32_t c) uint8_t physical_address_size; static uint32_t cpuid_1_features_edx = - // pat is disabled! + FEATURE(PAT) | FEATURE(FPU) | FEATURE(VME) | FEATURE(DE) | @@ -2705,10 +2713,13 @@ static void handle_cpuid_virtual(struct vcpu_t *vcpu, uint32_t a, uint32_t c) state->_edx = 0x0c040844; return; } - case 3: // Reserved + case 3: { // Reserved + state->_eax = state->_ebx = state->_ecx = state->_edx = 0; + return; + } case 4: { // Deterministic Cache Parameters // [31:26] cores per package - 1 - state->_eax = state->_ebx = state->_ecx = state->_edx = 0; + // Use host cache values. return; } case 5: // MONITOR/MWAIT @@ -3605,6 +3616,15 @@ static int misc_msr_write(struct vcpu_t *vcpu, uint32_t msr, uint64_t val) return 1; } +static inline bool is_pat_valid(uint64_t val) +{ + if (val & 0xF8F8F8F8F8F8F8F8) + return false; + + // 0, 1, 4, 5, 6, 7 are valid values. + return (val | ((val & 0x0202020202020202) << 1)) == val; +} + static int handle_msr_write(struct vcpu_t *vcpu, uint32_t msr, uint64_t val, bool by_host) { @@ -3763,7 +3783,15 @@ static int handle_msr_write(struct vcpu_t *vcpu, uint32_t msr, uint64_t val, break; } case IA32_CR_PAT: { + // Attempting to write an undefined memory type encoding into the + // PAT causes a general-protection (#GP) exception to be generated + if (!is_pat_valid(val)) { + r = 1; + break; + } + vcpu->cr_pat = val; + vmwrite(vcpu, GUEST_PAT, vcpu->cr_pat); break; } case IA32_MTRR_DEF_TYPE: { @@ -3964,6 +3992,13 @@ static int _copy_desc(segment_desc_t *old, segment_desc_t *new) return flags; } +int vcpu_get_state_size(struct vcpu_t *vcpu) +{ + if (vcpu->vm->features & VM_FEATURES_CR8) + return sizeof(struct vcpu_state_t); + return offsetof(struct vcpu_state_t, _cr8); +} + int vcpu_get_regs(struct vcpu_t *vcpu, struct vcpu_state_t *ustate) { struct vcpu_state_t *state = vcpu->state; @@ -3999,6 +4034,9 @@ int vcpu_get_regs(struct vcpu_t *vcpu, struct vcpu_state_t *ustate) _copy_desc(&state->_gdt, &ustate->_gdt); _copy_desc(&state->_idt, &ustate->_idt); + if (vcpu->vm->features & VM_FEATURES_CR8) + ustate->_cr8 = state->_cr8; + return 0; } @@ -4124,6 +4162,9 @@ int vcpu_set_regs(struct vcpu_t *vcpu, struct vcpu_state_t *ustate) VMWRITE_DESC(vcpu, IDTR, state->_idt); } + if (vcpu->vm->features & VM_FEATURES_CR8) + state->_cr8 = ustate->_cr8; + if ((vmcs_err = put_vmcs(vcpu, &flags))) { vcpu_set_panic(vcpu); hax_log(HAX_LOGPANIC, "put_vmcs failed on vcpu_set_regs: %x\n", diff --git a/core/vm.c b/core/vm.c index a5c02091..96a245b1 100644 --- a/core/vm.c +++ b/core/vm.c @@ -73,6 +73,9 @@ int hax_vm_set_qemuversion(struct vm_t *vm, struct hax_qemu_version *ver) vm->features |= VM_FEATURES_FASTMMIO_BASIC; if (ver->cur_version >= 0x4) { vm->features |= VM_FEATURES_FASTMMIO_EXTRA; + if (ver->cur_version >= 0x5) { + vm->features |= VM_FEATURES_CR8; + } } } return 0; diff --git a/include/hax.h b/include/hax.h index a95345f6..0fd64dca 100644 --- a/include/hax.h +++ b/include/hax.h @@ -39,7 +39,7 @@ // declaration struct vcpu_t; -#define HAX_CUR_VERSION 0x0004 +#define HAX_CUR_VERSION 0x0005 #define HAX_COMPAT_VERSION 0x0001 // EPT2 refers to the new memory virtualization engine, which implements lazy diff --git a/include/vcpu_state.h b/include/vcpu_state.h index e4507066..ea59112c 100644 --- a/include/vcpu_state.h +++ b/include/vcpu_state.h @@ -188,6 +188,8 @@ struct vcpu_state_t { uint32_t _activity_state; uint32_t pad; interruptibility_state_t _interruptibility_state; + + uint64_t _cr8; } PACKED; void dump(void); diff --git a/platforms/linux/components.c b/platforms/linux/components.c index 9a0ef1bd..0d4f8b7c 100644 --- a/platforms/linux/components.c +++ b/platforms/linux/components.c @@ -411,7 +411,8 @@ static long hax_vcpu_ioctl(struct file *filp, unsigned int cmd, } case HAX_VCPU_SET_REGS: { struct vcpu_state_t vc_state; - if (copy_from_user(&vc_state, argp, sizeof(vc_state))) { + int size = vcpu_get_state_size(cvcpu); + if (copy_from_user(&vc_state, argp, size)) { ret = -EFAULT; break; } @@ -420,8 +421,9 @@ static long hax_vcpu_ioctl(struct file *filp, unsigned int cmd, } case HAX_VCPU_GET_REGS: { struct vcpu_state_t vc_state; + int size = vcpu_get_state_size(cvcpu); ret = vcpu_get_regs(cvcpu, &vc_state); - if (copy_to_user(argp, &vc_state, sizeof(vc_state))) { + if (copy_to_user(argp, &vc_state, size)) { ret = -EFAULT; break; } diff --git a/platforms/windows/hax_entry.c b/platforms/windows/hax_entry.c index 74a0b440..7f51bad9 100644 --- a/platforms/windows/hax_entry.c +++ b/platforms/windows/hax_entry.c @@ -387,7 +387,7 @@ NTSTATUS HaxVcpuControl(PDEVICE_OBJECT DeviceObject, } case HAX_VCPU_SET_REGS: { struct vcpu_state_t *vc_state; - if(inBufLength < sizeof(struct vcpu_state_t)) { + if (inBufLength < vcpu_get_state_size(cvcpu)) { ret = STATUS_INVALID_PARAMETER; goto done; } @@ -398,7 +398,8 @@ NTSTATUS HaxVcpuControl(PDEVICE_OBJECT DeviceObject, } case HAX_VCPU_GET_REGS: { struct vcpu_state_t *vc_state; - if(outBufLength < sizeof(struct vcpu_state_t)) { + infret = vcpu_get_state_size(cvcpu); + if (outBufLength < infret) { ret = STATUS_INVALID_PARAMETER; goto done; @@ -406,7 +407,6 @@ NTSTATUS HaxVcpuControl(PDEVICE_OBJECT DeviceObject, vc_state = (struct vcpu_state_t *)outBuf; // vcpu_get_regs() cannot fail vcpu_get_regs(cvcpu, vc_state); - infret = sizeof(struct vcpu_state_t); break; } case HAX_VCPU_IOCTL_INTERRUPT: {