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: