hv: support gva2gpa in different paging modes

Translate gva2gpa in different paging modes.
Change the definition of gva2gpa.
- return value for error status
- Add a parameter for error code when paging fault.
Change the definition of vm_gva2gpa.
- return value for error status
- Add a parameter for error code when paing fault.

Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com>
Acked-by: Xu, Anthony <anthony.xu@intel.com>
This commit is contained in:
Binbin Wu 2018-05-15 14:47:43 +08:00 committed by lijinxia
parent dd14d8e1b0
commit 9e7179c950
7 changed files with 248 additions and 41 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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)"

View File

@ -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 {

View File

@ -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);