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:
Kaige Fu 2018-08-06 08:40:10 +08:00 committed by lijinxia
parent ce79d3aa24
commit f03ae8d09c
9 changed files with 671 additions and 788 deletions

View File

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

View File

@ -6,7 +6,6 @@
#include <hypervisor.h>
#include "guest/instr_emul_wrapper.h"
#include "guest/instr_emul.h"
#define ACRN_DBG_EPT 6U

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,6 @@
#include <hypervisor.h>
#include "instr_emul_wrapper.h"
#include "instr_emul.h"
#include "vlapic_priv.h"

View File

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

View File

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