Module Name: src Committed By: maxv Date: Mon Jan 7 13:47:33 UTC 2019
Modified Files: src/lib/libnvmm: libnvmm_x86.c Log Message: Improvements and fixes: * Decode AND/OR/XOR from Group1. * Sign-extend the immediates and displacements in 64bit mode. * Fix the storage of {read,write}_guest_memory, now that we batch certain IO operations we can copy more than 8 bytes, and shit hits the fan. * Remove the CR4_PSE check in the 64bit MMU. This bit is actually ignored in long mode, and some systems (like FreeBSD) don't set it. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 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.10 src/lib/libnvmm/libnvmm_x86.c:1.11 --- src/lib/libnvmm/libnvmm_x86.c:1.10 Sun Jan 6 16:10:51 2019 +++ src/lib/libnvmm/libnvmm_x86.c Mon Jan 7 13:47:33 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: libnvmm_x86.c,v 1.10 2019/01/06 16:10:51 maxv Exp $ */ +/* $NetBSD: libnvmm_x86.c,v 1.11 2019/01/07 13:47:33 maxv Exp $ */ /* * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -282,7 +282,7 @@ x86_gva_64bit_canonical(gvaddr_t gva) static int x86_gva_to_gpa_64bit(struct nvmm_machine *mach, uint64_t cr3, - gvaddr_t gva, gpaddr_t *gpa, bool has_pse, nvmm_prot_t *prot) + gvaddr_t gva, gpaddr_t *gpa, nvmm_prot_t *prot) { gpaddr_t L4gpa, L3gpa, L2gpa, L1gpa; uintptr_t L4hva, L3hva, L2hva, L1hva; @@ -325,8 +325,6 @@ x86_gva_to_gpa_64bit(struct nvmm_machine *prot &= ~NVMM_PROT_WRITE; if (pte & PG_NX) *prot &= ~NVMM_PROT_EXEC; - if ((pte & PG_PS) && !has_pse) - return -1; if (pte & PG_PS) { *gpa = (pte & PTE64_L3_FRAME); *gpa = *gpa + (gva & (PTE64_L2_MASK|PTE64_L1_MASK)); @@ -347,8 +345,6 @@ x86_gva_to_gpa_64bit(struct nvmm_machine *prot &= ~NVMM_PROT_WRITE; if (pte & PG_NX) *prot &= ~NVMM_PROT_EXEC; - if ((pte & PG_PS) && !has_pse) - return -1; if (pte & PG_PS) { *gpa = (pte & PTE64_L2_FRAME); *gpa = *gpa + (gva & PTE64_L1_MASK); @@ -402,7 +398,7 @@ x86_gva_to_gpa(struct nvmm_machine *mach if (is_pae && is_lng) { /* 64bit */ - ret = x86_gva_to_gpa_64bit(mach, cr3, gva, gpa, has_pse, prot); + ret = x86_gva_to_gpa_64bit(mach, cr3, gva, gpa, prot); } else if (is_pae && !is_lng) { /* 32bit PAE */ ret = x86_gva_to_gpa_32bit_pae(mach, cr3, gva, gpa, has_pse, @@ -553,7 +549,6 @@ read_guest_memory(struct nvmm_machine *m gvaddr_t gva, uint8_t *data, size_t size) { struct nvmm_mem mem; - uint8_t membuf[8]; nvmm_prot_t prot; gpaddr_t gpa; uintptr_t hva; @@ -580,13 +575,12 @@ read_guest_memory(struct nvmm_machine *m is_mmio = (ret == -1); if (is_mmio) { - mem.data = membuf; + mem.data = data; mem.gva = gva; mem.gpa = gpa; mem.write = false; mem.size = size; (*__callbacks.mem)(&mem); - memcpy(data, mem.data, size); } else { memcpy(data, (uint8_t *)hva, size); } @@ -606,7 +600,6 @@ write_guest_memory(struct nvmm_machine * gvaddr_t gva, uint8_t *data, size_t size) { struct nvmm_mem mem; - uint8_t membuf[8]; nvmm_prot_t prot; gpaddr_t gpa; uintptr_t hva; @@ -633,11 +626,10 @@ write_guest_memory(struct nvmm_machine * is_mmio = (ret == -1); if (is_mmio) { - mem.data = membuf; + mem.data = data; mem.gva = gva; mem.gpa = gpa; mem.write = true; - memcpy(mem.data, data, size); mem.size = size; (*__callbacks.mem)(&mem); } else { @@ -878,7 +870,7 @@ enum x86_disp_type { struct x86_disp { enum x86_disp_type type; - uint8_t data[4]; + uint64_t data; /* 4 bytes, but can be sign-extended */ }; enum REGMODRM__Mod { @@ -919,7 +911,7 @@ struct x86_regmodrm { struct x86_immediate { size_t size; /* 1/2/4/8 */ - uint8_t data[8]; + uint64_t data; }; struct x86_sib { @@ -992,9 +984,9 @@ struct x86_opcode { bool szoverride; int defsize; int allsize; + bool group1; bool group11; bool immediate; - int immsize; int flags; void (*emul)(struct nvmm_mem *, void (*)(struct nvmm_mem *), uint64_t *); }; @@ -1008,8 +1000,15 @@ struct x86_group_entry { #define OPSIZE_DOUB 0x04 /* 4 bytes */ #define OPSIZE_QUAD 0x08 /* 8 bytes */ -#define FLAG_z 0x02 -#define FLAG_e 0x10 +#define FLAG_imm8 0x01 +#define FLAG_immz 0x02 +#define FLAG_ze 0x04 + +static const struct x86_group_entry group1[8] = { + [1] = { .emul = x86_emul_or }, + [4] = { .emul = x86_emul_and }, + [6] = { .emul = x86_emul_xor } +}; static const struct x86_group_entry group11[8] = { [0] = { .emul = x86_emul_mov } @@ -1017,9 +1016,27 @@ static const struct x86_group_entry grou static const struct x86_opcode primary_opcode_table[] = { /* + * Group1 + */ + { + /* Ev, Ib */ + .byte = 0x83, + .regmodrm = true, + .regtorm = true, + .szoverride = true, + .defsize = -1, + .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, + .group1 = true, + .immediate = true, + .flags = FLAG_imm8, + .emul = NULL /* group1 */ + }, + + /* * Group11 */ { + /* Eb, Ib */ .byte = 0xC6, .regmodrm = true, .regtorm = true, @@ -1028,10 +1045,10 @@ static const struct x86_opcode primary_o .allsize = -1, .group11 = true, .immediate = true, - .immsize = OPSIZE_BYTE, .emul = NULL /* group11 */ }, { + /* Ev, Iz */ .byte = 0xC7, .regmodrm = true, .regtorm = true, @@ -1040,8 +1057,7 @@ static const struct x86_opcode primary_o .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, .group11 = true, .immediate = true, - .immsize = -1, /* special, Z */ - .flags = FLAG_z, + .flags = FLAG_immz, .emul = NULL /* group11 */ }, @@ -1340,7 +1356,7 @@ static const struct x86_opcode secondary .szoverride = true, .defsize = OPSIZE_BYTE, .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, - .flags = FLAG_e, + .flags = FLAG_ze, .emul = x86_emul_mov }, { @@ -1351,7 +1367,7 @@ static const struct x86_opcode secondary .szoverride = true, .defsize = OPSIZE_WORD, .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, - .flags = FLAG_e, + .flags = FLAG_ze, .emul = x86_emul_mov }, }; @@ -1756,34 +1772,54 @@ node_dmo(struct x86_decode_fsm *fsm, str return 0; } +static uint64_t +sign_extend(uint64_t val, int size) +{ + if (size == 1) { + if (val & __BIT(7)) + val |= 0xFFFFFFFFFFFFFF00; + } else if (size == 2) { + if (val & __BIT(15)) + val |= 0xFFFFFFFFFFFF0000; + } else if (size == 4) { + if (val & __BIT(31)) + val |= 0xFFFFFFFF00000000; + } + return val; +} + static int node_immediate(struct x86_decode_fsm *fsm, struct x86_instr *instr) { const struct x86_opcode *opcode = instr->opcode; struct x86_store *store; - uint8_t flags; uint8_t immsize; + size_t sesize = 0; /* The immediate is the source */ store = &instr->src; immsize = instr->operand_size; - /* Get the correct flags */ - flags = opcode->flags; - if ((flags & FLAG_z) && (immsize == 8)) { - /* 'z' operates here */ + if (opcode->flags & FLAG_imm8) { + sesize = immsize; + immsize = 1; + } else if ((opcode->flags & FLAG_immz) && (immsize == 8)) { + sesize = immsize; immsize = 4; } store->type = STORE_IMM; store->u.imm.size = immsize; - - if (fsm_read(fsm, store->u.imm.data, store->u.imm.size) == -1) { + if (fsm_read(fsm, (uint8_t *)&store->u.imm.data, immsize) == -1) { return -1; } - fsm_advance(fsm, store->u.imm.size, NULL); + if (sesize != 0) { + store->u.imm.data = sign_extend(store->u.imm.data, sesize); + store->u.imm.size = sesize; + } + return 0; } @@ -1791,6 +1827,7 @@ static int node_disp(struct x86_decode_fsm *fsm, struct x86_instr *instr) { const struct x86_opcode *opcode = instr->opcode; + uint64_t data = 0; size_t n; if (instr->strm->disp.type == DISP_1) { @@ -1799,10 +1836,16 @@ node_disp(struct x86_decode_fsm *fsm, st n = 4; } - if (fsm_read(fsm, instr->strm->disp.data, n) == -1) { + if (fsm_read(fsm, (uint8_t *)&data, n) == -1) { return -1; } + if (__predict_true(fsm->is64bit)) { + data = sign_extend(data, n); + } + + instr->strm->disp.data = data; + if (opcode->immediate) { fsm_advance(fsm, n, node_immediate); } else { @@ -1903,12 +1946,7 @@ get_register_reg(struct x86_instr *instr const struct x86_reg *reg; size_t regsize; - if ((opcode->flags & FLAG_z) && (instr->operand_size == 8)) { - /* 'z' operates here */ - regsize = 4; - } else { - regsize = instr->operand_size; - } + regsize = instr->operand_size; reg = &gpr_map[instr->rexpref.r][enc][regsize-1]; if (reg->num == -1) { @@ -1926,12 +1964,7 @@ get_register_rm(struct x86_instr *instr, size_t regsize; if (instr->strm->disp.type == DISP_NONE) { - if ((opcode->flags & FLAG_z) && (instr->operand_size == 8)) { - /* 'z' operates here */ - regsize = 4; - } else { - regsize = instr->operand_size; - } + regsize = instr->operand_size; } else { /* Indirect access, the size is that of the address. */ regsize = instr->address_size; @@ -2015,7 +2048,12 @@ node_regmodrm(struct x86_decode_fsm *fsm * Special cases: Groups. The REG field of REGMODRM is the index in * the group. op1 gets overwritten in the Immediate node, if any. */ - if (opcode->group11) { + if (opcode->group1) { + if (group1[instr->regmodrm.reg].emul == NULL) { + return -1; + } + instr->emul = group1[instr->regmodrm.reg].emul; + } else if (opcode->group11) { if (group11[instr->regmodrm.reg].emul == NULL) { return -1; } @@ -2227,7 +2265,7 @@ node_secondary_opcode(struct x86_decode_ instr->operand_size = get_operand_size(fsm, instr); instr->address_size = get_address_size(fsm, instr); - if (opcode->flags & FLAG_e) { + if (opcode->flags & FLAG_ze) { /* * Compute the mask for zero-extend. Update the operand size, * we move fewer bytes. @@ -2597,7 +2635,6 @@ store_to_gva(struct nvmm_x64_state *stat gvaddr_t gva = 0; uint64_t reg; int ret, seg; - uint32_t *p; if (store->type == STORE_SIB) { sib = &store->u.sib; @@ -2618,8 +2655,7 @@ store_to_gva(struct nvmm_x64_state *stat } if (store->disp.type != DISP_NONE) { - p = (uint32_t *)&store->disp.data[0]; - gva += *p; + gva += store->disp.data; } if (!is_long_mode(state)) { @@ -2791,7 +2827,7 @@ assist_mem_single(struct nvmm_machine *m uint64_t val; int ret; - memset(&mem, 0, sizeof(mem)); + memset(membuf, 0, sizeof(membuf)); mem.data = membuf; switch (instr->src.type) { @@ -2817,7 +2853,7 @@ assist_mem_single(struct nvmm_machine *m case STORE_IMM: mem.write = true; mem.size = instr->src.u.imm.size; - memcpy(mem.data, instr->src.u.imm.data, mem.size); + memcpy(mem.data, &instr->src.u.imm.data, mem.size); break; case STORE_SIB: