diff options
| author | maxv <maxv@NetBSD.org> | 2019-01-06 16:10:51 +0000 |
|---|---|---|
| committer | maxv <maxv@NetBSD.org> | 2019-01-06 16:10:51 +0000 |
| commit | 7806fb18ec7eeb9dfed1f16f15ba82fa38f50ff2 (patch) | |
| tree | 29867947984be9df448989a0df0d8aee1c7dddd3 /lib/libnvmm | |
| parent | a2715533b43583b695a5c213a838d5017f3055f0 (diff) | |
Improvements and fixes in NVMM.
Kernel driver:
* Don't take an extra (unneeded) reference to the UAO.
* Provide npc for HLT. I'm not really happy with it right now, will
likely be revisited.
* Add the INT_SHADOW, INT_WINDOW_EXIT and NMI_WINDOW_EXIT states. Provide
them in the exitstate too.
* Don't take the TPR into account when processing INTs. The virtualizer
can do that itself (Qemu already does).
* Provide a hypervisor signature in CPUID, and hide SVM.
* Ignore certain MSRs. One special case is MSR_NB_CFG in which we set
NB_CFG_INITAPICCPUIDLO. Allow reads of MSR_TSC.
* If the LWP has pending signals or softints, leave, rather than waiting
for a rescheduling to happen later. This reduces interrupt processing
time in the guest (Qemu sends a signal to the thread, and now we leave
right away). This could be improved even more by sending an actual IPI
to the CPU, but I'll see later.
Libnvmm:
* Fix the MMU translation of large pages, we need to add the lower bits
too.
* Change the IO and Mem structures to take a pointer rather than a
static array. This provides more flexibility.
* Batch together the str+rep IO transactions. We do one big memory
read/write, and then send the IO commands to the hypervisor all at
once. This considerably increases performance.
* Decode MOVZX.
With these changes in place, Qemu+NVMM works. I can install NetBSD 8.0
in a VM with multiple VCPUs, connect to the network, etc.
Diffstat (limited to 'lib/libnvmm')
| -rw-r--r-- | lib/libnvmm/libnvmm.3 | 12 | ||||
| -rw-r--r-- | lib/libnvmm/libnvmm_x86.c | 206 | ||||
| -rw-r--r-- | lib/libnvmm/nvmm.h | 6 |
3 files changed, 205 insertions, 19 deletions
diff --git a/lib/libnvmm/libnvmm.3 b/lib/libnvmm/libnvmm.3 index 1479b54faa6..cf86d294369 100644 --- a/lib/libnvmm/libnvmm.3 +++ b/lib/libnvmm/libnvmm.3 @@ -1,4 +1,4 @@ -.\" $NetBSD: libnvmm.3,v 1.6 2018/12/27 07:22:31 maxv Exp $ +.\" $NetBSD: libnvmm.3,v 1.7 2019/01/06 16:10:51 maxv Exp $ .\" .\" Copyright (c) 2018 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd December 26, 2018 +.Dd January 06, 2019 .Dt LIBNVMM 3 .Os .Sh NAME @@ -242,8 +242,6 @@ on CPU .Fa cpuid from machine .Fa mach . -.Fa cb -will be called to handle the transaction. See .Sx I/O Assist below for details. @@ -255,8 +253,6 @@ on CPU .Fa cpuid from machine .Fa mach . -.Fa cb -will be called to handle the transaction. See .Sx Mem Assist below for details. @@ -415,7 +411,7 @@ struct nvmm_io { uint64_t port; bool in; size_t size; - uint8_t data[8]; + uint8_t *data; }; .Ed .Pp @@ -463,7 +459,7 @@ struct nvmm_mem { gpaddr_t gpa; bool write; size_t size; - uint8_t data[8]; + uint8_t *data; }; .Ed .Pp diff --git a/lib/libnvmm/libnvmm_x86.c b/lib/libnvmm/libnvmm_x86.c index c36a1fe6c85..e01131ed89e 100644 --- a/lib/libnvmm/libnvmm_x86.c +++ b/lib/libnvmm/libnvmm_x86.c @@ -1,4 +1,4 @@ -/* $NetBSD: libnvmm_x86.c,v 1.9 2019/01/04 10:25:39 maxv Exp $ */ +/* $NetBSD: libnvmm_x86.c,v 1.10 2019/01/06 16:10:51 maxv Exp $ */ /* * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -45,6 +45,8 @@ #include "nvmm.h" +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + #include <x86/specialreg.h> extern struct nvmm_callbacks __callbacks; @@ -83,6 +85,11 @@ nvmm_vcpu_dump(struct nvmm_machine *mach, nvmm_cpuid_t cpuid) (void *)state.segs[i].limit, state.segs[i].attrib.p, state.segs[i].attrib.def32); } + printf("| -> MSR_EFER=%p\n", (void *)state.msrs[NVMM_X64_MSR_EFER]); + printf("| -> CR0=%p\n", (void *)state.crs[NVMM_X64_CR_CR0]); + printf("| -> CR3=%p\n", (void *)state.crs[NVMM_X64_CR_CR3]); + printf("| -> CR4=%p\n", (void *)state.crs[NVMM_X64_CR_CR4]); + printf("| -> CR8=%p\n", (void *)state.crs[NVMM_X64_CR_CR8]); printf("| -> CPL=%p\n", (void *)state.misc[NVMM_X64_MISC_CPL]); return 0; @@ -131,6 +138,7 @@ x86_gva_to_gpa_32bit(struct nvmm_machine *mach, uint64_t cr3, return -1; if (pte & PG_PS) { *gpa = (pte & PTE32_L2_FRAME); + *gpa = *gpa + (gva & PTE32_L1_MASK); return 0; } @@ -215,6 +223,7 @@ x86_gva_to_gpa_32bit_pae(struct nvmm_machine *mach, uint64_t cr3, return -1; if (pte & PG_PS) { *gpa = (pte & PTE32_PAE_L2_FRAME); + *gpa = *gpa + (gva & PTE32_PAE_L1_MASK); return 0; } @@ -320,6 +329,7 @@ x86_gva_to_gpa_64bit(struct nvmm_machine *mach, uint64_t cr3, return -1; if (pte & PG_PS) { *gpa = (pte & PTE64_L3_FRAME); + *gpa = *gpa + (gva & (PTE64_L2_MASK|PTE64_L1_MASK)); return 0; } @@ -341,6 +351,7 @@ x86_gva_to_gpa_64bit(struct nvmm_machine *mach, uint64_t cr3, return -1; if (pte & PG_PS) { *gpa = (pte & PTE64_L2_FRAME); + *gpa = *gpa + (gva & PTE64_L1_MASK); return 0; } @@ -500,13 +511,34 @@ mask_from_adsize(size_t adsize) } static uint64_t +rep_get_cnt(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; + + return cnt; +} + +static void +rep_set_cnt(struct nvmm_x64_state *state, size_t adsize, uint64_t cnt) +{ + uint64_t mask; + + mask = mask_from_adsize(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 = state->gprs[NVMM_X64_GPR_RCX] & mask; cnt -= 1; cnt &= mask; @@ -521,6 +553,7 @@ read_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state, 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; @@ -547,6 +580,7 @@ read_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state, is_mmio = (ret == -1); if (is_mmio) { + mem.data = membuf; mem.gva = gva; mem.gpa = gpa; mem.write = false; @@ -572,6 +606,7 @@ write_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state, 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; @@ -598,6 +633,7 @@ write_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state, is_mmio = (ret == -1); if (is_mmio) { + mem.data = membuf; mem.gva = gva; mem.gpa = gpa; mem.write = true; @@ -622,16 +658,55 @@ write_guest_memory(struct nvmm_machine *mach, struct nvmm_x64_state *state, static int fetch_segment(struct nvmm_machine *, struct nvmm_x64_state *); +#define NVMM_IO_BATCH_SIZE 32 + +static int +assist_io_batch(struct nvmm_machine *mach, struct nvmm_x64_state *state, + struct nvmm_io *io, gvaddr_t gva, uint64_t cnt) +{ + uint8_t iobuf[NVMM_IO_BATCH_SIZE]; + size_t i, iosize, iocnt; + int ret; + + cnt = MIN(cnt, NVMM_IO_BATCH_SIZE); + iosize = MIN(io->size * cnt, NVMM_IO_BATCH_SIZE); + iocnt = iosize / io->size; + + io->data = iobuf; + + if (!io->in) { + ret = read_guest_memory(mach, state, gva, iobuf, iosize); + if (ret == -1) + return -1; + } + + for (i = 0; i < iocnt; i++) { + (*__callbacks.io)(io); + io->data += io->size; + } + + if (io->in) { + ret = write_guest_memory(mach, state, gva, iobuf, iosize); + if (ret == -1) + return -1; + } + + return iocnt; +} + int nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, struct nvmm_exit *exit) { struct nvmm_x64_state state; struct nvmm_io io; - uint64_t cnt; + uint64_t cnt = 0; /* GCC */ + uint8_t iobuf[8]; + int iocnt = 1; gvaddr_t gva; int reg = 0; /* GCC */ int ret, seg; + bool psld = false; if (__predict_false(exit->reason != NVMM_EXIT_IO)) { errno = EINVAL; @@ -641,6 +716,7 @@ nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, io.port = exit->u.io.port; io.in = (exit->u.io.type == NVMM_EXIT_IO_IN); io.size = exit->u.io.operand_size; + io.data = iobuf; ret = nvmm_vcpu_getstate(mach, cpuid, &state, NVMM_X64_STATE_GPRS | NVMM_X64_STATE_SEGS | @@ -648,6 +724,17 @@ nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, if (ret == -1) return -1; + if (exit->u.io.rep) { + cnt = rep_get_cnt(&state, exit->u.io.address_size); + if (__predict_false(cnt == 0)) { + return 0; + } + } + + if (__predict_false(state.gprs[NVMM_X64_GPR_RFLAGS] & PSL_D)) { + psld = true; + } + /* * Determine GVA. */ @@ -678,6 +765,13 @@ nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, if (ret == -1) return -1; } + + if (exit->u.io.rep && !psld) { + iocnt = assist_io_batch(mach, &state, &io, gva, cnt); + if (iocnt == -1) + return -1; + goto done; + } } if (!io.in) { @@ -704,16 +798,18 @@ nvmm_assist_io(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, } } +done: if (exit->u.io.str) { - if (state.gprs[NVMM_X64_GPR_RFLAGS] & PSL_D) { - state.gprs[reg] -= io.size; + if (__predict_false(psld)) { + state.gprs[reg] -= iocnt * io.size; } else { - state.gprs[reg] += io.size; + state.gprs[reg] += iocnt * io.size; } } if (exit->u.io.rep) { - cnt = rep_dec_apply(&state, exit->u.io.address_size); + cnt -= iocnt; + rep_set_cnt(&state, exit->u.io.address_size, cnt); if (cnt == 0) { state.gprs[NVMM_X64_GPR_RIP] = exit->u.io.npc; } @@ -858,6 +954,7 @@ struct x86_instr { struct x86_rexpref rexpref; size_t operand_size; size_t address_size; + uint64_t zeroextend_mask; struct x86_regmodrm regmodrm; @@ -912,6 +1009,7 @@ struct x86_group_entry { #define OPSIZE_QUAD 0x08 /* 8 bytes */ #define FLAG_z 0x02 +#define FLAG_e 0x10 static const struct x86_group_entry group11[8] = { [0] = { .emul = x86_emul_mov } @@ -1230,6 +1328,34 @@ static const struct x86_opcode primary_opcode_table[] = { }, }; +static const struct x86_opcode secondary_opcode_table[] = { + /* + * MOVZX + */ + { + /* Gv, Eb */ + .byte = 0xB6, + .regmodrm = true, + .regtorm = false, + .szoverride = true, + .defsize = OPSIZE_BYTE, + .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, + .flags = FLAG_e, + .emul = x86_emul_mov + }, + { + /* Gv, Ew */ + .byte = 0xB7, + .regmodrm = true, + .regtorm = false, + .szoverride = true, + .defsize = OPSIZE_WORD, + .allsize = OPSIZE_WORD|OPSIZE_DOUB|OPSIZE_QUAD, + .flags = FLAG_e, + .emul = x86_emul_mov + }, +}; + static const struct x86_reg gpr_map__rip = { NVMM_X64_GPR_RIP, 0xFFFFFFFFFFFFFFFF }; /* [REX-present][enc][opsize] */ @@ -2059,6 +2185,67 @@ node_primary_opcode(struct x86_decode_fsm *fsm, struct x86_instr *instr) 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) +{ + const struct x86_opcode *opcode; + uint8_t byte; + size_t i, n; + + if (fsm_read(fsm, &byte, sizeof(byte)) == -1) { + return -1; + } + + n = sizeof(secondary_opcode_table) / sizeof(secondary_opcode_table[0]); + for (i = 0; i < n; i++) { + if (secondary_opcode_table[i].byte == byte) + break; + } + if (i == n) { + return -1; + } + opcode = &secondary_opcode_table[i]; + + instr->opcode = opcode; + instr->emul = opcode->emul; + instr->operand_size = get_operand_size(fsm, instr); + instr->address_size = get_address_size(fsm, instr); + + if (opcode->flags & FLAG_e) { + /* + * Compute the mask for zero-extend. Update the operand size, + * we move fewer bytes. + */ + instr->zeroextend_mask = size_to_mask(instr->operand_size); + instr->zeroextend_mask &= ~size_to_mask(opcode->defsize); + instr->operand_size = opcode->defsize; + } + + if (opcode->regmodrm) { + fsm_advance(fsm, 1, node_regmodrm); + } else { + return -1; + } + + return 0; +} + static int node_main(struct x86_decode_fsm *fsm, struct x86_instr *instr) { @@ -2078,7 +2265,7 @@ node_main(struct x86_decode_fsm *fsm, struct x86_instr *instr) * after being introduced. */ if (byte == ESCAPE) { - return -1; + fsm_advance(fsm, 1, node_secondary_opcode); } else if (!instr->rexpref.present) { if (byte == VEX_1) { return -1; @@ -2600,10 +2787,12 @@ assist_mem_single(struct nvmm_machine *mach, struct nvmm_x64_state *state, struct x86_instr *instr) { struct nvmm_mem mem; + uint8_t membuf[8]; uint64_t val; int ret; memset(&mem, 0, sizeof(mem)); + mem.data = membuf; switch (instr->src.type) { case STORE_REG: @@ -2703,6 +2892,7 @@ assist_mem_single(struct nvmm_machine *mach, struct nvmm_x64_state *state, val = __SHIFTIN(val, instr->dst.u.reg->mask); state->gprs[instr->dst.u.reg->num] &= ~instr->dst.u.reg->mask; state->gprs[instr->dst.u.reg->num] |= val; + state->gprs[instr->dst.u.reg->num] &= ~instr->zeroextend_mask; } return 0; diff --git a/lib/libnvmm/nvmm.h b/lib/libnvmm/nvmm.h index 614b91aa47a..b924f235c80 100644 --- a/lib/libnvmm/nvmm.h +++ b/lib/libnvmm/nvmm.h @@ -1,4 +1,4 @@ -/* $NetBSD: nvmm.h,v 1.4 2018/12/27 07:22:31 maxv Exp $ */ +/* $NetBSD: nvmm.h,v 1.5 2019/01/06 16:10:51 maxv Exp $ */ /* * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -50,7 +50,7 @@ struct nvmm_io { uint64_t port; bool in; size_t size; - uint8_t data[8]; + uint8_t *data; }; struct nvmm_mem { @@ -58,7 +58,7 @@ struct nvmm_mem { gpaddr_t gpa; bool write; size_t size; - uint8_t data[8]; + uint8_t *data; }; struct nvmm_callbacks { |
