diff --git a/hypervisor/arch/x86/guest/guest.c b/hypervisor/arch/x86/guest/guest.c index 3fd957402..974392a2e 100644 --- a/hypervisor/arch/x86/guest/guest.c +++ b/hypervisor/arch/x86/guest/guest.c @@ -15,6 +15,19 @@ uint32_t e820_entries; struct e820_entry e820[E820_MAX_ENTRIES]; struct e820_mem_params e820_mem; +struct page_walk_info { + uint64_t top_entry; /* Top level paging structure entry */ + int level; + int width; + bool is_user_mode; + bool is_write_access; + bool is_inst_fetch; + bool pse; /* CR4.PSE for 32bit paing, + * true for PAE/4-level paing */ + bool wp; /* CR0.WP */ + bool nxe; /* MSR_IA32_EFER_NXE_BIT */ +}; + inline bool is_vm0(struct vm *vm) { @@ -173,34 +186,179 @@ enum vm_paging_mode get_vcpu_paging_mode(struct vcpu *vcpu) return PAGING_MODE_4_LEVEL; } -uint64_t gva2gpa(struct vm *vm, uint64_t cr3, uint64_t gva) +/* TODO: Add code to check for Revserved bits, SMAP and PKE when do translation + * during page walk */ +static int _gva2gpa_common(struct vcpu *vcpu, struct page_walk_info *pw_info, + uint64_t gva, uint64_t *gpa, uint32_t *err_code) { - int level, index, shift; - uint64_t *base, addr, entry, page_size; - uint64_t gpa = 0; + int i, index, shift; + uint8_t *base; + uint64_t entry; + uint64_t addr, page_size; + int ret = 0; + int fault = 0; - addr = cr3; + if (pw_info->level < 1) + return -EINVAL; - for (level = 3; level >= 0; level--) { + addr = pw_info->top_entry; + for (i = pw_info->level - 1; i >= 0; i--) { addr = addr & IA32E_REF_MASK; - base = GPA2HVA(vm, addr); - ASSERT(base != NULL, "invalid ptp base."); - shift = level * 9 + 12; - index = (gva >> shift) & 0x1FF; + base = GPA2HVA(vcpu->vm, addr); + if (base == NULL) { + ret = -EFAULT; + goto out; + } + + shift = i * pw_info->width + 12; + index = (gva >> shift) & ((1UL << pw_info->width) - 1); page_size = 1UL << shift; - entry = base[index]; - if (level > 0 && (entry & MMU_32BIT_PDE_PS) != 0) + if (pw_info->width == 10) + /* 32bit entry */ + entry = *((uint32_t *)(base + 4 * index)); + else + entry = *((uint64_t *)(base + 8 * index)); + + /* check if the entry present */ + if (!(entry & MMU_32BIT_PDE_P)) { + ret = -EFAULT; + goto out; + } + /* check for R/W */ + if (pw_info->is_write_access && !(entry & MMU_32BIT_PDE_RW)) { + /* Case1: Supermode and wp is 1 + * Case2: Usermode */ + if (!(!pw_info->is_user_mode && !pw_info->wp)) + fault = 1; + } + /* check for nx, since for 32-bit paing, the XD bit is + * reserved(0), use the same logic as PAE/4-level paging */ + if (pw_info->is_inst_fetch && pw_info->nxe && + (entry & MMU_MEM_ATTR_BIT_EXECUTE_DISABLE)) + fault = 1; + + /* check for U/S */ + if (!(entry & MMU_32BIT_PDE_US) && pw_info->is_user_mode) + fault = 1; + + if (pw_info->pse && (i > 0 && (entry & MMU_32BIT_PDE_PS))) break; addr = entry; } - entry >>= shift; entry <<= (shift + 12); entry >>= 12; - gpa = entry | (gva & (page_size - 1)); + entry >>= shift; + /* shift left 12bit more and back to clear XD/Prot Key/Ignored bits */ + entry <<= (shift + 12); + entry >>= 12; + *gpa = entry | (gva & (page_size - 1)); +out: - return gpa; + if (fault) { + ret = -EFAULT; + *err_code |= PAGE_FAULT_P_FLAG; + } + return ret; } +static int _gva2gpa_pae(struct vcpu *vcpu, struct page_walk_info *pw_info, + uint64_t gva, uint64_t *gpa, uint32_t *err_code) +{ + int index; + uint64_t *base; + uint64_t entry; + uint64_t addr; + int ret; + + addr = pw_info->top_entry & 0xFFFFFFF0UL; + base = GPA2HVA(vcpu->vm, addr); + if (base == NULL) { + ret = -EFAULT; + goto out; + } + + index = (gva >> 30) & 0x3; + entry = base[index]; + + if (!(entry & MMU_32BIT_PDE_P)) { + ret = -EFAULT; + goto out; + } + + pw_info->level = 2; + pw_info->top_entry = entry; + ret = _gva2gpa_common(vcpu, pw_info, gva, gpa, err_code); + +out: + return ret; + +} + +/* Refer to SDM Vol.3A 6-39 section 6.15 for the format of paging fault error + * code. + * + * Caller should set the contect of err_code properly according to the address + * usage when calling this function: + * - If it is an address for write, set PAGE_FAULT_WR_FLAG in err_code. + * - If it is an address for instruction featch, set PAGE_FAULT_ID_FLAG in + * err_code. + * Caller should check the return value to confirm if the function success or + * not. + * If a protection volation detected during page walk, this function still will + * give the gpa translated, it is up to caller to decide if it need to inject a + * #PF or not. + * - Return 0 for success. + * - Return -EINVAL for invalid parameter. + * - Return -EFAULT for paging fault, and refer to err_code for paging fault + * error code. + */ +int gva2gpa(struct vcpu *vcpu, uint64_t gva, uint64_t *gpa, + uint32_t *err_code) +{ + struct run_context *cur_context = + &vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context]; + enum vm_paging_mode pm = get_vcpu_paging_mode(vcpu); + struct page_walk_info pw_info; + int ret = 0; + + if (!gpa || !err_code) + return -EINVAL; + *gpa = 0; + + pw_info.top_entry = cur_context->cr3; + pw_info.level = pm; + pw_info.is_write_access = !!(*err_code & PAGE_FAULT_WR_FLAG); + pw_info.is_inst_fetch = !!(*err_code & PAGE_FAULT_ID_FLAG); + pw_info.is_user_mode = ((exec_vmread(VMX_GUEST_CS_SEL) & 0x3) == 3); + pw_info.pse = true; + pw_info.nxe = cur_context->ia32_efer & MSR_IA32_EFER_NXE_BIT; + pw_info.wp = !!(cur_context->cr0 & CR0_WP); + + *err_code &= ~PAGE_FAULT_P_FLAG; + + if (pm == PAGING_MODE_4_LEVEL) { + pw_info.width = 9; + ret = _gva2gpa_common(vcpu, &pw_info, gva, gpa, err_code); + } else if(pm == PAGING_MODE_3_LEVEL) { + pw_info.width = 9; + ret = _gva2gpa_pae(vcpu, &pw_info, gva, gpa, err_code); + } else if (pm == PAGING_MODE_2_LEVEL) { + pw_info.width = 10; + pw_info.pse = !!(cur_context->cr4 & CR4_PSE); + pw_info.nxe = false; + ret = _gva2gpa_common(vcpu, &pw_info, gva, gpa, err_code); + } else + *gpa = gva; + + if (ret == -EFAULT) { + if (pw_info.is_user_mode) + *err_code |= PAGE_FAULT_US_FLAG; + } + + return ret; +} + + void init_e820(void) { unsigned int i; diff --git a/hypervisor/arch/x86/guest/instr_emul.c b/hypervisor/arch/x86/guest/instr_emul.c index 6387eb025..87ee48416 100644 --- a/hypervisor/arch/x86/guest/instr_emul.c +++ b/hypervisor/arch/x86/guest/instr_emul.c @@ -674,6 +674,7 @@ emulate_movs(struct vcpu *vcpu, __unused uint64_t gpa, struct vie *vie, uint64_t dstaddr, srcaddr, dstgpa, srcgpa; uint64_t rcx, rdi, rsi, rflags; int error, fault, opsize, seg, repeat; + uint32_t err_code; opsize = (vie->op.op_byte == 0xA4) ? 1 : vie->opsize; error = 0; @@ -713,8 +714,14 @@ emulate_movs(struct vcpu *vcpu, __unused uint64_t gpa, struct vie *vie, if (error || fault) goto done; - vm_gva2gpa(vcpu, srcaddr, &srcgpa); - vm_gva2gpa(vcpu, dstaddr, &dstgpa); + err_code = 0; + error = vm_gva2gpa(vcpu, srcaddr, &srcgpa, &err_code); + if (error) + goto done; + err_code = PAGE_FAULT_WR_FLAG; + error = vm_gva2gpa(vcpu, dstaddr, &dstgpa, &err_code); + if (error) + goto done; memcpy_s((char *)dstaddr, 16, (char *)srcaddr, opsize); error = vie_read_register(vcpu, VM_REG_GUEST_RSI, &rsi); @@ -1236,6 +1243,7 @@ emulate_stack_op(struct vcpu *vcpu, uint64_t mmio_gpa, struct vie *vie, struct seg_desc ss_desc; uint64_t cr0, rflags, rsp, stack_gla, stack_gpa, val; int error, size, stackaddrsize, pushop; + uint32_t err_code = 0; memset(&ss_desc, 0, sizeof(ss_desc)); @@ -1302,7 +1310,13 @@ emulate_stack_op(struct vcpu *vcpu, uint64_t mmio_gpa, struct vie *vie, return 0; } - vm_gva2gpa(vcpu, stack_gla, &stack_gpa); + if (pushop) + err_code |= PAGE_FAULT_WR_FLAG; + error = vm_gva2gpa(vcpu, stack_gla, &stack_gpa, &err_code); + if (error) { + pr_err("%s: failed to translate gva2gpa", __func__); + return error; + } if (pushop) { error = memread(vcpu, mmio_gpa, &val, size, arg); if (error == 0) diff --git a/hypervisor/arch/x86/guest/instr_emul_wrapper.c b/hypervisor/arch/x86/guest/instr_emul_wrapper.c index 8e2c005ef..c9dac5332 100644 --- a/hypervisor/arch/x86/guest/instr_emul_wrapper.c +++ b/hypervisor/arch/x86/guest/instr_emul_wrapper.c @@ -304,15 +304,15 @@ static int mmio_write(struct vcpu *vcpu, __unused uint64_t gpa, uint64_t wval, return 0; } -void vm_gva2gpa(struct vcpu *vcpu, uint64_t gva, uint64_t *gpa) +int vm_gva2gpa(struct vcpu *vcpu, uint64_t gva, uint64_t *gpa, + uint32_t *err_code) { ASSERT(gpa != NULL, "Error in input arguments"); ASSERT(vcpu != NULL, "Invalid vcpu id when gva2gpa"); - *gpa = gva2gpa(vcpu->vm, - vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3, gva); + return gva2gpa(vcpu, gva, gpa, err_code); } uint8_t decode_instruction(struct vcpu *vcpu) @@ -323,13 +323,19 @@ uint8_t decode_instruction(struct vcpu *vcpu) uint32_t csar; int retval = 0; enum vm_cpu_mode cpu_mode; + int error; + uint32_t err_code; guest_rip_gva = vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].rip; - guest_rip_gpa = gva2gpa(vcpu->vm, - vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3, - guest_rip_gva); + err_code = PAGE_FAULT_ID_FLAG; + error = gva2gpa(vcpu, guest_rip_gva, &guest_rip_gpa, &err_code); + if (error) { + pr_err("gva2gpa failed for guest_rip_gva 0x%016llx:", + guest_rip_gva); + return 0; + } guest_rip_hva = GPA2HVA(vcpu->vm, guest_rip_gpa); emul_cnx = &per_cpu(g_inst_ctxt, vcpu->pcpu_id); diff --git a/hypervisor/arch/x86/guest/ucode.c b/hypervisor/arch/x86/guest/ucode.c index 7ac907eb1..28dd38145 100644 --- a/hypervisor/arch/x86/guest/ucode.c +++ b/hypervisor/arch/x86/guest/ucode.c @@ -32,10 +32,16 @@ void acrn_update_ucode(struct vcpu *vcpu, uint64_t v) int data_size, data_page_num; uint8_t *ucode_ptr, *ptr; int chunk_size; + int error = 0; + uint32_t err_code; gva = v - sizeof(struct ucode_header); - vm_gva2gpa(vcpu, gva, &gpa); + err_code = 0; + error = vm_gva2gpa(vcpu, gva, &gpa, &err_code); + if (error) + return; + uhdr = (struct ucode_header *)GPA2HVA(vcpu->vm, gpa); data_size = GET_DATA_SIZE(uhdr) + sizeof(struct ucode_header); @@ -60,7 +66,12 @@ void acrn_update_ucode(struct vcpu *vcpu, uint64_t v) ucode_ptr += chunk_size; gva += chunk_size; - vm_gva2gpa(vcpu, gva, &gpa); + err_code = 0; + error = vm_gva2gpa(vcpu, gva, &gpa, &err_code); + if (error) { + free(ucode_ptr); + return; + } hva = (uint64_t)GPA2HVA(vcpu->vm, gpa); } diff --git a/hypervisor/debug/dump.c b/hypervisor/debug/dump.c index f2abeeb39..4f9f91cb6 100644 --- a/hypervisor/debug/dump.c +++ b/hypervisor/debug/dump.c @@ -104,8 +104,16 @@ static void dump_guest_stack(struct vcpu *vcpu) uint64_t page2_size; struct run_context *cur_context = &vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context]; + uint32_t err_code; + int err; - gpa = gva2gpa(vcpu->vm, cur_context->cr3, cur_context->rsp); + err_code = 0; + err = gva2gpa(vcpu, cur_context->rsp, &gpa, &err_code); + if (err) { + printf("gva2gpa failed for guest rsp 0x%016llx\r\n", + cur_context->rsp); + return; + } hpa = gpa2hpa(vcpu->vm, gpa); printf("\r\nGuest Stack:\r\n"); printf("Dump stack for vcpu %d, from gva 0x%016llx ->" @@ -131,8 +139,15 @@ static void dump_guest_stack(struct vcpu *vcpu) "0x%016llx\r\n", (hpa+i*32), tmp[i*4], tmp[i*4+1], tmp[i*4+2], tmp[i*4+3]); } - gpa = gva2gpa(vcpu->vm, cur_context->cr3, + err_code = 0; + err = gva2gpa(vcpu, cur_context->rsp + page1_size, &gpa, + &err_code); + if (err) { + printf("gva2gpa failed for guest rsp 0x%016llx\r\n", cur_context->rsp + page1_size); + return; + + } hpa = gpa2hpa(vcpu->vm, gpa); printf("Dump stack for vcpu %d, from gva 0x%016llx ->" "gpa 0x%016llx -> hpa 0x%016llx \r\n", @@ -159,6 +174,8 @@ static void show_guest_call_trace(struct vcpu *vcpu) uint64_t count = 0; struct run_context *cur_context = &vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context]; + int err; + uint32_t err_code; bp = cur_context->guest_cpu_regs.regs.rbp; printf("Guest Call Trace: **************************************\r\n"); @@ -177,7 +194,11 @@ static void show_guest_call_trace(struct vcpu *vcpu) * if the address is invalid, it will cause hv page fault * then halt system */ while ((count++ < CALL_TRACE_HIERARCHY_MAX) && (bp != 0)) { - gpa = gva2gpa(vcpu->vm, cur_context->cr3, bp); + err = gva2gpa(vcpu, bp, &gpa, &err_code); + if (err) { + printf("gva2gpa failed for guest bp 0x%016llx\r\n", bp); + break; + } hpa = gpa2hpa(vcpu->vm, gpa); hva = HPA2HVA(hpa); printf("BP_GVA(0x%016llx)->BP_GPA(0x%016llx)" diff --git a/hypervisor/debug/shell_internal.c b/hypervisor/debug/shell_internal.c index 37a2e702d..0c366829e 100644 --- a/hypervisor/debug/shell_internal.c +++ b/hypervisor/debug/shell_internal.c @@ -648,6 +648,7 @@ int shell_vcpu_dumpreg(struct shell *p_shell, uint64_t gpa, hpa, i; uint64_t *tmp; struct run_context *cur_context; + uint32_t err_code; /* User input invalidation */ if (argc != 3) { @@ -724,10 +725,8 @@ int shell_vcpu_dumpreg(struct shell *p_shell, shell_puts(p_shell, temp_str); /* dump sp */ - gpa = gva2gpa(vm, cur_context->cr3, - cur_context->rsp); - if (gpa == 0) { - status = -EINVAL; + status = gva2gpa(vcpu, cur_context->rsp, &gpa, &err_code); + if (status) { shell_puts(p_shell, "Cannot handle user gva yet!\r\n"); } else { hpa = gpa2hpa(vm, gpa); @@ -763,6 +762,7 @@ int shell_vcpu_dumpmem(struct shell *p_shell, char temp_str[MAX_STR_SIZE]; struct vm *vm; struct vcpu *vcpu; + uint32_t err_code; /* User input invalidation */ if (argc != 4 && argc != 5) { @@ -791,12 +791,8 @@ int shell_vcpu_dumpmem(struct shell *p_shell, vcpu = vcpu_from_vid(vm, (long)vcpu_id); if (vcpu) { - struct run_context *cur_context = - &vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context]; - - gpa = gva2gpa(vcpu->vm, cur_context->cr3, gva); - if (gpa == 0) { - status = -EINVAL; + status = gva2gpa(vcpu, gva, &gpa, &err_code); + if (status) { shell_puts(p_shell, "Cannot handle user gva yet!\r\n"); } else { diff --git a/hypervisor/include/arch/x86/guest/guest.h b/hypervisor/include/arch/x86/guest/guest.h index 70bb7b5db..7cc41418b 100644 --- a/hypervisor/include/arch/x86/guest/guest.h +++ b/hypervisor/include/arch/x86/guest/guest.h @@ -91,8 +91,9 @@ bool is_vm0(struct vm *vm); bool vm_lapic_disabled(struct vm *vm); uint64_t vcpumask2pcpumask(struct vm *vm, uint64_t vdmask); -uint64_t gva2gpa(struct vm *vm, uint64_t cr3, uint64_t gva); -void vm_gva2gpa(struct vcpu *vcpu, uint64_t gla, uint64_t *gpa); +int gva2gpa(struct vcpu *vcpu, uint64_t gva, uint64_t *gpa, uint32_t *err_code); +int vm_gva2gpa(struct vcpu *vcpu, uint64_t gla, uint64_t *gpa, + uint32_t *err_code); struct vcpu *get_primary_vcpu(struct vm *vm); struct vcpu *vcpu_from_vid(struct vm *vm, int vcpu_id);