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;