Module Name: src
Committed By: maxv
Date: Sun Jan 13 10:43:23 UTC 2019
Modified Files:
src/lib/libnvmm: libnvmm_x86.c
Log Message:
Handle more corner cases, clean up a little, and add a set of instructions
in Group1.
To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/lib/libnvmm/libnvmm_x86.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/lib/libnvmm/libnvmm_x86.c
diff -u src/lib/libnvmm/libnvmm_x86.c:1.14 src/lib/libnvmm/libnvmm_x86.c:1.15
--- src/lib/libnvmm/libnvmm_x86.c:1.14 Tue Jan 8 07:34:22 2019
+++ src/lib/libnvmm/libnvmm_x86.c Sun Jan 13 10:43:22 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: libnvmm_x86.c,v 1.14 2019/01/08 07:34:22 maxv Exp $ */
+/* $NetBSD: libnvmm_x86.c,v 1.15 2019/01/13 10:43:22 maxv Exp $ */
/*
* Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -77,13 +77,15 @@ nvmm_vcpu_dump(struct nvmm_machine *mach
printf("| -> RAX=%p\n", (void *)state.gprs[NVMM_X64_GPR_RAX]);
printf("| -> RBX=%p\n", (void *)state.gprs[NVMM_X64_GPR_RBX]);
printf("| -> RCX=%p\n", (void *)state.gprs[NVMM_X64_GPR_RCX]);
+ printf("| -> RFLAGS=%p\n", (void *)state.gprs[NVMM_X64_GPR_RFLAGS]);
for (i = 0; i < NVMM_X64_NSEG; i++) {
- printf("| -> %s: sel=0x%lx base=%p, limit=%p, P=%d, D=%d\n",
+ printf("| -> %s: sel=0x%lx base=%p, limit=%p, P=%d, D=%d L=%d\n",
segnames[i],
state.segs[i].selector,
(void *)state.segs[i].base,
(void *)state.segs[i].limit,
- state.segs[i].attrib.p, state.segs[i].attrib.def32);
+ state.segs[i].attrib.p, state.segs[i].attrib.def32,
+ state.segs[i].attrib.lng);
}
printf("| -> MSR_EFER=%p\n", (void *)state.msrs[NVMM_X64_MSR_EFER]);
printf("| -> CR0=%p\n", (void *)state.crs[NVMM_X64_CR_CR0]);
@@ -392,7 +394,7 @@ x86_gva_to_gpa(struct nvmm_machine *mach
gva &= ~PAGE_MASK;
is_pae = (state->crs[NVMM_X64_CR_CR4] & CR4_PAE) != 0;
- is_lng = (state->msrs[NVMM_X64_MSR_EFER] & EFER_LME) != 0;
+ is_lng = (state->msrs[NVMM_X64_MSR_EFER] & EFER_LMA) != 0;
has_pse = (state->crs[NVMM_X64_CR_CR4] & CR4_PSE) != 0;
cr3 = state->crs[NVMM_X64_CR_CR3];
@@ -437,6 +439,12 @@ nvmm_gva_to_gpa(struct nvmm_machine *mac
/* -------------------------------------------------------------------------- */
static inline bool
+is_long_mode(struct nvmm_x64_state *state)
+{
+ return (state->msrs[NVMM_X64_MSR_EFER] & EFER_LMA) != 0;
+}
+
+static inline bool
is_64bit(struct nvmm_x64_state *state)
{
return (state->segs[NVMM_X64_SEG_CS].attrib.lng != 0);
@@ -456,14 +464,8 @@ is_16bit(struct nvmm_x64_state *state)
(state->segs[NVMM_X64_SEG_CS].attrib.def32 == 0);
}
-static inline bool
-is_long_mode(struct nvmm_x64_state *state)
-{
- return (state->msrs[NVMM_X64_MSR_EFER] & EFER_LME) != 0;
-}
-
static int
-segment_apply(struct nvmm_x64_state_seg *seg, gvaddr_t *gva, size_t size)
+segment_check(struct nvmm_x64_state_seg *seg, gvaddr_t gva, size_t size)
{
uint64_t limit;
@@ -480,11 +482,10 @@ segment_apply(struct nvmm_x64_state_seg
limit *= PAGE_SIZE;
}
- if (__predict_false(*gva + size > limit)) {
+ if (__predict_false(gva + size > limit)) {
goto error;
}
- *gva += seg->base;
return 0;
error:
@@ -492,17 +493,25 @@ error:
return -1;
}
-static uint64_t
-mask_from_adsize(size_t adsize)
+static inline void
+segment_apply(struct nvmm_x64_state_seg *seg, gvaddr_t *gva)
{
- switch (adsize) {
- case 8:
- return 0xFFFFFFFFFFFFFFFF;
- case 4:
- return 0x00000000FFFFFFFF;
+ *gva += seg->base;
+}
+
+static inline uint64_t
+size_to_mask(size_t size)
+{
+ switch (size) {
+ case 1:
+ return 0x00000000000000FF;
case 2:
- default: /* impossible */
return 0x000000000000FFFF;
+ case 4:
+ return 0x00000000FFFFFFFF;
+ case 8:
+ default:
+ return 0xFFFFFFFFFFFFFFFF;
}
}
@@ -511,7 +520,7 @@ rep_get_cnt(struct nvmm_x64_state *state
{
uint64_t mask, cnt;
- mask = mask_from_adsize(adsize);
+ mask = size_to_mask(adsize);
cnt = state->gprs[NVMM_X64_GPR_RCX] & mask;
return cnt;
@@ -522,28 +531,12 @@ rep_set_cnt(struct nvmm_x64_state *state
{
uint64_t mask;
- mask = mask_from_adsize(adsize);
+ /* XXX: should we zero-extend? */
+ mask = size_to_mask(adsize);
state->gprs[NVMM_X64_GPR_RCX] &= ~mask;
state->gprs[NVMM_X64_GPR_RCX] |= cnt;
}
-static uint64_t
-rep_dec_apply(struct nvmm_x64_state *state, size_t adsize)
-{
- uint64_t mask, cnt;
-
- mask = mask_from_adsize(adsize);
-
- cnt = state->gprs[NVMM_X64_GPR_RCX] & mask;
- cnt -= 1;
- cnt &= mask;
-
- state->gprs[NVMM_X64_GPR_RCX] &= ~mask;
- state->gprs[NVMM_X64_GPR_RCX] |= cnt;
-
- return cnt;
-}
-
static int
read_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state,
gvaddr_t gva, uint8_t *data, size_t size)
@@ -693,7 +686,7 @@ nvmm_assist_io(struct nvmm_machine *mach
uint64_t cnt = 0; /* GCC */
uint8_t iobuf[8];
int iocnt = 1;
- gvaddr_t gva;
+ gvaddr_t gva = 0; /* GCC */
int reg = 0; /* GCC */
int ret, seg;
bool psld = false;
@@ -717,7 +710,8 @@ nvmm_assist_io(struct nvmm_machine *mach
if (exit->u.io.rep) {
cnt = rep_get_cnt(&state, exit->u.io.address_size);
if (__predict_false(cnt == 0)) {
- return 0;
+ state.gprs[NVMM_X64_GPR_RIP] = exit->u.io.npc;
+ goto out;
}
}
@@ -736,24 +730,29 @@ nvmm_assist_io(struct nvmm_machine *mach
}
gva = state.gprs[reg];
- gva &= mask_from_adsize(exit->u.io.address_size);
+ gva &= size_to_mask(exit->u.io.address_size);
- if (!is_long_mode(&state)) {
- if (exit->u.io.seg != -1) {
- seg = exit->u.io.seg;
+ if (exit->u.io.seg != -1) {
+ seg = exit->u.io.seg;
+ } else {
+ if (io.in) {
+ seg = NVMM_X64_SEG_ES;
} else {
- if (io.in) {
- seg = NVMM_X64_SEG_ES;
- } else {
- seg = fetch_segment(mach, &state);
- if (seg == -1)
- return -1;
- }
+ seg = fetch_segment(mach, &state);
+ if (seg == -1)
+ return -1;
}
+ }
- ret = segment_apply(&state.segs[seg], &gva, io.size);
+ if (__predict_true(is_long_mode(&state))) {
+ if (seg == NVMM_X64_SEG_GS || seg == NVMM_X64_SEG_FS) {
+ segment_apply(&state.segs[seg], &gva);
+ }
+ } else {
+ ret = segment_check(&state.segs[seg], gva, io.size);
if (ret == -1)
return -1;
+ segment_apply(&state.segs[seg], &gva);
}
if (exit->u.io.rep && !psld) {
@@ -780,6 +779,10 @@ nvmm_assist_io(struct nvmm_machine *mach
if (io.in) {
if (!exit->u.io.str) {
memcpy(&state.gprs[NVMM_X64_GPR_RAX], io.data, io.size);
+ if (io.size == 4) {
+ /* Zero-extend to 64 bits. */
+ state.gprs[NVMM_X64_GPR_RAX] &= size_to_mask(4);
+ }
} else {
ret = write_guest_memory(mach, &state, gva, io.data,
io.size);
@@ -807,6 +810,7 @@ done:
state.gprs[NVMM_X64_GPR_RIP] = exit->u.io.npc;
}
+out:
ret = nvmm_vcpu_setstate(mach, cpuid, &state, NVMM_X64_STATE_GPRS);
if (ret == -1)
return -1;
@@ -907,7 +911,6 @@ struct x86_regmodrm {
};
struct x86_immediate {
- size_t size; /* 1/2/4/8 */
uint64_t data;
};
@@ -1016,6 +1019,19 @@ static const struct x86_opcode primary_o
* Group1
*/
{
+ /* Ev, Iz */
+ .byte = 0x81,
+ .regmodrm = true,
+ .regtorm = true,
+ .szoverride = true,
+ .defsize = -1,
+ .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD,
+ .group1 = true,
+ .immediate = true,
+ .flags = FLAG_immz,
+ .emul = NULL /* group1 */
+ },
+ {
/* Ev, Ib */
.byte = 0x83,
.regmodrm = true,
@@ -1769,7 +1785,7 @@ node_dmo(struct x86_decode_fsm *fsm, str
return 0;
}
-static uint64_t
+static inline uint64_t
sign_extend(uint64_t val, int size)
{
if (size == 1) {
@@ -1806,15 +1822,13 @@ node_immediate(struct x86_decode_fsm *fs
}
store->type = STORE_IMM;
- store->u.imm.size = immsize;
if (fsm_read(fsm, (uint8_t *)&store->u.imm.data, immsize) == -1) {
return -1;
}
- fsm_advance(fsm, store->u.imm.size, NULL);
+ fsm_advance(fsm, immsize, NULL);
if (sesize != 0) {
store->u.imm.data = sign_extend(store->u.imm.data, sesize);
- store->u.imm.size = sesize;
}
return 0;
@@ -2204,6 +2218,11 @@ node_primary_opcode(struct x86_decode_fs
instr->operand_size = get_operand_size(fsm, instr);
instr->address_size = get_address_size(fsm, instr);
+ if (fsm->is64bit && (instr->operand_size == 4)) {
+ /* Zero-extend to 64 bits. */
+ instr->zeroextend_mask = ~size_to_mask(4);
+ }
+
if (opcode->regmodrm) {
fsm_advance(fsm, 1, node_regmodrm);
} else if (opcode->dmo) {
@@ -2220,22 +2239,6 @@ node_primary_opcode(struct x86_decode_fs
return 0;
}
-static uint64_t
-size_to_mask(size_t size)
-{
- switch (size) {
- case 1:
- return 0x00000000000000FF;
- case 2:
- return 0x000000000000FFFF;
- case 4:
- return 0x00000000FFFFFFFF;
- case 8:
- default:
- return 0xFFFFFFFFFFFFFFFF;
- }
-}
-
static int
node_secondary_opcode(struct x86_decode_fsm *fsm, struct x86_instr *instr)
{
@@ -2609,11 +2612,7 @@ gpr_read_address(struct x86_instr *instr
uint64_t val;
val = state->gprs[gpr];
- if (__predict_false(instr->address_size == 4)) {
- val &= 0x00000000FFFFFFFF;
- } else if (__predict_false(instr->address_size == 2)) {
- val &= 0x000000000000FFFF;
- }
+ val &= size_to_mask(instr->address_size);
return val;
}
@@ -2649,20 +2648,25 @@ store_to_gva(struct nvmm_x64_state *stat
gva += store->disp.data;
}
- if (!is_long_mode(state)) {
- if (store->hardseg != 0) {
- seg = store->hardseg;
+ if (store->hardseg != 0) {
+ seg = store->hardseg;
+ } else {
+ if (__predict_false(instr->legpref.seg != -1)) {
+ seg = instr->legpref.seg;
} else {
- if (__predict_false(instr->legpref.seg != -1)) {
- seg = instr->legpref.seg;
- } else {
- seg = NVMM_X64_SEG_DS;
- }
+ seg = NVMM_X64_SEG_DS;
}
+ }
- ret = segment_apply(&state->segs[seg], &gva, size);
+ if (__predict_true(is_long_mode(state))) {
+ if (seg == NVMM_X64_SEG_GS || seg == NVMM_X64_SEG_FS) {
+ segment_apply(&state->segs[seg], &gva);
+ }
+ } else {
+ ret = segment_check(&state->segs[seg], gva, size);
if (ret == -1)
return -1;
+ segment_apply(&state->segs[seg], &gva);
}
*gvap = gva;
@@ -2680,11 +2684,12 @@ fetch_segment(struct nvmm_machine *mach,
fetchsize = sizeof(inst_bytes);
gva = state->gprs[NVMM_X64_GPR_RIP];
- if (!is_long_mode(state)) {
- ret = segment_apply(&state->segs[NVMM_X64_SEG_CS], &gva,
+ if (__predict_false(!is_long_mode(state))) {
+ ret = segment_check(&state->segs[NVMM_X64_SEG_CS], gva,
fetchsize);
if (ret == -1)
return -1;
+ segment_apply(&state->segs[NVMM_X64_SEG_CS], &gva);
}
ret = read_guest_memory(mach, state, gva, inst_bytes, fetchsize);
@@ -2736,11 +2741,12 @@ fetch_instruction(struct nvmm_machine *m
fetchsize = sizeof(exit->u.mem.inst_bytes);
gva = state->gprs[NVMM_X64_GPR_RIP];
- if (!is_long_mode(state)) {
- ret = segment_apply(&state->segs[NVMM_X64_SEG_CS], &gva,
+ if (__predict_false(!is_long_mode(state))) {
+ ret = segment_check(&state->segs[NVMM_X64_SEG_CS], gva,
fetchsize);
if (ret == -1)
return -1;
+ segment_apply(&state->segs[NVMM_X64_SEG_CS], &gva);
}
ret = read_guest_memory(mach, state, gva, exit->u.mem.inst_bytes,
@@ -2871,7 +2877,7 @@ nvmm_assist_mem(struct nvmm_machine *mac
{
struct nvmm_x64_state state;
struct x86_instr instr;
- uint64_t cnt;
+ uint64_t cnt = 0; /* GCC */
int ret;
if (__predict_false(exit->reason != NVMM_EXIT_MEMORY)) {
@@ -2880,8 +2886,8 @@ nvmm_assist_mem(struct nvmm_machine *mac
}
ret = nvmm_vcpu_getstate(mach, cpuid, &state,
- NVMM_X64_STATE_GPRS | NVMM_X64_STATE_SEGS | NVMM_X64_STATE_CRS |
- NVMM_X64_STATE_MSRS);
+ NVMM_X64_STATE_GPRS | NVMM_X64_STATE_SEGS |
+ NVMM_X64_STATE_CRS | NVMM_X64_STATE_MSRS);
if (ret == -1)
return -1;
@@ -2902,6 +2908,14 @@ nvmm_assist_mem(struct nvmm_machine *mac
return -1;
}
+ if (instr.legpref.rep || instr.legpref.repn) {
+ cnt = rep_get_cnt(&state, instr.address_size);
+ if (__predict_false(cnt == 0)) {
+ state.gprs[NVMM_X64_GPR_RIP] += instr.len;
+ goto out;
+ }
+ }
+
if (instr.opcode->movs) {
ret = assist_mem_double(mach, &state, &instr);
} else {
@@ -2913,7 +2927,8 @@ nvmm_assist_mem(struct nvmm_machine *mac
}
if (instr.legpref.rep || instr.legpref.repn) {
- cnt = rep_dec_apply(&state, instr.address_size);
+ cnt -= 1;
+ rep_set_cnt(&state, instr.address_size, cnt);
if (cnt == 0) {
state.gprs[NVMM_X64_GPR_RIP] += instr.len;
} else if (__predict_false(instr.legpref.repn)) {
@@ -2925,6 +2940,7 @@ nvmm_assist_mem(struct nvmm_machine *mac
state.gprs[NVMM_X64_GPR_RIP] += instr.len;
}
+out:
ret = nvmm_vcpu_setstate(mach, cpuid, &state, NVMM_X64_STATE_GPRS);
if (ret == -1)
return -1;