mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-01 20:05:30 +00:00
HV: instr_emul: Rearrange logic of instr_emul*
Originally, there is cross-references between instr_emul.h and instr_emul_wrapper.h. User must include both of them when calling instruction emulation functions. This will raise up some confusion and inconvenience. So we rearrange the logic of instruction emulation code as following: - External API -- defined in instr_emul.h * decode_instruction(struct vcpu *vcpu) * emulate_instruction(struct vcpu *vcpu) - Make all other functions as static in instr_emul.c - Remove instr_emul_wrapper.c/h No functional change. Signed-off-by: Kaige Fu <kaige.fu@intel.com> Reviewed-by: Eddie Dong <eddie.dong@intel.com> Reviewed-by: Yin Fengwei <fengwei.yin@intel.com>
This commit is contained in:
parent
ce79d3aa24
commit
f03ae8d09c
@ -139,7 +139,6 @@ C_SRCS += arch/x86/pm.c
|
||||
S_SRCS += arch/x86/wakeup.S
|
||||
C_SRCS += arch/x86/guest/vcpu.c
|
||||
C_SRCS += arch/x86/guest/vm.c
|
||||
C_SRCS += arch/x86/guest/instr_emul_wrapper.c
|
||||
C_SRCS += arch/x86/guest/vlapic.c
|
||||
C_SRCS += arch/x86/guest/guest.c
|
||||
C_SRCS += arch/x86/guest/vmcall.c
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#include "guest/instr_emul_wrapper.h"
|
||||
#include "guest/instr_emul.h"
|
||||
|
||||
#define ACRN_DBG_EPT 6U
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#include "instr_emul_wrapper.h"
|
||||
#include "instr_emul.h"
|
||||
|
||||
/* struct vie_op.op_type */
|
||||
@ -191,6 +190,447 @@ static uint64_t size2mask[9] = {
|
||||
[8] = ~0UL,
|
||||
};
|
||||
|
||||
#define VMX_INVALID_VMCS_FIELD 0xffffffffU
|
||||
|
||||
static int encode_vmcs_seg_desc(enum cpu_reg_name seg,
|
||||
uint32_t *base, uint32_t *lim, uint32_t *acc)
|
||||
{
|
||||
switch (seg) {
|
||||
case CPU_REG_ES:
|
||||
*base = VMX_GUEST_ES_BASE;
|
||||
*lim = VMX_GUEST_ES_LIMIT;
|
||||
*acc = VMX_GUEST_ES_ATTR;
|
||||
break;
|
||||
case CPU_REG_CS:
|
||||
*base = VMX_GUEST_CS_BASE;
|
||||
*lim = VMX_GUEST_CS_LIMIT;
|
||||
*acc = VMX_GUEST_CS_ATTR;
|
||||
break;
|
||||
case CPU_REG_SS:
|
||||
*base = VMX_GUEST_SS_BASE;
|
||||
*lim = VMX_GUEST_SS_LIMIT;
|
||||
*acc = VMX_GUEST_SS_ATTR;
|
||||
break;
|
||||
case CPU_REG_DS:
|
||||
*base = VMX_GUEST_DS_BASE;
|
||||
*lim = VMX_GUEST_DS_LIMIT;
|
||||
*acc = VMX_GUEST_DS_ATTR;
|
||||
break;
|
||||
case CPU_REG_FS:
|
||||
*base = VMX_GUEST_FS_BASE;
|
||||
*lim = VMX_GUEST_FS_LIMIT;
|
||||
*acc = VMX_GUEST_FS_ATTR;
|
||||
break;
|
||||
case CPU_REG_GS:
|
||||
*base = VMX_GUEST_GS_BASE;
|
||||
*lim = VMX_GUEST_GS_LIMIT;
|
||||
*acc = VMX_GUEST_GS_ATTR;
|
||||
break;
|
||||
case CPU_REG_TR:
|
||||
*base = VMX_GUEST_TR_BASE;
|
||||
*lim = VMX_GUEST_TR_LIMIT;
|
||||
*acc = VMX_GUEST_TR_ATTR;
|
||||
break;
|
||||
case CPU_REG_LDTR:
|
||||
*base = VMX_GUEST_LDTR_BASE;
|
||||
*lim = VMX_GUEST_LDTR_LIMIT;
|
||||
*acc = VMX_GUEST_LDTR_ATTR;
|
||||
break;
|
||||
case CPU_REG_IDTR:
|
||||
*base = VMX_GUEST_IDTR_BASE;
|
||||
*lim = VMX_GUEST_IDTR_LIMIT;
|
||||
*acc = 0xffffffffU;
|
||||
break;
|
||||
case CPU_REG_GDTR:
|
||||
*base = VMX_GUEST_GDTR_BASE;
|
||||
*lim = VMX_GUEST_GDTR_LIMIT;
|
||||
*acc = 0xffffffffU;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*Description:
|
||||
*This local function is to covert register names into
|
||||
*the corresponding field index MACROs in VMCS.
|
||||
*
|
||||
*Post Condition:
|
||||
*In the non-general register names group (CPU_REG_CR0~CPU_REG_GDTR),
|
||||
*for register names CPU_REG_CR2, CPU_REG_IDTR and CPU_REG_GDTR,
|
||||
*this function returns VMX_INVALID_VMCS_FIELD;
|
||||
*for other register names, it returns correspoding field index MACROs
|
||||
*in VMCS.
|
||||
*
|
||||
**/
|
||||
static uint32_t get_vmcs_field(enum cpu_reg_name ident)
|
||||
{
|
||||
switch (ident) {
|
||||
case CPU_REG_CR0:
|
||||
return VMX_GUEST_CR0;
|
||||
case CPU_REG_CR3:
|
||||
return VMX_GUEST_CR3;
|
||||
case CPU_REG_CR4:
|
||||
return VMX_GUEST_CR4;
|
||||
case CPU_REG_DR7:
|
||||
return VMX_GUEST_DR7;
|
||||
case CPU_REG_RSP:
|
||||
return VMX_GUEST_RSP;
|
||||
case CPU_REG_RIP:
|
||||
return VMX_GUEST_RIP;
|
||||
case CPU_REG_RFLAGS:
|
||||
return VMX_GUEST_RFLAGS;
|
||||
case CPU_REG_ES:
|
||||
return VMX_GUEST_ES_SEL;
|
||||
case CPU_REG_CS:
|
||||
return VMX_GUEST_CS_SEL;
|
||||
case CPU_REG_SS:
|
||||
return VMX_GUEST_SS_SEL;
|
||||
case CPU_REG_DS:
|
||||
return VMX_GUEST_DS_SEL;
|
||||
case CPU_REG_FS:
|
||||
return VMX_GUEST_FS_SEL;
|
||||
case CPU_REG_GS:
|
||||
return VMX_GUEST_GS_SEL;
|
||||
case CPU_REG_TR:
|
||||
return VMX_GUEST_TR_SEL;
|
||||
case CPU_REG_LDTR:
|
||||
return VMX_GUEST_LDTR_SEL;
|
||||
case CPU_REG_EFER:
|
||||
return VMX_GUEST_IA32_EFER_FULL;
|
||||
case CPU_REG_PDPTE0:
|
||||
return VMX_GUEST_PDPTE0_FULL;
|
||||
case CPU_REG_PDPTE1:
|
||||
return VMX_GUEST_PDPTE1_FULL;
|
||||
case CPU_REG_PDPTE2:
|
||||
return VMX_GUEST_PDPTE2_FULL;
|
||||
case CPU_REG_PDPTE3:
|
||||
return VMX_GUEST_PDPTE3_FULL;
|
||||
default:
|
||||
return VMX_INVALID_VMCS_FIELD;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_segment_register(enum cpu_reg_name reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CPU_REG_ES:
|
||||
case CPU_REG_CS:
|
||||
case CPU_REG_SS:
|
||||
case CPU_REG_DS:
|
||||
case CPU_REG_FS:
|
||||
case CPU_REG_GS:
|
||||
case CPU_REG_TR:
|
||||
case CPU_REG_LDTR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_descriptor_table(enum cpu_reg_name reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CPU_REG_IDTR:
|
||||
case CPU_REG_GDTR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int vm_get_register(struct vcpu *vcpu, enum cpu_reg_name reg,
|
||||
uint64_t *retval)
|
||||
{
|
||||
if (vcpu == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg > CPU_REG_LAST) || (reg < CPU_REG_FIRST)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg >= CPU_REG_GENERAL_FIRST) && (reg <= CPU_REG_GENERAL_LAST)) {
|
||||
*retval = vcpu_get_gpreg(vcpu, reg);
|
||||
} else if ((reg >= CPU_REG_NONGENERAL_FIRST) &&
|
||||
(reg <= CPU_REG_NONGENERAL_LAST)) {
|
||||
uint32_t field = get_vmcs_field(reg);
|
||||
|
||||
if (field != VMX_INVALID_VMCS_FIELD) {
|
||||
if (reg <= CPU_REG_NATURAL_LAST) {
|
||||
*retval = exec_vmread(field);
|
||||
} else if (reg <= CPU_REG_64BIT_LAST) {
|
||||
*retval = exec_vmread64(field);
|
||||
} else {
|
||||
*retval = (uint64_t)exec_vmread16(field);
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vm_set_register(struct vcpu *vcpu, enum cpu_reg_name reg,
|
||||
uint64_t val)
|
||||
{
|
||||
if (vcpu == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg > CPU_REG_LAST) || (reg < CPU_REG_FIRST)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg >= CPU_REG_GENERAL_FIRST) && (reg <= CPU_REG_GENERAL_LAST)) {
|
||||
vcpu_set_gpreg(vcpu, reg, val);
|
||||
} else if ((reg >= CPU_REG_NONGENERAL_FIRST) &&
|
||||
(reg <= CPU_REG_NONGENERAL_LAST)) {
|
||||
uint32_t field = get_vmcs_field(reg);
|
||||
|
||||
if (field != VMX_INVALID_VMCS_FIELD) {
|
||||
if (reg <= CPU_REG_NATURAL_LAST) {
|
||||
exec_vmwrite(field, val);
|
||||
} else if (reg <= CPU_REG_64BIT_LAST) {
|
||||
exec_vmwrite64(field, val);
|
||||
} else {
|
||||
exec_vmwrite16(field, (uint16_t)val);
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vm_set_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc)
|
||||
{
|
||||
int error;
|
||||
uint32_t base, limit, access;
|
||||
|
||||
if ((vcpu == NULL) || (desc == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_segment_register(seg) && !is_descriptor_table(seg)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
|
||||
if ((error != 0) || (access == 0xffffffffU)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
exec_vmwrite(base, desc->base);
|
||||
exec_vmwrite32(limit, desc->limit);
|
||||
exec_vmwrite32(access, desc->access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vm_get_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc)
|
||||
{
|
||||
int error;
|
||||
uint32_t base, limit, access;
|
||||
|
||||
if ((vcpu == NULL) || (desc == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_segment_register(seg) && !is_descriptor_table(seg)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
|
||||
if ((error != 0) || (access == 0xffffffffU)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc->base = exec_vmread(base);
|
||||
desc->limit = exec_vmread32(limit);
|
||||
desc->access = exec_vmread32(access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_guest_paging_info(struct vcpu *vcpu, struct instr_emul_ctxt *emul_ctxt,
|
||||
uint32_t csar)
|
||||
{
|
||||
uint8_t cpl;
|
||||
|
||||
cpl = (uint8_t)((csar >> 5) & 3U);
|
||||
emul_ctxt->paging.cr3 = exec_vmread(VMX_GUEST_CR3);
|
||||
emul_ctxt->paging.cpl = cpl;
|
||||
emul_ctxt->paging.cpu_mode = get_vcpu_mode(vcpu);
|
||||
emul_ctxt->paging.paging_mode = get_vcpu_paging_mode(vcpu);
|
||||
}
|
||||
|
||||
static int vie_alignment_check(uint8_t cpl, uint8_t size, uint64_t cr0,
|
||||
uint64_t rflags, uint64_t gla)
|
||||
{
|
||||
pr_dbg("Checking alignment with cpl: %hhu, addrsize: %hhu", cpl, size);
|
||||
|
||||
if (cpl < 3U || (cr0 & CR0_AM) == 0UL || (rflags & PSL_AC) == 0UL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((gla & (size - 1U)) != 0UL) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla)
|
||||
{
|
||||
uint64_t mask;
|
||||
|
||||
if (cpu_mode != CPU_MODE_64BIT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The value of the bit 47 in the 'gla' should be replicated in the
|
||||
* most significant 16 bits.
|
||||
*/
|
||||
mask = ~((1UL << 48) - 1);
|
||||
if ((gla & (1UL << 47)) != 0U) {
|
||||
return ((gla & mask) != mask) ? 1 : 0;
|
||||
} else {
|
||||
return ((gla & mask) != 0U) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*@pre seg must be segment register index
|
||||
*@pre length_arg must be 1, 2, 4 or 8
|
||||
*@pre prot must be PROT_READ or PROT_WRITE
|
||||
*
|
||||
*return 0 - on success
|
||||
*return -1 - on failure
|
||||
*/
|
||||
static int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc, uint64_t offset_arg, uint8_t length_arg,
|
||||
uint8_t addrsize, uint32_t prot, uint64_t *gla)
|
||||
{
|
||||
uint64_t firstoff, low_limit, high_limit, segbase;
|
||||
uint64_t offset = offset_arg;
|
||||
uint8_t length = length_arg;
|
||||
uint8_t glasize;
|
||||
uint32_t type;
|
||||
|
||||
firstoff = offset;
|
||||
if (cpu_mode == CPU_MODE_64BIT) {
|
||||
if (addrsize != 4U && addrsize != 8U) {
|
||||
pr_dbg("%s: invalid addr size %d for cpu mode %d",
|
||||
__func__, addrsize, cpu_mode);
|
||||
return -1;
|
||||
}
|
||||
glasize = 8U;
|
||||
} else {
|
||||
if (addrsize != 2U && addrsize != 4U) {
|
||||
pr_dbg("%s: invalid addr size %d for cpu mode %d",
|
||||
__func__, addrsize, cpu_mode);
|
||||
return -1;
|
||||
}
|
||||
glasize = 4U;
|
||||
/*
|
||||
* If the segment selector is loaded with a NULL selector
|
||||
* then the descriptor is unusable and attempting to use
|
||||
* it results in a #GP(0).
|
||||
*/
|
||||
if (SEG_DESC_UNUSABLE(desc->access)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The processor generates a #NP exception when a segment
|
||||
* register is loaded with a selector that points to a
|
||||
* descriptor that is not present. If this was the case then
|
||||
* it would have been checked before the VM-exit.
|
||||
*/
|
||||
if (SEG_DESC_PRESENT(desc->access) != 0) {
|
||||
/* TODO: Inject #NP */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The descriptor type must indicate a code/data segment. */
|
||||
type = SEG_DESC_TYPE(desc->access);
|
||||
if (type < 16 || type > 31) {
|
||||
/*TODO: Inject #GP */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((prot & PROT_READ) != 0U) {
|
||||
/* #GP on a read access to a exec-only code segment */
|
||||
if ((type & 0xAU) == 0x8U) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((prot & PROT_WRITE) != 0U) {
|
||||
/*
|
||||
* #GP on a write access to a code segment or a
|
||||
* read-only data segment.
|
||||
*/
|
||||
if ((type & 0x8U) != 0U) { /* code segment */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((type & 0xAU) == 0U) { /* read-only data seg */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 'desc->limit' is fully expanded taking granularity into
|
||||
* account.
|
||||
*/
|
||||
if ((type & 0xCU) == 0x4U) {
|
||||
/* expand-down data segment */
|
||||
low_limit = desc->limit + 1U;
|
||||
high_limit = SEG_DESC_DEF32(desc->access) ?
|
||||
0xffffffffU : 0xffffU;
|
||||
} else {
|
||||
/* code segment or expand-up data segment */
|
||||
low_limit = 0U;
|
||||
high_limit = desc->limit;
|
||||
}
|
||||
|
||||
while (length > 0U) {
|
||||
offset &= size2mask[addrsize];
|
||||
if (offset < low_limit || offset > high_limit) {
|
||||
return -1;
|
||||
}
|
||||
offset++;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In 64-bit mode all segments except %fs and %gs have a segment
|
||||
* base address of 0.
|
||||
*/
|
||||
if (cpu_mode == CPU_MODE_64BIT && seg != CPU_REG_FS &&
|
||||
seg != CPU_REG_GS) {
|
||||
segbase = 0UL;
|
||||
} else {
|
||||
segbase = desc->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Truncate 'firstoff' to the effective address size before adding
|
||||
* it to the segment base.
|
||||
*/
|
||||
firstoff &= size2mask[addrsize];
|
||||
*gla = (segbase + firstoff) & size2mask[glasize];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmio_read(struct vcpu *vcpu, uint64_t *rval)
|
||||
{
|
||||
if (vcpu == NULL) {
|
||||
@ -211,8 +651,8 @@ static int mmio_write(struct vcpu *vcpu, uint64_t wval)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vie_calc_bytereg(struct instr_emul_vie *vie, enum cpu_reg_name *reg, int *lhbr)
|
||||
static void vie_calc_bytereg(struct instr_emul_vie *vie,
|
||||
enum cpu_reg_name *reg, int *lhbr)
|
||||
{
|
||||
*lhbr = 0;
|
||||
*reg = vie->reg;
|
||||
@ -237,8 +677,8 @@ vie_calc_bytereg(struct instr_emul_vie *vie, enum cpu_reg_name *reg, int *lhbr)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vie_read_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie, uint8_t *rval)
|
||||
static int vie_read_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie,
|
||||
uint8_t *rval)
|
||||
{
|
||||
uint64_t val;
|
||||
int error, lhbr;
|
||||
@ -259,8 +699,8 @@ vie_read_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie, uint8_t *rval)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
vie_write_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie, uint8_t byte)
|
||||
static int vie_write_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie,
|
||||
uint8_t byte)
|
||||
{
|
||||
uint64_t origval, val, mask;
|
||||
int error, lhbr;
|
||||
@ -285,9 +725,8 @@ vie_write_bytereg(struct vcpu *vcpu, struct instr_emul_vie *vie, uint8_t byte)
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
vie_update_register(struct vcpu *vcpu, enum cpu_reg_name reg,
|
||||
uint64_t val_arg, uint8_t size)
|
||||
static int vie_update_register(struct vcpu *vcpu, enum cpu_reg_name reg,
|
||||
uint64_t val_arg, uint8_t size)
|
||||
{
|
||||
int error;
|
||||
uint64_t origval;
|
||||
@ -582,8 +1021,7 @@ static int emulate_movx(struct vcpu *vcpu, struct instr_emul_vie *vie)
|
||||
/*
|
||||
* Helper function to calculate and validate a linear address.
|
||||
*/
|
||||
static int
|
||||
get_gla(struct vcpu *vcpu, __unused struct instr_emul_vie *vie,
|
||||
static int get_gla(struct vcpu *vcpu, __unused struct instr_emul_vie *vie,
|
||||
struct vm_guest_paging *paging,
|
||||
uint8_t opsize, uint8_t addrsize, uint32_t prot, enum cpu_reg_name seg,
|
||||
enum cpu_reg_name gpr, uint64_t *gla, int *fault)
|
||||
@ -1365,7 +1803,7 @@ static int emulate_bittest(struct vcpu *vcpu, struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vmm_emulate_instruction(struct instr_emul_ctxt *ctxt)
|
||||
static int vmm_emulate_instruction(struct instr_emul_ctxt *ctxt)
|
||||
{
|
||||
struct vm_guest_paging *paging = &ctxt->paging;
|
||||
struct instr_emul_vie *vie = &ctxt->vie;
|
||||
@ -1423,167 +1861,7 @@ int vmm_emulate_instruction(struct instr_emul_ctxt *ctxt)
|
||||
return error;
|
||||
}
|
||||
|
||||
int vie_alignment_check(uint8_t cpl, uint8_t size, uint64_t cr0,
|
||||
uint64_t rflags, uint64_t gla)
|
||||
{
|
||||
pr_dbg("Checking alignment with cpl: %hhu, addrsize: %hhu", cpl, size);
|
||||
|
||||
if (cpl < 3U || (cr0 & CR0_AM) == 0UL || (rflags & PSL_AC) == 0UL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((gla & (size - 1U)) != 0UL) ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla)
|
||||
{
|
||||
uint64_t mask;
|
||||
|
||||
if (cpu_mode != CPU_MODE_64BIT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The value of the bit 47 in the 'gla' should be replicated in the
|
||||
* most significant 16 bits.
|
||||
*/
|
||||
mask = ~((1UL << 48) - 1);
|
||||
if ((gla & (1UL << 47)) != 0U) {
|
||||
return ((gla & mask) != mask) ? 1 : 0;
|
||||
} else {
|
||||
return ((gla & mask) != 0U) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*@pre seg must be segment register index
|
||||
*@pre length_arg must be 1, 2, 4 or 8
|
||||
*@pre prot must be PROT_READ or PROT_WRITE
|
||||
*
|
||||
*return 0 - on success
|
||||
*return -1 - on failure
|
||||
*/
|
||||
int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc, uint64_t offset_arg, uint8_t length_arg,
|
||||
uint8_t addrsize, uint32_t prot, uint64_t *gla)
|
||||
{
|
||||
uint64_t firstoff, low_limit, high_limit, segbase;
|
||||
uint64_t offset = offset_arg;
|
||||
uint8_t length = length_arg;
|
||||
uint8_t glasize;
|
||||
uint32_t type;
|
||||
|
||||
firstoff = offset;
|
||||
if (cpu_mode == CPU_MODE_64BIT) {
|
||||
if (addrsize != 4U && addrsize != 8U) {
|
||||
pr_dbg("%s: invalid addr size %d for cpu mode %d",
|
||||
__func__, addrsize, cpu_mode);
|
||||
return -1;
|
||||
}
|
||||
glasize = 8U;
|
||||
} else {
|
||||
if (addrsize != 2U && addrsize != 4U) {
|
||||
pr_dbg("%s: invalid addr size %d for cpu mode %d",
|
||||
__func__, addrsize, cpu_mode);
|
||||
return -1;
|
||||
}
|
||||
glasize = 4U;
|
||||
/*
|
||||
* If the segment selector is loaded with a NULL selector
|
||||
* then the descriptor is unusable and attempting to use
|
||||
* it results in a #GP(0).
|
||||
*/
|
||||
if (SEG_DESC_UNUSABLE(desc->access)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The processor generates a #NP exception when a segment
|
||||
* register is loaded with a selector that points to a
|
||||
* descriptor that is not present. If this was the case then
|
||||
* it would have been checked before the VM-exit.
|
||||
*/
|
||||
if (SEG_DESC_PRESENT(desc->access) != 0) {
|
||||
/* TODO: Inject #NP */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The descriptor type must indicate a code/data segment. */
|
||||
type = SEG_DESC_TYPE(desc->access);
|
||||
if (type < 16 || type > 31) {
|
||||
/*TODO: Inject #GP */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((prot & PROT_READ) != 0U) {
|
||||
/* #GP on a read access to a exec-only code segment */
|
||||
if ((type & 0xAU) == 0x8U) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((prot & PROT_WRITE) != 0U) {
|
||||
/*
|
||||
* #GP on a write access to a code segment or a
|
||||
* read-only data segment.
|
||||
*/
|
||||
if ((type & 0x8U) != 0U) { /* code segment */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((type & 0xAU) == 0U) { /* read-only data seg */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 'desc->limit' is fully expanded taking granularity into
|
||||
* account.
|
||||
*/
|
||||
if ((type & 0xCU) == 0x4U) {
|
||||
/* expand-down data segment */
|
||||
low_limit = desc->limit + 1U;
|
||||
high_limit = SEG_DESC_DEF32(desc->access) ?
|
||||
0xffffffffU : 0xffffU;
|
||||
} else {
|
||||
/* code segment or expand-up data segment */
|
||||
low_limit = 0U;
|
||||
high_limit = desc->limit;
|
||||
}
|
||||
|
||||
while (length > 0U) {
|
||||
offset &= size2mask[addrsize];
|
||||
if (offset < low_limit || offset > high_limit) {
|
||||
return -1;
|
||||
}
|
||||
offset++;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In 64-bit mode all segments except %fs and %gs have a segment
|
||||
* base address of 0.
|
||||
*/
|
||||
if (cpu_mode == CPU_MODE_64BIT && seg != CPU_REG_FS &&
|
||||
seg != CPU_REG_GS) {
|
||||
segbase = 0UL;
|
||||
} else {
|
||||
segbase = desc->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Truncate 'firstoff' to the effective address size before adding
|
||||
* it to the segment base.
|
||||
*/
|
||||
firstoff &= size2mask[addrsize];
|
||||
*gla = (segbase + firstoff) & size2mask[glasize];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vie_init(struct instr_emul_vie *vie, struct vcpu *vcpu)
|
||||
static int vie_init(struct instr_emul_vie *vie, struct vcpu *vcpu)
|
||||
{
|
||||
uint64_t guest_rip_gva = vcpu_get_rip(vcpu);
|
||||
uint32_t inst_len = vcpu->arch_vcpu.inst_len;
|
||||
@ -1613,8 +1891,7 @@ vie_init(struct instr_emul_vie *vie, struct vcpu *vcpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vie_peek(struct instr_emul_vie *vie, uint8_t *x)
|
||||
static int vie_peek(struct instr_emul_vie *vie, uint8_t *x)
|
||||
{
|
||||
|
||||
if (vie->num_processed < vie->num_valid) {
|
||||
@ -1625,15 +1902,13 @@ vie_peek(struct instr_emul_vie *vie, uint8_t *x)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vie_advance(struct instr_emul_vie *vie)
|
||||
static void vie_advance(struct instr_emul_vie *vie)
|
||||
{
|
||||
|
||||
vie->num_processed++;
|
||||
}
|
||||
|
||||
static bool
|
||||
segment_override(uint8_t x, enum cpu_reg_name *seg)
|
||||
static bool segment_override(uint8_t x, enum cpu_reg_name *seg)
|
||||
{
|
||||
|
||||
switch (x) {
|
||||
@ -1661,8 +1936,8 @@ segment_override(uint8_t x, enum cpu_reg_name *seg)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_prefixes(struct instr_emul_vie *vie, enum vm_cpu_mode cpu_mode, bool cs_d)
|
||||
static int decode_prefixes(struct instr_emul_vie *vie,
|
||||
enum vm_cpu_mode cpu_mode, bool cs_d)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
@ -1733,8 +2008,7 @@ decode_prefixes(struct instr_emul_vie *vie, enum vm_cpu_mode cpu_mode, bool cs_d
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_two_byte_opcode(struct instr_emul_vie *vie)
|
||||
static int decode_two_byte_opcode(struct instr_emul_vie *vie)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
@ -1752,8 +2026,7 @@ decode_two_byte_opcode(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_opcode(struct instr_emul_vie *vie)
|
||||
static int decode_opcode(struct instr_emul_vie *vie)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
@ -1777,8 +2050,7 @@ decode_opcode(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_modrm(struct instr_emul_vie *vie, enum vm_cpu_mode cpu_mode)
|
||||
static int decode_modrm(struct instr_emul_vie *vie, enum vm_cpu_mode cpu_mode)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
@ -1832,8 +2104,7 @@ decode_modrm(struct instr_emul_vie *vie, enum vm_cpu_mode cpu_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_sib(struct instr_emul_vie *vie)
|
||||
static int decode_sib(struct instr_emul_vie *vie)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
@ -1900,8 +2171,7 @@ decode_sib(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_displacement(struct instr_emul_vie *vie)
|
||||
static int decode_displacement(struct instr_emul_vie *vie)
|
||||
{
|
||||
int n, i;
|
||||
uint8_t x;
|
||||
@ -1941,8 +2211,7 @@ decode_displacement(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_immediate(struct instr_emul_vie *vie)
|
||||
static int decode_immediate(struct instr_emul_vie *vie)
|
||||
{
|
||||
int i, n;
|
||||
uint8_t x;
|
||||
@ -2006,8 +2275,7 @@ decode_immediate(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
decode_moffset(struct instr_emul_vie *vie)
|
||||
static int decode_moffset(struct instr_emul_vie *vie)
|
||||
{
|
||||
uint8_t i, n, x;
|
||||
union {
|
||||
@ -2042,8 +2310,8 @@ decode_moffset(struct instr_emul_vie *vie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
local_decode_instruction(enum vm_cpu_mode cpu_mode, bool cs_d, struct instr_emul_vie *vie)
|
||||
static int local_decode_instruction(enum vm_cpu_mode cpu_mode,
|
||||
bool cs_d, struct instr_emul_vie *vie)
|
||||
{
|
||||
if (decode_prefixes(vie, cpu_mode, cs_d) != 0) {
|
||||
return -1;
|
||||
@ -2077,3 +2345,54 @@ local_decode_instruction(enum vm_cpu_mode cpu_mode, bool cs_d, struct instr_emul
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decode_instruction(struct vcpu *vcpu)
|
||||
{
|
||||
struct instr_emul_ctxt *emul_ctxt;
|
||||
uint32_t csar;
|
||||
int retval = 0;
|
||||
enum vm_cpu_mode cpu_mode;
|
||||
|
||||
emul_ctxt = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
|
||||
if (emul_ctxt == NULL) {
|
||||
pr_err("%s: Failed to get emul_ctxt", __func__);
|
||||
return -1;
|
||||
}
|
||||
emul_ctxt->vcpu = vcpu;
|
||||
|
||||
retval = vie_init(&emul_ctxt->vie, vcpu);
|
||||
if (retval < 0) {
|
||||
if (retval != -EFAULT) {
|
||||
pr_err("decode instruction failed @ 0x%016llx:",
|
||||
vcpu_get_rip(vcpu));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
csar = exec_vmread32(VMX_GUEST_CS_ATTR);
|
||||
get_guest_paging_info(vcpu, emul_ctxt, csar);
|
||||
cpu_mode = get_vcpu_mode(vcpu);
|
||||
|
||||
retval = local_decode_instruction(cpu_mode, SEG_DESC_DEF32(csar),
|
||||
&emul_ctxt->vie);
|
||||
|
||||
if (retval != 0) {
|
||||
pr_err("decode instruction failed @ 0x%016llx:",
|
||||
vcpu_get_rip(vcpu));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return emul_ctxt->vie.opsize;
|
||||
}
|
||||
|
||||
int emulate_instruction(struct vcpu *vcpu)
|
||||
{
|
||||
struct instr_emul_ctxt *ctxt = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
|
||||
|
||||
if (ctxt == NULL) {
|
||||
pr_err("%s: Failed to get instr_emul_ctxt", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return vmm_emulate_instruction(ctxt);
|
||||
}
|
||||
|
@ -27,48 +27,170 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _VMM_INSTRUCTION_EMUL_H_
|
||||
#define _VMM_INSTRUCTION_EMUL_H_
|
||||
#ifndef INSTR_EMUL_WRAPPER_H
|
||||
#define INSTR_EMUL_WRAPPER_H
|
||||
#include <cpu.h>
|
||||
|
||||
#include "instr_emul_wrapper.h"
|
||||
/**
|
||||
* Define the following MACRO to make range checking clear.
|
||||
*
|
||||
* CPU_REG_FIRST indicates the first register name, its value
|
||||
* is the same as CPU_REG_RAX;
|
||||
* CPU_REG_LAST indicates the last register name, its value is
|
||||
* the same as CPU_REG_GDTR;
|
||||
*
|
||||
* CPU_REG_GENERAL_FIRST indicates the first general register name,
|
||||
* its value is the same as CPU_REG_RAX;
|
||||
* CPU_REG_GENERAL_LAST indicates the last general register name,
|
||||
* its value is the same as CPU_REG_RDI;
|
||||
*
|
||||
* CPU_REG_NONGENERAL_FIRST indicates the first non general register
|
||||
* name, its value is the same as CPU_REG_CR0;
|
||||
* CPU_REG_NONGENERAL_LAST indicates the last non general register
|
||||
* name, its value is the same as CPU_REG_GDTR;
|
||||
*
|
||||
* CPU_REG_NATURAL_FIRST indicates the first register name that
|
||||
* is corresponds to the natural width field in VMCS, its value
|
||||
* is the same as CPU_REG_CR0;
|
||||
* CPU_REG_NATURAL_LAST indicates the last register name that
|
||||
* is corresponds to the natural width field in VMCS, its value
|
||||
* is the same as CPU_REG_RFLAGS;
|
||||
*
|
||||
* CPU_REG_64BIT_FIRST indicates the first register name that
|
||||
* is corresponds to the 64 bit field in VMCS, its value
|
||||
* is the same as CPU_REG_EFER;
|
||||
* CPU_REG_64BIT_LAST indicates the last register name that
|
||||
* is corresponds to the 64 bit field in VMCS, its value
|
||||
* is the same as CPU_REG_PDPTE3;
|
||||
*
|
||||
* CPU_REG_SEG_FIRST indicates the first segement register name,
|
||||
* its value is the same as CPU_REG_ES;
|
||||
* CPU_REG_SEG_FIRST indicates the last segement register name,
|
||||
* its value is the same as CPU_REG_GS
|
||||
*
|
||||
*/
|
||||
#define CPU_REG_FIRST CPU_REG_RAX
|
||||
#define CPU_REG_LAST CPU_REG_GDTR
|
||||
#define CPU_REG_GENERAL_FIRST CPU_REG_RAX
|
||||
#define CPU_REG_GENERAL_LAST CPU_REG_R15
|
||||
#define CPU_REG_NONGENERAL_FIRST CPU_REG_CR0
|
||||
#define CPU_REG_NONGENERAL_LAST CPU_REG_GDTR
|
||||
#define CPU_REG_NATURAL_FIRST CPU_REG_CR0
|
||||
#define CPU_REG_NATURAL_LAST CPU_REG_RFLAGS
|
||||
#define CPU_REG_64BIT_FIRST CPU_REG_EFER
|
||||
#define CPU_REG_64BIT_LAST CPU_REG_PDPTE3
|
||||
#define CPU_REG_SEG_FIRST CPU_REG_ES
|
||||
#define CPU_REG_SEG_LAST CPU_REG_GS
|
||||
|
||||
/* Emulate the decoded 'ctxt->vie' instruction. */
|
||||
int vmm_emulate_instruction(struct instr_emul_ctxt *ctxt);
|
||||
struct instr_emul_vie_op {
|
||||
uint8_t op_type; /* type of operation (e.g. MOV) */
|
||||
uint16_t op_flags;
|
||||
};
|
||||
|
||||
int vie_update_register(struct vcpu *vcpu, enum cpu_reg_name reg,
|
||||
uint64_t val_arg, uint8_t size);
|
||||
#define VIE_INST_SIZE 15U
|
||||
struct instr_emul_vie {
|
||||
uint8_t inst[VIE_INST_SIZE]; /* instruction bytes */
|
||||
uint8_t num_valid; /* size of the instruction */
|
||||
uint8_t num_processed;
|
||||
|
||||
uint8_t addrsize:4, opsize:4; /* address and operand sizes */
|
||||
uint8_t rex_w:1, /* REX prefix */
|
||||
rex_r:1,
|
||||
rex_x:1,
|
||||
rex_b:1,
|
||||
rex_present:1,
|
||||
repz_present:1, /* REP/REPE/REPZ prefix */
|
||||
repnz_present:1, /* REPNE/REPNZ prefix */
|
||||
opsize_override:1, /* Operand size override */
|
||||
addrsize_override:1, /* Address size override */
|
||||
seg_override:1; /* Segment override */
|
||||
|
||||
uint8_t mod:2, /* ModRM byte */
|
||||
reg:4,
|
||||
rm:4;
|
||||
|
||||
uint8_t ss:2, /* SIB byte */
|
||||
index:4,
|
||||
base:4;
|
||||
|
||||
uint8_t disp_bytes;
|
||||
uint8_t imm_bytes;
|
||||
|
||||
uint8_t scale;
|
||||
enum cpu_reg_name base_register; /* CPU_REG_xyz */
|
||||
enum cpu_reg_name index_register; /* CPU_REG_xyz */
|
||||
enum cpu_reg_name segment_register; /* CPU_REG_xyz */
|
||||
|
||||
int64_t displacement; /* optional addr displacement */
|
||||
int64_t immediate; /* optional immediate operand */
|
||||
|
||||
uint8_t decoded; /* set to 1 if successfully decoded */
|
||||
|
||||
uint8_t opcode;
|
||||
struct instr_emul_vie_op op; /* opcode description */
|
||||
};
|
||||
|
||||
#define PSL_C 0x00000001U /* carry bit */
|
||||
#define PSL_PF 0x00000004U /* parity bit */
|
||||
#define PSL_AF 0x00000010U /* bcd carry bit */
|
||||
#define PSL_Z 0x00000040U /* zero bit */
|
||||
#define PSL_N 0x00000080U /* negative bit */
|
||||
#define PSL_T 0x00000100U /* trace enable bit */
|
||||
#define PSL_I 0x00000200U /* interrupt enable bit */
|
||||
#define PSL_D 0x00000400U /* string instruction direction bit */
|
||||
#define PSL_V 0x00000800U /* overflow bit */
|
||||
#define PSL_IOPL 0x00003000U /* i/o privilege level */
|
||||
#define PSL_NT 0x00004000U /* nested task bit */
|
||||
#define PSL_RF 0x00010000U /* resume flag bit */
|
||||
#define PSL_VM 0x00020000U /* virtual 8086 mode bit */
|
||||
#define PSL_AC 0x00040000U /* alignment checking */
|
||||
#define PSL_VIF 0x00080000U /* virtual interrupt enable */
|
||||
#define PSL_VIP 0x00100000U /* virtual interrupt pending */
|
||||
#define PSL_ID 0x00200000U /* identification bit */
|
||||
|
||||
/*
|
||||
* Returns 1 if an alignment check exception should be injected and 0 otherwise.
|
||||
* The 'access' field has the format specified in Table 21-2 of the Intel
|
||||
* Architecture Manual vol 3b.
|
||||
*
|
||||
* XXX The contents of the 'access' field are architecturally defined except
|
||||
* bit 16 - Segment Unusable.
|
||||
*/
|
||||
int vie_alignment_check(uint8_t cpl, uint8_t size, uint64_t cr0,
|
||||
uint64_t rflags, uint64_t gla);
|
||||
struct seg_desc {
|
||||
uint64_t base;
|
||||
uint32_t limit;
|
||||
uint32_t access;
|
||||
};
|
||||
|
||||
/* Returns 1 if the 'gla' is not canonical and 0 otherwise. */
|
||||
int vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla);
|
||||
|
||||
int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc, uint64_t offset_arg, uint8_t length_arg,
|
||||
uint8_t addrsize, uint32_t prot, uint64_t *gla);
|
||||
|
||||
int vie_init(struct instr_emul_vie *vie, struct vcpu *vcpu);
|
||||
|
||||
/*
|
||||
* Decode the instruction fetched into 'vie' so it can be emulated.
|
||||
*
|
||||
* 'gla' is the guest linear address provided by the hardware assist
|
||||
* that caused the nested page table fault. It is used to verify that
|
||||
* the software instruction decoding is in agreement with the hardware.
|
||||
*
|
||||
* Some hardware assists do not provide the 'gla' to the hypervisor.
|
||||
* To skip the 'gla' verification for this or any other reason pass
|
||||
* in VIE_INVALID_GLA instead.
|
||||
* Protections are chosen from these bits, or-ed together
|
||||
*/
|
||||
#define VIE_INVALID_GLA (1UL << 63) /* a non-canonical address */
|
||||
int
|
||||
local_decode_instruction(enum vm_cpu_mode cpu_mode, bool cs_d, struct instr_emul_vie *vie);
|
||||
#define PROT_NONE 0x00U /* no permissions */
|
||||
#define PROT_READ 0x01U /* pages can be read */
|
||||
#define PROT_WRITE 0x02U /* pages can be written */
|
||||
#define PROT_EXEC 0x04U /* pages can be executed */
|
||||
|
||||
#define SEG_DESC_TYPE(access) ((access) & 0x001fU)
|
||||
#define SEG_DESC_DPL(access) (((access) >> 5) & 0x3U)
|
||||
#define SEG_DESC_PRESENT(access) (((access) & 0x0080U) != 0U)
|
||||
#define SEG_DESC_DEF32(access) (((access) & 0x4000U) != 0U)
|
||||
#define SEG_DESC_GRANULARITY(access) (((access) & 0x8000U) != 0U)
|
||||
#define SEG_DESC_UNUSABLE(access) (((access) & 0x10000U) != 0U)
|
||||
|
||||
struct vm_guest_paging {
|
||||
uint64_t cr3;
|
||||
uint8_t cpl;
|
||||
enum vm_cpu_mode cpu_mode;
|
||||
enum vm_paging_mode paging_mode;
|
||||
};
|
||||
|
||||
struct instr_emul_ctxt {
|
||||
struct instr_emul_vie vie;
|
||||
struct vm_guest_paging paging;
|
||||
struct vcpu *vcpu;
|
||||
};
|
||||
|
||||
int emulate_instruction(struct vcpu *vcpu);
|
||||
int decode_instruction(struct vcpu *vcpu);
|
||||
|
||||
#endif /* _VMM_INSTRUCTION_EMUL_H_ */
|
||||
#endif
|
||||
|
@ -1,355 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#include "instr_emul_wrapper.h"
|
||||
#include "instr_emul.h"
|
||||
|
||||
#define VMX_INVALID_VMCS_FIELD 0xffffffffU
|
||||
|
||||
static int
|
||||
encode_vmcs_seg_desc(enum cpu_reg_name seg,
|
||||
uint32_t *base, uint32_t *lim, uint32_t *acc);
|
||||
|
||||
static uint32_t
|
||||
get_vmcs_field(enum cpu_reg_name ident);
|
||||
|
||||
static bool
|
||||
is_segment_register(enum cpu_reg_name reg);
|
||||
|
||||
static bool
|
||||
is_descriptor_table(enum cpu_reg_name reg);
|
||||
|
||||
int vm_get_register(struct vcpu *vcpu, enum cpu_reg_name reg, uint64_t *retval)
|
||||
{
|
||||
if (vcpu == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg > CPU_REG_LAST) || (reg < CPU_REG_FIRST)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg >= CPU_REG_GENERAL_FIRST) && (reg <= CPU_REG_GENERAL_LAST)) {
|
||||
*retval = vcpu_get_gpreg(vcpu, reg);
|
||||
} else if ((reg >= CPU_REG_NONGENERAL_FIRST) &&
|
||||
(reg <= CPU_REG_NONGENERAL_LAST)) {
|
||||
uint32_t field = get_vmcs_field(reg);
|
||||
|
||||
if (field != VMX_INVALID_VMCS_FIELD) {
|
||||
if (reg <= CPU_REG_NATURAL_LAST) {
|
||||
*retval = exec_vmread(field);
|
||||
} else if (reg <= CPU_REG_64BIT_LAST) {
|
||||
*retval = exec_vmread64(field);
|
||||
} else {
|
||||
*retval = (uint64_t)exec_vmread16(field);
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vm_set_register(struct vcpu *vcpu, enum cpu_reg_name reg, uint64_t val)
|
||||
{
|
||||
if (vcpu == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg > CPU_REG_LAST) || (reg < CPU_REG_FIRST)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((reg >= CPU_REG_GENERAL_FIRST) && (reg <= CPU_REG_GENERAL_LAST)) {
|
||||
vcpu_set_gpreg(vcpu, reg, val);
|
||||
} else if ((reg >= CPU_REG_NONGENERAL_FIRST) &&
|
||||
(reg <= CPU_REG_NONGENERAL_LAST)) {
|
||||
uint32_t field = get_vmcs_field(reg);
|
||||
|
||||
if (field != VMX_INVALID_VMCS_FIELD) {
|
||||
if (reg <= CPU_REG_NATURAL_LAST) {
|
||||
exec_vmwrite(field, val);
|
||||
} else if (reg <= CPU_REG_64BIT_LAST) {
|
||||
exec_vmwrite64(field, val);
|
||||
} else {
|
||||
exec_vmwrite16(field, (uint16_t)val);
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vm_set_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc)
|
||||
{
|
||||
int error;
|
||||
uint32_t base, limit, access;
|
||||
|
||||
if ((vcpu == NULL) || (desc == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_segment_register(seg) && !is_descriptor_table(seg)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
|
||||
if ((error != 0) || (access == 0xffffffffU)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
exec_vmwrite(base, desc->base);
|
||||
exec_vmwrite32(limit, desc->limit);
|
||||
exec_vmwrite32(access, desc->access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vm_get_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc)
|
||||
{
|
||||
int error;
|
||||
uint32_t base, limit, access;
|
||||
|
||||
if ((vcpu == NULL) || (desc == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_segment_register(seg) && !is_descriptor_table(seg)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
|
||||
if ((error != 0) || (access == 0xffffffffU)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc->base = exec_vmread(base);
|
||||
desc->limit = exec_vmread32(limit);
|
||||
desc->access = exec_vmread32(access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_descriptor_table(enum cpu_reg_name reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CPU_REG_IDTR:
|
||||
case CPU_REG_GDTR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_segment_register(enum cpu_reg_name reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CPU_REG_ES:
|
||||
case CPU_REG_CS:
|
||||
case CPU_REG_SS:
|
||||
case CPU_REG_DS:
|
||||
case CPU_REG_FS:
|
||||
case CPU_REG_GS:
|
||||
case CPU_REG_TR:
|
||||
case CPU_REG_LDTR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
encode_vmcs_seg_desc(enum cpu_reg_name seg,
|
||||
uint32_t *base, uint32_t *lim, uint32_t *acc)
|
||||
{
|
||||
switch (seg) {
|
||||
case CPU_REG_ES:
|
||||
*base = VMX_GUEST_ES_BASE;
|
||||
*lim = VMX_GUEST_ES_LIMIT;
|
||||
*acc = VMX_GUEST_ES_ATTR;
|
||||
break;
|
||||
case CPU_REG_CS:
|
||||
*base = VMX_GUEST_CS_BASE;
|
||||
*lim = VMX_GUEST_CS_LIMIT;
|
||||
*acc = VMX_GUEST_CS_ATTR;
|
||||
break;
|
||||
case CPU_REG_SS:
|
||||
*base = VMX_GUEST_SS_BASE;
|
||||
*lim = VMX_GUEST_SS_LIMIT;
|
||||
*acc = VMX_GUEST_SS_ATTR;
|
||||
break;
|
||||
case CPU_REG_DS:
|
||||
*base = VMX_GUEST_DS_BASE;
|
||||
*lim = VMX_GUEST_DS_LIMIT;
|
||||
*acc = VMX_GUEST_DS_ATTR;
|
||||
break;
|
||||
case CPU_REG_FS:
|
||||
*base = VMX_GUEST_FS_BASE;
|
||||
*lim = VMX_GUEST_FS_LIMIT;
|
||||
*acc = VMX_GUEST_FS_ATTR;
|
||||
break;
|
||||
case CPU_REG_GS:
|
||||
*base = VMX_GUEST_GS_BASE;
|
||||
*lim = VMX_GUEST_GS_LIMIT;
|
||||
*acc = VMX_GUEST_GS_ATTR;
|
||||
break;
|
||||
case CPU_REG_TR:
|
||||
*base = VMX_GUEST_TR_BASE;
|
||||
*lim = VMX_GUEST_TR_LIMIT;
|
||||
*acc = VMX_GUEST_TR_ATTR;
|
||||
break;
|
||||
case CPU_REG_LDTR:
|
||||
*base = VMX_GUEST_LDTR_BASE;
|
||||
*lim = VMX_GUEST_LDTR_LIMIT;
|
||||
*acc = VMX_GUEST_LDTR_ATTR;
|
||||
break;
|
||||
case CPU_REG_IDTR:
|
||||
*base = VMX_GUEST_IDTR_BASE;
|
||||
*lim = VMX_GUEST_IDTR_LIMIT;
|
||||
*acc = 0xffffffffU;
|
||||
break;
|
||||
case CPU_REG_GDTR:
|
||||
*base = VMX_GUEST_GDTR_BASE;
|
||||
*lim = VMX_GUEST_GDTR_LIMIT;
|
||||
*acc = 0xffffffffU;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
*
|
||||
*Description:
|
||||
*This local function is to covert register names into
|
||||
*the corresponding field index MACROs in VMCS.
|
||||
*
|
||||
*Post Condition:
|
||||
*In the non-general register names group (CPU_REG_CR0~CPU_REG_GDTR),
|
||||
*for register names CPU_REG_CR2, CPU_REG_IDTR and CPU_REG_GDTR,
|
||||
*this function returns VMX_INVALID_VMCS_FIELD;
|
||||
*for other register names, it returns correspoding field index MACROs
|
||||
*in VMCS.
|
||||
*
|
||||
**/
|
||||
static uint32_t get_vmcs_field(enum cpu_reg_name ident)
|
||||
{
|
||||
switch (ident) {
|
||||
case CPU_REG_CR0:
|
||||
return VMX_GUEST_CR0;
|
||||
case CPU_REG_CR3:
|
||||
return VMX_GUEST_CR3;
|
||||
case CPU_REG_CR4:
|
||||
return VMX_GUEST_CR4;
|
||||
case CPU_REG_DR7:
|
||||
return VMX_GUEST_DR7;
|
||||
case CPU_REG_RSP:
|
||||
return VMX_GUEST_RSP;
|
||||
case CPU_REG_RIP:
|
||||
return VMX_GUEST_RIP;
|
||||
case CPU_REG_RFLAGS:
|
||||
return VMX_GUEST_RFLAGS;
|
||||
case CPU_REG_ES:
|
||||
return VMX_GUEST_ES_SEL;
|
||||
case CPU_REG_CS:
|
||||
return VMX_GUEST_CS_SEL;
|
||||
case CPU_REG_SS:
|
||||
return VMX_GUEST_SS_SEL;
|
||||
case CPU_REG_DS:
|
||||
return VMX_GUEST_DS_SEL;
|
||||
case CPU_REG_FS:
|
||||
return VMX_GUEST_FS_SEL;
|
||||
case CPU_REG_GS:
|
||||
return VMX_GUEST_GS_SEL;
|
||||
case CPU_REG_TR:
|
||||
return VMX_GUEST_TR_SEL;
|
||||
case CPU_REG_LDTR:
|
||||
return VMX_GUEST_LDTR_SEL;
|
||||
case CPU_REG_EFER:
|
||||
return VMX_GUEST_IA32_EFER_FULL;
|
||||
case CPU_REG_PDPTE0:
|
||||
return VMX_GUEST_PDPTE0_FULL;
|
||||
case CPU_REG_PDPTE1:
|
||||
return VMX_GUEST_PDPTE1_FULL;
|
||||
case CPU_REG_PDPTE2:
|
||||
return VMX_GUEST_PDPTE2_FULL;
|
||||
case CPU_REG_PDPTE3:
|
||||
return VMX_GUEST_PDPTE3_FULL;
|
||||
default:
|
||||
return VMX_INVALID_VMCS_FIELD;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_guest_paging_info(struct vcpu *vcpu, struct instr_emul_ctxt *emul_ctxt,
|
||||
uint32_t csar)
|
||||
{
|
||||
uint8_t cpl;
|
||||
|
||||
cpl = (uint8_t)((csar >> 5) & 3U);
|
||||
emul_ctxt->paging.cr3 = exec_vmread(VMX_GUEST_CR3);
|
||||
emul_ctxt->paging.cpl = cpl;
|
||||
emul_ctxt->paging.cpu_mode = get_vcpu_mode(vcpu);
|
||||
emul_ctxt->paging.paging_mode = get_vcpu_paging_mode(vcpu);
|
||||
}
|
||||
|
||||
int decode_instruction(struct vcpu *vcpu)
|
||||
{
|
||||
struct instr_emul_ctxt *emul_ctxt;
|
||||
uint32_t csar;
|
||||
int retval = 0;
|
||||
enum vm_cpu_mode cpu_mode;
|
||||
|
||||
emul_ctxt = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
|
||||
if (emul_ctxt == NULL) {
|
||||
pr_err("%s: Failed to get emul_ctxt", __func__);
|
||||
return -1;
|
||||
}
|
||||
emul_ctxt->vcpu = vcpu;
|
||||
|
||||
retval = vie_init(&emul_ctxt->vie, vcpu);
|
||||
if (retval < 0) {
|
||||
if (retval != -EFAULT) {
|
||||
pr_err("decode instruction failed @ 0x%016llx:",
|
||||
vcpu_get_rip(vcpu));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
csar = exec_vmread32(VMX_GUEST_CS_ATTR);
|
||||
get_guest_paging_info(vcpu, emul_ctxt, csar);
|
||||
cpu_mode = get_vcpu_mode(vcpu);
|
||||
|
||||
retval = local_decode_instruction(cpu_mode, SEG_DESC_DEF32(csar),
|
||||
&emul_ctxt->vie);
|
||||
|
||||
if (retval != 0) {
|
||||
pr_err("decode instruction failed @ 0x%016llx:",
|
||||
vcpu_get_rip(vcpu));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return emul_ctxt->vie.opsize;
|
||||
}
|
||||
|
||||
int emulate_instruction(struct vcpu *vcpu)
|
||||
{
|
||||
struct instr_emul_ctxt *ctxt = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
|
||||
|
||||
if (ctxt == NULL) {
|
||||
pr_err("%s: Failed to get instr_emul_ctxt", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return vmm_emulate_instruction(ctxt);
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 NetApp, Inc.
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef INSTR_EMUL_WRAPPER_H
|
||||
#define INSTR_EMUL_WRAPPER_H
|
||||
#include <cpu.h>
|
||||
|
||||
/**
|
||||
* Define the following MACRO to make range checking clear.
|
||||
*
|
||||
* CPU_REG_FIRST indicates the first register name, its value
|
||||
* is the same as CPU_REG_RAX;
|
||||
* CPU_REG_LAST indicates the last register name, its value is
|
||||
* the same as CPU_REG_GDTR;
|
||||
*
|
||||
* CPU_REG_GENERAL_FIRST indicates the first general register name,
|
||||
* its value is the same as CPU_REG_RAX;
|
||||
* CPU_REG_GENERAL_LAST indicates the last general register name,
|
||||
* its value is the same as CPU_REG_RDI;
|
||||
*
|
||||
* CPU_REG_NONGENERAL_FIRST indicates the first non general register
|
||||
* name, its value is the same as CPU_REG_CR0;
|
||||
* CPU_REG_NONGENERAL_LAST indicates the last non general register
|
||||
* name, its value is the same as CPU_REG_GDTR;
|
||||
*
|
||||
* CPU_REG_NATURAL_FIRST indicates the first register name that
|
||||
* is corresponds to the natural width field in VMCS, its value
|
||||
* is the same as CPU_REG_CR0;
|
||||
* CPU_REG_NATURAL_LAST indicates the last register name that
|
||||
* is corresponds to the natural width field in VMCS, its value
|
||||
* is the same as CPU_REG_RFLAGS;
|
||||
*
|
||||
* CPU_REG_64BIT_FIRST indicates the first register name that
|
||||
* is corresponds to the 64 bit field in VMCS, its value
|
||||
* is the same as CPU_REG_EFER;
|
||||
* CPU_REG_64BIT_LAST indicates the last register name that
|
||||
* is corresponds to the 64 bit field in VMCS, its value
|
||||
* is the same as CPU_REG_PDPTE3;
|
||||
*
|
||||
* CPU_REG_SEG_FIRST indicates the first segement register name,
|
||||
* its value is the same as CPU_REG_ES;
|
||||
* CPU_REG_SEG_FIRST indicates the last segement register name,
|
||||
* its value is the same as CPU_REG_GS
|
||||
*
|
||||
*/
|
||||
#define CPU_REG_FIRST CPU_REG_RAX
|
||||
#define CPU_REG_LAST CPU_REG_GDTR
|
||||
#define CPU_REG_GENERAL_FIRST CPU_REG_RAX
|
||||
#define CPU_REG_GENERAL_LAST CPU_REG_R15
|
||||
#define CPU_REG_NONGENERAL_FIRST CPU_REG_CR0
|
||||
#define CPU_REG_NONGENERAL_LAST CPU_REG_GDTR
|
||||
#define CPU_REG_NATURAL_FIRST CPU_REG_CR0
|
||||
#define CPU_REG_NATURAL_LAST CPU_REG_RFLAGS
|
||||
#define CPU_REG_64BIT_FIRST CPU_REG_EFER
|
||||
#define CPU_REG_64BIT_LAST CPU_REG_PDPTE3
|
||||
#define CPU_REG_SEG_FIRST CPU_REG_ES
|
||||
#define CPU_REG_SEG_LAST CPU_REG_GS
|
||||
|
||||
struct instr_emul_vie_op {
|
||||
uint8_t op_type; /* type of operation (e.g. MOV) */
|
||||
uint16_t op_flags;
|
||||
};
|
||||
|
||||
#define VIE_INST_SIZE 15U
|
||||
struct instr_emul_vie {
|
||||
uint8_t inst[VIE_INST_SIZE]; /* instruction bytes */
|
||||
uint8_t num_valid; /* size of the instruction */
|
||||
uint8_t num_processed;
|
||||
|
||||
uint8_t addrsize:4, opsize:4; /* address and operand sizes */
|
||||
uint8_t rex_w:1, /* REX prefix */
|
||||
rex_r:1,
|
||||
rex_x:1,
|
||||
rex_b:1,
|
||||
rex_present:1,
|
||||
repz_present:1, /* REP/REPE/REPZ prefix */
|
||||
repnz_present:1, /* REPNE/REPNZ prefix */
|
||||
opsize_override:1, /* Operand size override */
|
||||
addrsize_override:1, /* Address size override */
|
||||
seg_override:1; /* Segment override */
|
||||
|
||||
uint8_t mod:2, /* ModRM byte */
|
||||
reg:4,
|
||||
rm:4;
|
||||
|
||||
uint8_t ss:2, /* SIB byte */
|
||||
index:4,
|
||||
base:4;
|
||||
|
||||
uint8_t disp_bytes;
|
||||
uint8_t imm_bytes;
|
||||
|
||||
uint8_t scale;
|
||||
enum cpu_reg_name base_register; /* CPU_REG_xyz */
|
||||
enum cpu_reg_name index_register; /* CPU_REG_xyz */
|
||||
enum cpu_reg_name segment_register; /* CPU_REG_xyz */
|
||||
|
||||
int64_t displacement; /* optional addr displacement */
|
||||
int64_t immediate; /* optional immediate operand */
|
||||
|
||||
uint8_t decoded; /* set to 1 if successfully decoded */
|
||||
|
||||
uint8_t opcode;
|
||||
struct instr_emul_vie_op op; /* opcode description */
|
||||
};
|
||||
|
||||
#define PSL_C 0x00000001U /* carry bit */
|
||||
#define PSL_PF 0x00000004U /* parity bit */
|
||||
#define PSL_AF 0x00000010U /* bcd carry bit */
|
||||
#define PSL_Z 0x00000040U /* zero bit */
|
||||
#define PSL_N 0x00000080U /* negative bit */
|
||||
#define PSL_T 0x00000100U /* trace enable bit */
|
||||
#define PSL_I 0x00000200U /* interrupt enable bit */
|
||||
#define PSL_D 0x00000400U /* string instruction direction bit */
|
||||
#define PSL_V 0x00000800U /* overflow bit */
|
||||
#define PSL_IOPL 0x00003000U /* i/o privilege level */
|
||||
#define PSL_NT 0x00004000U /* nested task bit */
|
||||
#define PSL_RF 0x00010000U /* resume flag bit */
|
||||
#define PSL_VM 0x00020000U /* virtual 8086 mode bit */
|
||||
#define PSL_AC 0x00040000U /* alignment checking */
|
||||
#define PSL_VIF 0x00080000U /* virtual interrupt enable */
|
||||
#define PSL_VIP 0x00100000U /* virtual interrupt pending */
|
||||
#define PSL_ID 0x00200000U /* identification bit */
|
||||
|
||||
/*
|
||||
* The 'access' field has the format specified in Table 21-2 of the Intel
|
||||
* Architecture Manual vol 3b.
|
||||
*
|
||||
* XXX The contents of the 'access' field are architecturally defined except
|
||||
* bit 16 - Segment Unusable.
|
||||
*/
|
||||
struct seg_desc {
|
||||
uint64_t base;
|
||||
uint32_t limit;
|
||||
uint32_t access;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Protections are chosen from these bits, or-ed together
|
||||
*/
|
||||
#define PROT_NONE 0x00U /* no permissions */
|
||||
#define PROT_READ 0x01U /* pages can be read */
|
||||
#define PROT_WRITE 0x02U /* pages can be written */
|
||||
#define PROT_EXEC 0x04U /* pages can be executed */
|
||||
|
||||
#define SEG_DESC_TYPE(access) ((access) & 0x001fU)
|
||||
#define SEG_DESC_DPL(access) (((access) >> 5) & 0x3U)
|
||||
#define SEG_DESC_PRESENT(access) (((access) & 0x0080U) != 0U)
|
||||
#define SEG_DESC_DEF32(access) (((access) & 0x4000U) != 0U)
|
||||
#define SEG_DESC_GRANULARITY(access) (((access) & 0x8000U) != 0U)
|
||||
#define SEG_DESC_UNUSABLE(access) (((access) & 0x10000U) != 0U)
|
||||
|
||||
struct vm_guest_paging {
|
||||
uint64_t cr3;
|
||||
uint8_t cpl;
|
||||
enum vm_cpu_mode cpu_mode;
|
||||
enum vm_paging_mode paging_mode;
|
||||
};
|
||||
|
||||
struct instr_emul_ctxt {
|
||||
struct instr_emul_vie vie;
|
||||
struct vm_guest_paging paging;
|
||||
struct vcpu *vcpu;
|
||||
};
|
||||
|
||||
int vm_get_register(struct vcpu *vcpu, enum cpu_reg_name reg, uint64_t *retval);
|
||||
int vm_set_register(struct vcpu *vcpu, enum cpu_reg_name reg, uint64_t val);
|
||||
int vm_get_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc);
|
||||
int vm_set_seg_desc(struct vcpu *vcpu, enum cpu_reg_name seg,
|
||||
struct seg_desc *desc);
|
||||
#endif
|
@ -31,7 +31,6 @@
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#include "instr_emul_wrapper.h"
|
||||
#include "instr_emul.h"
|
||||
|
||||
#include "vlapic_priv.h"
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#include "guest/instr_emul_wrapper.h"
|
||||
#include "guest/instr_emul.h"
|
||||
|
||||
static void complete_ioreq(struct vhm_request *vhm_req)
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <gdt.h>
|
||||
#include <timer.h>
|
||||
#include <logmsg.h>
|
||||
#include "arch/x86/guest/instr_emul_wrapper.h"
|
||||
#include "arch/x86/guest/instr_emul.h"
|
||||
|
||||
struct per_cpu_region {
|
||||
uint64_t *sbuf[ACRN_SBUF_ID_MAX];
|
||||
|
Loading…
Reference in New Issue
Block a user