Module Name: src Committed By: maxv Date: Wed Oct 23 07:01:12 UTC 2019
Modified Files: src/lib/libnvmm: libnvmm.c libnvmm_x86.c nvmm.h src/sys/dev/nvmm: nvmm.c nvmm.h nvmm_internal.h nvmm_ioctl.h src/sys/dev/nvmm/x86: nvmm_x86.h nvmm_x86_svm.c nvmm_x86_vmx.c src/tests/lib/libnvmm: h_io_assist.c h_mem_assist.c Log Message: Miscellaneous changes in NVMM, to address several inconsistencies and issues in the libnvmm API. - Rename NVMM_CAPABILITY_VERSION to NVMM_KERN_VERSION, and check it in libnvmm. Introduce NVMM_USER_VERSION, for future use. - In libnvmm, open "/dev/nvmm" as read-only and with O_CLOEXEC. This is to avoid sharing the VMs with the children if the process forks. In the NVMM driver, force O_CLOEXEC on open(). - Rename the following things for consistency: nvmm_exit* -> nvmm_vcpu_exit* nvmm_event* -> nvmm_vcpu_event* NVMM_EXIT_* -> NVMM_VCPU_EXIT_* NVMM_EVENT_INTERRUPT_HW -> NVMM_VCPU_EVENT_INTR NVMM_EVENT_EXCEPTION -> NVMM_VCPU_EVENT_EXCP Delete NVMM_EVENT_INTERRUPT_SW, unused already. - Slightly reorganize the MI/MD definitions, for internal clarity. - Split NVMM_VCPU_EXIT_MSR in two: NVMM_VCPU_EXIT_{RD,WR}MSR. Also provide separate u.rdmsr and u.wrmsr fields. This is more consistent with the other exit reasons. - Change the types of several variables: event.type enum -> u_int event.vector uint64_t -> uint8_t exit.u.*msr.msr: uint64_t -> uint32_t exit.u.io.type: enum -> bool exit.u.io.seg: int -> int8_t cap.arch.mxcsr_mask: uint64_t -> uint32_t cap.arch.conf_cpuid_maxops: uint64_t -> uint32_t - Delete NVMM_VCPU_EXIT_MWAIT_COND, it is AMD-only and confusing, and we already intercept 'monitor' so it is never armed. - Introduce vmx_exit_insn() for NVMM-Intel, similar to svm_exit_insn(). The 'npc' field wasn't getting filled properly during certain VMEXITs. - Introduce nvmm_vcpu_configure(). Similar to nvmm_machine_configure(), but as its name indicates, the configuration is per-VCPU and not per-VM. Migrate and rename NVMM_MACH_CONF_X86_CPUID to NVMM_VCPU_CONF_CPUID. This becomes per-VCPU, which makes more sense than per-VM. - Extend the NVMM_VCPU_CONF_CPUID conf to allow triggering VMEXITs on specific leaves. Until now we could only mask the leaves. An uint32_t is added in the structure: uint32_t mask:1; uint32_t exit:1; uint32_t rsvd:30; The two first bits select the desired behavior on the leaf. Specifying zero on both resets the leaf to the default behavior. The new NVMM_VCPU_EXIT_CPUID exit reason is added. To generate a diff of this commit: cvs rdiff -u -r1.14 -r1.15 src/lib/libnvmm/libnvmm.c cvs rdiff -u -r1.35 -r1.36 src/lib/libnvmm/libnvmm_x86.c cvs rdiff -u -r1.12 -r1.13 src/lib/libnvmm/nvmm.h cvs rdiff -u -r1.22 -r1.23 src/sys/dev/nvmm/nvmm.c cvs rdiff -u -r1.10 -r1.11 src/sys/dev/nvmm/nvmm.h cvs rdiff -u -r1.12 -r1.13 src/sys/dev/nvmm/nvmm_internal.h cvs rdiff -u -r1.7 -r1.8 src/sys/dev/nvmm/nvmm_ioctl.h cvs rdiff -u -r1.15 -r1.16 src/sys/dev/nvmm/x86/nvmm_x86.h cvs rdiff -u -r1.50 -r1.51 src/sys/dev/nvmm/x86/nvmm_x86_svm.c cvs rdiff -u -r1.39 -r1.40 src/sys/dev/nvmm/x86/nvmm_x86_vmx.c cvs rdiff -u -r1.8 -r1.9 src/tests/lib/libnvmm/h_io_assist.c cvs rdiff -u -r1.14 -r1.15 src/tests/lib/libnvmm/h_mem_assist.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.c diff -u src/lib/libnvmm/libnvmm.c:1.14 src/lib/libnvmm/libnvmm.c:1.15 --- src/lib/libnvmm/libnvmm.c:1.14 Sat Jun 8 07:27:44 2019 +++ src/lib/libnvmm/libnvmm.c Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: libnvmm.c,v 1.14 2019/06/08 07:27:44 maxv Exp $ */ +/* $NetBSD: libnvmm.c,v 1.15 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -161,7 +161,7 @@ nvmm_init(void) { if (nvmm_fd != -1) return 0; - nvmm_fd = open("/dev/nvmm", O_RDWR); + nvmm_fd = open("/dev/nvmm", O_RDONLY | O_CLOEXEC); if (nvmm_fd == -1) return -1; if (nvmm_capability(&__capability) == -1) { @@ -169,6 +169,13 @@ nvmm_init(void) nvmm_fd = -1; return -1; } + if (__capability.version != NVMM_KERN_VERSION) { + close(nvmm_fd); + nvmm_fd = -1; + errno = EPROGMISMATCH; + return -1; + } + return 0; } @@ -322,6 +329,25 @@ nvmm_vcpu_destroy(struct nvmm_machine *m } int +nvmm_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, + uint64_t op, void *conf) +{ + struct nvmm_ioc_vcpu_configure args; + int ret; + + args.machid = mach->machid; + args.cpuid = vcpu->cpuid; + args.op = op; + args.conf = conf; + + ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CONFIGURE, &args); + if (ret == -1) + return -1; + + return 0; +} + +int nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, uint64_t flags) { Index: src/lib/libnvmm/libnvmm_x86.c diff -u src/lib/libnvmm/libnvmm_x86.c:1.35 src/lib/libnvmm/libnvmm_x86.c:1.36 --- src/lib/libnvmm/libnvmm_x86.c:1.35 Sat Oct 19 19:45:10 2019 +++ src/lib/libnvmm/libnvmm_x86.c Wed Oct 23 07:01:11 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: libnvmm_x86.c,v 1.35 2019/10/19 19:45:10 maxv Exp $ */ +/* $NetBSD: libnvmm_x86.c,v 1.36 2019/10/23 07:01:11 maxv Exp $ */ /* * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. @@ -706,7 +706,7 @@ int nvmm_assist_io(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) { struct nvmm_x64_state *state = vcpu->state; - struct nvmm_exit *exit = vcpu->exit; + struct nvmm_vcpu_exit *exit = vcpu->exit; struct nvmm_io io; uint64_t cnt = 0; /* GCC */ uint8_t iobuf[8]; @@ -716,13 +716,13 @@ nvmm_assist_io(struct nvmm_machine *mach int ret, seg; bool psld = false; - if (__predict_false(exit->reason != NVMM_EXIT_IO)) { + if (__predict_false(exit->reason != NVMM_VCPU_EXIT_IO)) { errno = EINVAL; return -1; } io.port = exit->u.io.port; - io.in = (exit->u.io.type == NVMM_EXIT_IO_IN); + io.in = exit->u.io.in; io.size = exit->u.io.operand_size; io.data = iobuf; @@ -3107,7 +3107,7 @@ fetch_segment(struct nvmm_machine *mach, static int fetch_instruction(struct nvmm_machine *mach, struct nvmm_x64_state *state, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { size_t fetchsize; gvaddr_t gva; @@ -3170,7 +3170,7 @@ assist_mem_double(struct nvmm_machine *m static int assist_mem_single(struct nvmm_machine *mach, struct nvmm_x64_state *state, - struct x86_instr *instr, struct nvmm_exit *exit) + struct x86_instr *instr, struct nvmm_vcpu_exit *exit) { struct nvmm_mem mem; uint8_t membuf[8]; @@ -3292,12 +3292,12 @@ int nvmm_assist_mem(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) { struct nvmm_x64_state *state = vcpu->state; - struct nvmm_exit *exit = vcpu->exit; + struct nvmm_vcpu_exit *exit = vcpu->exit; struct x86_instr instr; uint64_t cnt = 0; /* GCC */ int ret; - if (__predict_false(exit->reason != NVMM_EXIT_MEMORY)) { + if (__predict_false(exit->reason != NVMM_VCPU_EXIT_MEMORY)) { errno = EINVAL; return -1; } Index: src/lib/libnvmm/nvmm.h diff -u src/lib/libnvmm/nvmm.h:1.12 src/lib/libnvmm/nvmm.h:1.13 --- src/lib/libnvmm/nvmm.h:1.12 Sat Jun 8 07:27:44 2019 +++ src/lib/libnvmm/nvmm.h Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm.h,v 1.12 2019/06/08 07:27:44 maxv Exp $ */ +/* $NetBSD: nvmm.h,v 1.13 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -38,6 +38,8 @@ #include <dev/nvmm/nvmm.h> #include <dev/nvmm/nvmm_ioctl.h> +#define NVMM_USER_VERSION 1 + struct nvmm_io { uint64_t port; bool in; @@ -67,8 +69,8 @@ struct nvmm_machine { struct nvmm_vcpu { nvmm_cpuid_t cpuid; struct nvmm_vcpu_state *state; - struct nvmm_event *event; - struct nvmm_exit *exit; + struct nvmm_vcpu_event *event; + struct nvmm_vcpu_exit *exit; }; #define NVMM_MACH_CONF_CALLBACKS NVMM_MACH_CONF_LIBNVMM_BEGIN @@ -88,6 +90,8 @@ int nvmm_machine_configure(struct nvmm_m int nvmm_vcpu_create(struct nvmm_machine *, nvmm_cpuid_t, struct nvmm_vcpu *); int nvmm_vcpu_destroy(struct nvmm_machine *, struct nvmm_vcpu *); +int nvmm_vcpu_configure(struct nvmm_machine *, struct nvmm_vcpu *, uint64_t, + void *); int nvmm_vcpu_setstate(struct nvmm_machine *, struct nvmm_vcpu *, uint64_t); int nvmm_vcpu_getstate(struct nvmm_machine *, struct nvmm_vcpu *, uint64_t); int nvmm_vcpu_inject(struct nvmm_machine *, struct nvmm_vcpu *); Index: src/sys/dev/nvmm/nvmm.c diff -u src/sys/dev/nvmm/nvmm.c:1.22 src/sys/dev/nvmm/nvmm.c:1.23 --- src/sys/dev/nvmm/nvmm.c:1.22 Sat Jul 6 05:13:10 2019 +++ src/sys/dev/nvmm/nvmm.c Wed Oct 23 07:01:11 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: nvmm.c,v 1.22 2019/07/06 05:13:10 maxv Exp $ */ +/* $NetBSD: nvmm.c,v 1.23 2019/10/23 07:01:11 maxv Exp $ */ /* * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: nvmm.c,v 1.22 2019/07/06 05:13:10 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nvmm.c,v 1.23 2019/10/23 07:01:11 maxv Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -247,7 +247,7 @@ nvmm_kill_machines(struct nvmm_owner *ow static int nvmm_capability(struct nvmm_owner *owner, struct nvmm_ioc_capability *args) { - args->cap.version = NVMM_CAPABILITY_VERSION; + args->cap.version = NVMM_KERN_VERSION; args->cap.state_size = nvmm_impl->state_size; args->cap.max_machines = NVMM_MAX_MACHINES; args->cap.max_vcpus = NVMM_MAX_VCPUS; @@ -343,11 +343,11 @@ nvmm_machine_configure(struct nvmm_owner int error; op = NVMM_MACH_CONF_MD(args->op); - if (__predict_false(op >= nvmm_impl->conf_max)) { + if (__predict_false(op >= nvmm_impl->mach_conf_max)) { return EINVAL; } - allocsz = nvmm_impl->conf_sizes[op]; + allocsz = nvmm_impl->mach_conf_sizes[op]; data = kmem_alloc(allocsz, KM_SLEEP); error = nvmm_machine_get(owner, args->machid, &mach, true); @@ -443,6 +443,51 @@ out: } static int +nvmm_vcpu_configure(struct nvmm_owner *owner, + struct nvmm_ioc_vcpu_configure *args) +{ + struct nvmm_machine *mach; + struct nvmm_cpu *vcpu; + size_t allocsz; + uint64_t op; + void *data; + int error; + + op = NVMM_VCPU_CONF_MD(args->op); + if (__predict_false(op >= nvmm_impl->vcpu_conf_max)) + return EINVAL; + + allocsz = nvmm_impl->vcpu_conf_sizes[op]; + data = kmem_alloc(allocsz, KM_SLEEP); + + error = nvmm_machine_get(owner, args->machid, &mach, false); + if (error) { + kmem_free(data, allocsz); + return error; + } + + error = nvmm_vcpu_get(mach, args->cpuid, &vcpu); + if (error) { + nvmm_machine_put(mach); + kmem_free(data, allocsz); + return error; + } + + error = copyin(args->conf, data, allocsz); + if (error) { + goto out; + } + + error = (*nvmm_impl->vcpu_configure)(vcpu, op, data); + +out: + nvmm_vcpu_put(vcpu); + nvmm_machine_put(mach); + kmem_free(data, allocsz); + return error; +} + +static int nvmm_vcpu_setstate(struct nvmm_owner *owner, struct nvmm_ioc_vcpu_setstate *args) { @@ -515,7 +560,7 @@ out: static int nvmm_do_vcpu_run(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct vmspace *vm = mach->vm; int ret; @@ -526,7 +571,7 @@ nvmm_do_vcpu_run(struct nvmm_machine *ma return ret; } - if (__predict_true(exit->reason != NVMM_EXIT_MEMORY)) { + if (__predict_true(exit->reason != NVMM_VCPU_EXIT_MEMORY)) { break; } if (exit->u.mem.gpa >= mach->gpa_end) { @@ -996,6 +1041,8 @@ nvmm_open(dev_t dev, int flags, int type if (minor(dev) != 0) return EXDEV; + if (!(flags & O_CLOEXEC)) + return EINVAL; error = fd_allocfile(&fp, &fd); if (error) return error; @@ -1073,6 +1120,8 @@ nvmm_ioctl(file_t *fp, u_long cmd, void return nvmm_vcpu_create(owner, data); case NVMM_IOC_VCPU_DESTROY: return nvmm_vcpu_destroy(owner, data); + case NVMM_IOC_VCPU_CONFIGURE: + return nvmm_vcpu_configure(owner, data); case NVMM_IOC_VCPU_SETSTATE: return nvmm_vcpu_setstate(owner, data); case NVMM_IOC_VCPU_GETSTATE: Index: src/sys/dev/nvmm/nvmm.h diff -u src/sys/dev/nvmm/nvmm.h:1.10 src/sys/dev/nvmm/nvmm.h:1.11 --- src/sys/dev/nvmm/nvmm.h:1.10 Sat May 11 07:31:56 2019 +++ src/sys/dev/nvmm/nvmm.h Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm.h,v 1.10 2019/05/11 07:31:56 maxv Exp $ */ +/* $NetBSD: nvmm.h,v 1.11 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -48,42 +48,7 @@ typedef uint32_t nvmm_cpuid_t; #include <dev/nvmm/x86/nvmm_x86.h> #endif -#define NVMM_EXIT_NONE 0x0000000000000000ULL -#define NVMM_EXIT_MEMORY 0x0000000000000001ULL -#define NVMM_EXIT_IO 0x0000000000000002ULL -#define NVMM_EXIT_MSR 0x0000000000000003ULL /* x86 only? */ -#define NVMM_EXIT_INT_READY 0x0000000000000004ULL -#define NVMM_EXIT_NMI_READY 0x0000000000000005ULL -#define NVMM_EXIT_HALTED 0x0000000000000006ULL -#define NVMM_EXIT_SHUTDOWN 0x0000000000000007ULL -/* Range 0x1000-0x10000 is MD. */ -#define NVMM_EXIT_INVALID 0xFFFFFFFFFFFFFFFFULL - -struct nvmm_exit { - uint64_t reason; - union nvmm_exit_md u; - uint64_t exitstate[8]; -}; - -enum nvmm_event_type { - NVMM_EVENT_INTERRUPT_HW, - NVMM_EVENT_INTERRUPT_SW, - NVMM_EVENT_EXCEPTION -}; - -struct nvmm_event { - enum nvmm_event_type type; - uint64_t vector; - union { - /* NVMM_EVENT_INTERRUPT_HW */ - uint8_t prio; - - /* NVMM_EVENT_EXCEPTION */ - uint64_t error; - } u; -}; - -#define NVMM_CAPABILITY_VERSION 1 +#define NVMM_KERN_VERSION 1 struct nvmm_capability { uint64_t version; @@ -94,13 +59,18 @@ struct nvmm_capability { struct nvmm_cap_md arch; }; -/* Configuration slots. */ +/* Machine configuration slots. */ #define NVMM_MACH_CONF_LIBNVMM_BEGIN 0 #define NVMM_MACH_CONF_MI_BEGIN 100 #define NVMM_MACH_CONF_MD_BEGIN 200 - #define NVMM_MACH_CONF_MD(op) (op - NVMM_MACH_CONF_MD_BEGIN) +/* VCPU configuration slots. */ +#define NVMM_VCPU_CONF_LIBNVMM_BEGIN 0 +#define NVMM_VCPU_CONF_MI_BEGIN 100 +#define NVMM_VCPU_CONF_MD_BEGIN 200 +#define NVMM_VCPU_CONF_MD(op) (op - NVMM_VCPU_CONF_MD_BEGIN) + struct nvmm_comm_page { /* State. */ uint64_t state_wanted; @@ -110,7 +80,7 @@ struct nvmm_comm_page { /* Event. */ bool event_commit; - struct nvmm_event event; + struct nvmm_vcpu_event event; }; /* Index: src/sys/dev/nvmm/nvmm_internal.h diff -u src/sys/dev/nvmm/nvmm_internal.h:1.12 src/sys/dev/nvmm/nvmm_internal.h:1.13 --- src/sys/dev/nvmm/nvmm_internal.h:1.12 Sat Jul 6 05:13:10 2019 +++ src/sys/dev/nvmm/nvmm_internal.h Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm_internal.h,v 1.12 2019/07/06 05:13:10 maxv Exp $ */ +/* $NetBSD: nvmm_internal.h,v 1.13 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -95,8 +95,12 @@ struct nvmm_impl { void (*fini)(void); void (*capability)(struct nvmm_capability *); - size_t conf_max; - const size_t *conf_sizes; + size_t mach_conf_max; + const size_t *mach_conf_sizes; + + size_t vcpu_conf_max; + const size_t *vcpu_conf_sizes; + size_t state_size; void (*machine_create)(struct nvmm_machine *); @@ -105,11 +109,12 @@ struct nvmm_impl { int (*vcpu_create)(struct nvmm_machine *, struct nvmm_cpu *); void (*vcpu_destroy)(struct nvmm_machine *, struct nvmm_cpu *); + int (*vcpu_configure)(struct nvmm_cpu *, uint64_t, void *); void (*vcpu_setstate)(struct nvmm_cpu *); void (*vcpu_getstate)(struct nvmm_cpu *); int (*vcpu_inject)(struct nvmm_cpu *); int (*vcpu_run)(struct nvmm_machine *, struct nvmm_cpu *, - struct nvmm_exit *); + struct nvmm_vcpu_exit *); }; extern const struct nvmm_impl nvmm_x86_svm; Index: src/sys/dev/nvmm/nvmm_ioctl.h diff -u src/sys/dev/nvmm/nvmm_ioctl.h:1.7 src/sys/dev/nvmm/nvmm_ioctl.h:1.8 --- src/sys/dev/nvmm/nvmm_ioctl.h:1.7 Wed May 1 09:20:21 2019 +++ src/sys/dev/nvmm/nvmm_ioctl.h Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm_ioctl.h,v 1.7 2019/05/01 09:20:21 maxv Exp $ */ +/* $NetBSD: nvmm_ioctl.h,v 1.8 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -62,6 +62,13 @@ struct nvmm_ioc_vcpu_destroy { nvmm_cpuid_t cpuid; }; +struct nvmm_ioc_vcpu_configure { + nvmm_machid_t machid; + nvmm_cpuid_t cpuid; + uint64_t op; + void *conf; +}; + struct nvmm_ioc_vcpu_setstate { nvmm_machid_t machid; nvmm_cpuid_t cpuid; @@ -82,7 +89,7 @@ struct nvmm_ioc_vcpu_run { nvmm_machid_t machid; nvmm_cpuid_t cpuid; /* output */ - struct nvmm_exit exit; + struct nvmm_vcpu_exit exit; }; struct nvmm_ioc_hva_map { @@ -134,14 +141,15 @@ struct nvmm_ioc_ctl { #define NVMM_IOC_MACHINE_CONFIGURE _IOW ('N', 3, struct nvmm_ioc_machine_configure) #define NVMM_IOC_VCPU_CREATE _IOW ('N', 4, struct nvmm_ioc_vcpu_create) #define NVMM_IOC_VCPU_DESTROY _IOW ('N', 5, struct nvmm_ioc_vcpu_destroy) -#define NVMM_IOC_VCPU_SETSTATE _IOW ('N', 6, struct nvmm_ioc_vcpu_setstate) -#define NVMM_IOC_VCPU_GETSTATE _IOW ('N', 7, struct nvmm_ioc_vcpu_getstate) -#define NVMM_IOC_VCPU_INJECT _IOW ('N', 8, struct nvmm_ioc_vcpu_inject) -#define NVMM_IOC_VCPU_RUN _IOWR('N', 9, struct nvmm_ioc_vcpu_run) -#define NVMM_IOC_GPA_MAP _IOW ('N', 10, struct nvmm_ioc_gpa_map) -#define NVMM_IOC_GPA_UNMAP _IOW ('N', 11, struct nvmm_ioc_gpa_unmap) -#define NVMM_IOC_HVA_MAP _IOW ('N', 12, struct nvmm_ioc_hva_map) -#define NVMM_IOC_HVA_UNMAP _IOW ('N', 13, struct nvmm_ioc_hva_unmap) +#define NVMM_IOC_VCPU_CONFIGURE _IOW ('N', 6, struct nvmm_ioc_vcpu_configure) +#define NVMM_IOC_VCPU_SETSTATE _IOW ('N', 7, struct nvmm_ioc_vcpu_setstate) +#define NVMM_IOC_VCPU_GETSTATE _IOW ('N', 8, struct nvmm_ioc_vcpu_getstate) +#define NVMM_IOC_VCPU_INJECT _IOW ('N', 9, struct nvmm_ioc_vcpu_inject) +#define NVMM_IOC_VCPU_RUN _IOWR('N', 10, struct nvmm_ioc_vcpu_run) +#define NVMM_IOC_GPA_MAP _IOW ('N', 11, struct nvmm_ioc_gpa_map) +#define NVMM_IOC_GPA_UNMAP _IOW ('N', 12, struct nvmm_ioc_gpa_unmap) +#define NVMM_IOC_HVA_MAP _IOW ('N', 13, struct nvmm_ioc_hva_map) +#define NVMM_IOC_HVA_UNMAP _IOW ('N', 14, struct nvmm_ioc_hva_unmap) #define NVMM_IOC_CTL _IOW ('N', 20, struct nvmm_ioc_ctl) Index: src/sys/dev/nvmm/x86/nvmm_x86.h diff -u src/sys/dev/nvmm/x86/nvmm_x86.h:1.15 src/sys/dev/nvmm/x86/nvmm_x86.h:1.16 --- src/sys/dev/nvmm/x86/nvmm_x86.h:1.15 Sat May 11 07:31:56 2019 +++ src/sys/dev/nvmm/x86/nvmm_x86.h Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm_x86.h,v 1.15 2019/05/11 07:31:56 maxv Exp $ */ +/* $NetBSD: nvmm_x86.h,v 1.16 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -32,26 +32,21 @@ #ifndef _NVMM_X86_H_ #define _NVMM_X86_H_ -/* --------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ #ifndef ASM_NVMM -struct nvmm_exit_memory { +struct nvmm_x86_exit_memory { int prot; gpaddr_t gpa; uint8_t inst_len; uint8_t inst_bytes[15]; }; -enum nvmm_exit_io_type { - NVMM_EXIT_IO_IN, - NVMM_EXIT_IO_OUT -}; - -struct nvmm_exit_io { - enum nvmm_exit_io_type type; +struct nvmm_x86_exit_io { + bool in; uint16_t port; - int seg; + int8_t seg; uint8_t address_size; uint8_t operand_size; bool rep; @@ -59,48 +54,84 @@ struct nvmm_exit_io { uint64_t npc; }; -enum nvmm_exit_msr_type { - NVMM_EXIT_MSR_RDMSR, - NVMM_EXIT_MSR_WRMSR +struct nvmm_x86_exit_rdmsr { + uint32_t msr; + uint64_t npc; }; -struct nvmm_exit_msr { - enum nvmm_exit_msr_type type; - uint64_t msr; +struct nvmm_x86_exit_wrmsr { + uint32_t msr; uint64_t val; uint64_t npc; }; -struct nvmm_exit_insn { +struct nvmm_x86_exit_insn { uint64_t npc; }; -struct nvmm_exit_invalid { +struct nvmm_x86_exit_invalid { uint64_t hwcode; }; -union nvmm_exit_md { - struct nvmm_exit_memory mem; - struct nvmm_exit_io io; - struct nvmm_exit_msr msr; - struct nvmm_exit_insn insn; - struct nvmm_exit_invalid inv; +/* Generic. */ +#define NVMM_VCPU_EXIT_NONE 0x0000000000000000ULL +#define NVMM_VCPU_EXIT_INVALID 0xFFFFFFFFFFFFFFFFULL +/* x86: operations. */ +#define NVMM_VCPU_EXIT_MEMORY 0x0000000000000001ULL +#define NVMM_VCPU_EXIT_IO 0x0000000000000002ULL +/* x86: changes in VCPU state. */ +#define NVMM_VCPU_EXIT_SHUTDOWN 0x0000000000001000ULL +#define NVMM_VCPU_EXIT_INT_READY 0x0000000000001001ULL +#define NVMM_VCPU_EXIT_NMI_READY 0x0000000000001002ULL +#define NVMM_VCPU_EXIT_HALTED 0x0000000000001003ULL +/* x86: instructions. */ +#define NVMM_VCPU_EXIT_RDMSR 0x0000000000002000ULL +#define NVMM_VCPU_EXIT_WRMSR 0x0000000000002001ULL +#define NVMM_VCPU_EXIT_MONITOR 0x0000000000002002ULL +#define NVMM_VCPU_EXIT_MWAIT 0x0000000000002003ULL +#define NVMM_VCPU_EXIT_CPUID 0x0000000000002004ULL + +struct nvmm_x86_exit { + uint64_t reason; + union { + struct nvmm_x86_exit_memory mem; + struct nvmm_x86_exit_io io; + struct nvmm_x86_exit_rdmsr rdmsr; + struct nvmm_x86_exit_wrmsr wrmsr; + struct nvmm_x86_exit_insn insn; + struct nvmm_x86_exit_invalid inv; + } u; + uint64_t exitstate[8]; +}; + +#define NVMM_VCPU_EVENT_EXCP 0 +#define NVMM_VCPU_EVENT_INTR 1 + +struct nvmm_x86_event { + u_int type; + uint8_t vector; + union { + struct { + uint64_t error; + } excp; + } u; }; -#define NVMM_EXIT_MONITOR 0x0000000000001000ULL -#define NVMM_EXIT_MWAIT 0x0000000000001001ULL -#define NVMM_EXIT_MWAIT_COND 0x0000000000001002ULL - struct nvmm_cap_md { uint64_t xcr0_mask; - uint64_t mxcsr_mask; - uint64_t conf_cpuid_maxops; - uint64_t rsvd[5]; + uint32_t mxcsr_mask; + uint32_t conf_cpuid_maxops; + uint64_t rsvd[6]; }; #endif -/* --------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +/* + * Segment state indexes. We use X64 as naming convention, not to confuse with + * X86 which originally implied 32bit. + */ /* Segments. */ #define NVMM_X64_SEG_ES 0 @@ -229,28 +260,43 @@ struct nvmm_x64_state { struct fxsave fpu; }; -#define nvmm_vcpu_state nvmm_x64_state +#define NVMM_VCPU_CONF_CPUID NVMM_VCPU_CONF_MD_BEGIN -#define NVMM_MACH_CONF_X86_CPUID NVMM_MACH_CONF_MD_BEGIN -#define NVMM_X86_NCONF 1 +struct nvmm_vcpu_conf_cpuid { + /* The options. */ + uint32_t mask:1; + uint32_t exit:1; + uint32_t rsvd:30; -struct nvmm_mach_conf_x86_cpuid { + /* The leaf. */ uint32_t leaf; - struct { - uint32_t eax; - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - } set; - struct { - uint32_t eax; - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - } del; -}; + + /* The params. */ + union { + struct { + struct { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } set; + struct { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } del; + } mask; + } u; +}; + +#define nvmm_vcpu_exit nvmm_x86_exit +#define nvmm_vcpu_event nvmm_x86_event +#define nvmm_vcpu_state nvmm_x64_state #ifdef _KERNEL +#define NVMM_X86_MACH_NCONF 0 +#define NVMM_X86_VCPU_NCONF 1 struct nvmm_x86_cpuid_mask { uint32_t eax; uint32_t ebx; Index: src/sys/dev/nvmm/x86/nvmm_x86_svm.c diff -u src/sys/dev/nvmm/x86/nvmm_x86_svm.c:1.50 src/sys/dev/nvmm/x86/nvmm_x86_svm.c:1.51 --- src/sys/dev/nvmm/x86/nvmm_x86_svm.c:1.50 Sat Oct 12 06:31:04 2019 +++ src/sys/dev/nvmm/x86/nvmm_x86_svm.c Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm_x86_svm.c,v 1.50 2019/10/12 06:31:04 maxv Exp $ */ +/* $NetBSD: nvmm_x86_svm.c,v 1.51 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: nvmm_x86_svm.c,v 1.50 2019/10/12 06:31:04 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nvmm_x86_svm.c,v 1.51 2019/10/23 07:01:11 maxv Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -504,14 +504,12 @@ static uint64_t svm_xcr0_mask __read_mos /* -------------------------------------------------------------------------- */ struct svm_machdata { - bool cpuidpresent[SVM_NCPUIDS]; - struct nvmm_mach_conf_x86_cpuid cpuid[SVM_NCPUIDS]; volatile uint64_t mach_htlb_gen; }; -static const size_t svm_conf_sizes[NVMM_X86_NCONF] = { - [NVMM_MACH_CONF_MD(NVMM_MACH_CONF_X86_CPUID)] = - sizeof(struct nvmm_mach_conf_x86_cpuid) +static const size_t svm_vcpu_conf_sizes[NVMM_X86_VCPU_NCONF] = { + [NVMM_VCPU_CONF_MD(NVMM_VCPU_CONF_CPUID)] = + sizeof(struct nvmm_vcpu_conf_cpuid) }; struct svm_cpudata { @@ -553,6 +551,10 @@ struct svm_cpudata { uint64_t drs[NVMM_X64_NDR]; uint64_t gtsc; struct xsave_header gfpu __aligned(64); + + /* VCPU configuration. */ + bool cpuidpresent[SVM_NCPUIDS]; + struct nvmm_vcpu_conf_cpuid cpuid[SVM_NCPUIDS]; }; static void @@ -651,7 +653,7 @@ svm_event_waitexit_disable(struct nvmm_c } static inline int -svm_event_has_error(uint64_t vector) +svm_event_has_error(uint8_t vector) { switch (vector) { case 8: /* #DF */ @@ -674,29 +676,18 @@ svm_vcpu_inject(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; struct svm_cpudata *cpudata = vcpu->cpudata; struct vmcb *vmcb = cpudata->vmcb; - enum nvmm_event_type evtype; - uint64_t vector, error; + u_int evtype; + uint8_t vector; + uint64_t error; int type = 0, err = 0; evtype = comm->event.type; vector = comm->event.vector; - error = comm->event.u.error; + error = comm->event.u.excp.error; __insn_barrier(); - if (__predict_false(vector >= 256)) { - return EINVAL; - } - switch (evtype) { - case NVMM_EVENT_INTERRUPT_HW: - type = SVM_EVENT_TYPE_HW_INT; - if (vector == 2) { - type = SVM_EVENT_TYPE_NMI; - svm_event_waitexit_enable(vcpu, true); - } - err = 0; - break; - case NVMM_EVENT_EXCEPTION: + case NVMM_VCPU_EVENT_EXCP: type = SVM_EVENT_TYPE_EXC; if (vector == 2 || vector >= 32) return EINVAL; @@ -704,16 +695,24 @@ svm_vcpu_inject(struct nvmm_cpu *vcpu) return EINVAL; err = svm_event_has_error(vector); break; + case NVMM_VCPU_EVENT_INTR: + type = SVM_EVENT_TYPE_HW_INT; + if (vector == 2) { + type = SVM_EVENT_TYPE_NMI; + svm_event_waitexit_enable(vcpu, true); + } + err = 0; + break; default: return EINVAL; } vmcb->ctrl.eventinj = - __SHIFTIN(vector, VMCB_CTRL_EVENTINJ_VECTOR) | - __SHIFTIN(type, VMCB_CTRL_EVENTINJ_TYPE) | - __SHIFTIN(err, VMCB_CTRL_EVENTINJ_EV) | - __SHIFTIN(1, VMCB_CTRL_EVENTINJ_V) | - __SHIFTIN(error, VMCB_CTRL_EVENTINJ_ERRORCODE); + __SHIFTIN((uint64_t)vector, VMCB_CTRL_EVENTINJ_VECTOR) | + __SHIFTIN((uint64_t)type, VMCB_CTRL_EVENTINJ_TYPE) | + __SHIFTIN((uint64_t)err, VMCB_CTRL_EVENTINJ_EV) | + __SHIFTIN((uint64_t)1, VMCB_CTRL_EVENTINJ_V) | + __SHIFTIN((uint64_t)error, VMCB_CTRL_EVENTINJ_ERRORCODE); cpudata->evt_pending = true; @@ -726,9 +725,9 @@ svm_inject_ud(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; int ret __diagused; - comm->event.type = NVMM_EVENT_EXCEPTION; + comm->event.type = NVMM_VCPU_EVENT_EXCP; comm->event.vector = 6; - comm->event.u.error = 0; + comm->event.u.excp.error = 0; ret = svm_vcpu_inject(vcpu); KASSERT(ret == 0); @@ -740,9 +739,9 @@ svm_inject_gp(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; int ret __diagused; - comm->event.type = NVMM_EVENT_EXCEPTION; + comm->event.type = NVMM_VCPU_EVENT_EXCP; comm->event.vector = 13; - comm->event.u.error = 0; + comm->event.u.excp.error = 0; ret = svm_vcpu_inject(vcpu); KASSERT(ret == 0); @@ -849,12 +848,18 @@ svm_inkernel_handle_cpuid(struct nvmm_cp } static void +svm_exit_insn(struct vmcb *vmcb, struct nvmm_vcpu_exit *exit, uint64_t reason) +{ + exit->u.insn.npc = vmcb->ctrl.nrip; + exit->reason = reason; +} + +static void svm_exit_cpuid(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { - struct svm_machdata *machdata = mach->machdata; struct svm_cpudata *cpudata = vcpu->cpudata; - struct nvmm_mach_conf_x86_cpuid *cpuid; + struct nvmm_vcpu_conf_cpuid *cpuid; uint64_t eax, ecx; u_int descs[4]; size_t i; @@ -871,36 +876,42 @@ svm_exit_cpuid(struct nvmm_machine *mach svm_inkernel_handle_cpuid(vcpu, eax, ecx); for (i = 0; i < SVM_NCPUIDS; i++) { - cpuid = &machdata->cpuid[i]; - if (!machdata->cpuidpresent[i]) { + if (!cpudata->cpuidpresent[i]) { continue; } + cpuid = &cpudata->cpuid[i]; if (cpuid->leaf != eax) { continue; } + if (cpuid->exit) { + svm_exit_insn(cpudata->vmcb, exit, NVMM_VCPU_EXIT_CPUID); + return; + } + KASSERT(cpuid->mask); + /* del */ - cpudata->vmcb->state.rax &= ~cpuid->del.eax; - cpudata->gprs[NVMM_X64_GPR_RBX] &= ~cpuid->del.ebx; - cpudata->gprs[NVMM_X64_GPR_RCX] &= ~cpuid->del.ecx; - cpudata->gprs[NVMM_X64_GPR_RDX] &= ~cpuid->del.edx; + cpudata->vmcb->state.rax &= ~cpuid->u.mask.del.eax; + cpudata->gprs[NVMM_X64_GPR_RBX] &= ~cpuid->u.mask.del.ebx; + cpudata->gprs[NVMM_X64_GPR_RCX] &= ~cpuid->u.mask.del.ecx; + cpudata->gprs[NVMM_X64_GPR_RDX] &= ~cpuid->u.mask.del.edx; /* set */ - cpudata->vmcb->state.rax |= cpuid->set.eax; - cpudata->gprs[NVMM_X64_GPR_RBX] |= cpuid->set.ebx; - cpudata->gprs[NVMM_X64_GPR_RCX] |= cpuid->set.ecx; - cpudata->gprs[NVMM_X64_GPR_RDX] |= cpuid->set.edx; + cpudata->vmcb->state.rax |= cpuid->u.mask.set.eax; + cpudata->gprs[NVMM_X64_GPR_RBX] |= cpuid->u.mask.set.ebx; + cpudata->gprs[NVMM_X64_GPR_RCX] |= cpuid->u.mask.set.ecx; + cpudata->gprs[NVMM_X64_GPR_RDX] |= cpuid->u.mask.set.edx; break; } svm_inkernel_advance(cpudata->vmcb); - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; } static void svm_exit_hlt(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; struct vmcb *vmcb = cpudata->vmcb; @@ -910,7 +921,7 @@ svm_exit_hlt(struct nvmm_machine *mach, } svm_inkernel_advance(cpudata->vmcb); - exit->reason = NVMM_EXIT_HALTED; + exit->reason = NVMM_VCPU_EXIT_HALTED; } #define SVM_EXIT_IO_PORT __BITS(31,16) @@ -927,20 +938,15 @@ svm_exit_hlt(struct nvmm_machine *mach, static void svm_exit_io(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; uint64_t info = cpudata->vmcb->ctrl.exitinfo1; uint64_t nextpc = cpudata->vmcb->ctrl.exitinfo2; - exit->reason = NVMM_EXIT_IO; - - if (info & SVM_EXIT_IO_IN) { - exit->u.io.type = NVMM_EXIT_IO_IN; - } else { - exit->u.io.type = NVMM_EXIT_IO_OUT; - } + exit->reason = NVMM_VCPU_EXIT_IO; + exit->u.io.in = (info & SVM_EXIT_IO_IN) != 0; exit->u.io.port = __SHIFTOUT(info, SVM_EXIT_IO_PORT); if (svm_decode_assist) { @@ -984,54 +990,51 @@ static const uint64_t msr_ignore_list[] static bool svm_inkernel_handle_msr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; struct vmcb *vmcb = cpudata->vmcb; uint64_t val; size_t i; - switch (exit->u.msr.type) { - case NVMM_EXIT_MSR_RDMSR: - if (exit->u.msr.msr == MSR_NB_CFG) { + if (exit->reason == NVMM_VCPU_EXIT_RDMSR) { + if (exit->u.rdmsr.msr == MSR_NB_CFG) { val = NB_CFG_INITAPICCPUIDLO; vmcb->state.rax = (val & 0xFFFFFFFF); cpudata->gprs[NVMM_X64_GPR_RDX] = (val >> 32); goto handled; } for (i = 0; i < __arraycount(msr_ignore_list); i++) { - if (msr_ignore_list[i] != exit->u.msr.msr) + if (msr_ignore_list[i] != exit->u.rdmsr.msr) continue; val = 0; vmcb->state.rax = (val & 0xFFFFFFFF); cpudata->gprs[NVMM_X64_GPR_RDX] = (val >> 32); goto handled; } - break; - case NVMM_EXIT_MSR_WRMSR: - if (exit->u.msr.msr == MSR_EFER) { - if (__predict_false(exit->u.msr.val & ~EFER_VALID)) { + } else { + if (exit->u.wrmsr.msr == MSR_EFER) { + if (__predict_false(exit->u.wrmsr.val & ~EFER_VALID)) { goto error; } - if ((vmcb->state.efer ^ exit->u.msr.val) & + if ((vmcb->state.efer ^ exit->u.wrmsr.val) & EFER_TLB_FLUSH) { cpudata->gtlb_want_flush = true; } - vmcb->state.efer = exit->u.msr.val | EFER_SVME; + vmcb->state.efer = exit->u.wrmsr.val | EFER_SVME; svm_vmcb_cache_flush(vmcb, VMCB_CTRL_VMCB_CLEAN_CR); goto handled; } - if (exit->u.msr.msr == MSR_TSC) { - cpudata->gtsc = exit->u.msr.val; + if (exit->u.wrmsr.msr == MSR_TSC) { + cpudata->gtsc = exit->u.wrmsr.val; cpudata->gtsc_want_update = true; goto handled; } for (i = 0; i < __arraycount(msr_ignore_list); i++) { - if (msr_ignore_list[i] != exit->u.msr.msr) + if (msr_ignore_list[i] != exit->u.wrmsr.msr) continue; goto handled; } - break; } return false; @@ -1045,49 +1048,69 @@ error: return true; } -static void -svm_exit_msr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) +static inline void +svm_exit_rdmsr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; - uint64_t info = cpudata->vmcb->ctrl.exitinfo1; - if (info == 0) { - exit->u.msr.type = NVMM_EXIT_MSR_RDMSR; - } else { - exit->u.msr.type = NVMM_EXIT_MSR_WRMSR; + exit->reason = NVMM_VCPU_EXIT_RDMSR; + exit->u.rdmsr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + exit->u.rdmsr.npc = cpudata->vmcb->ctrl.nrip; + + if (svm_inkernel_handle_msr(mach, vcpu, exit)) { + exit->reason = NVMM_VCPU_EXIT_NONE; + return; } - exit->u.msr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + svm_vcpu_state_provide(vcpu, NVMM_X64_STATE_GPRS); +} - if (info == 1) { - uint64_t rdx, rax; - rdx = cpudata->gprs[NVMM_X64_GPR_RDX]; - rax = cpudata->vmcb->state.rax; - exit->u.msr.val = (rdx << 32) | (rax & 0xFFFFFFFF); - } else { - exit->u.msr.val = 0; - } +static inline void +svm_exit_wrmsr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, + struct nvmm_vcpu_exit *exit) +{ + struct svm_cpudata *cpudata = vcpu->cpudata; + uint64_t rdx, rax; + + rdx = cpudata->gprs[NVMM_X64_GPR_RDX]; + rax = cpudata->vmcb->state.rax; + + exit->reason = NVMM_VCPU_EXIT_WRMSR; + exit->u.wrmsr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + exit->u.wrmsr.val = (rdx << 32) | (rax & 0xFFFFFFFF); + exit->u.wrmsr.npc = cpudata->vmcb->ctrl.nrip; if (svm_inkernel_handle_msr(mach, vcpu, exit)) { - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; return; } - exit->reason = NVMM_EXIT_MSR; - exit->u.msr.npc = cpudata->vmcb->ctrl.nrip; - svm_vcpu_state_provide(vcpu, NVMM_X64_STATE_GPRS); } static void +svm_exit_msr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, + struct nvmm_vcpu_exit *exit) +{ + struct svm_cpudata *cpudata = vcpu->cpudata; + uint64_t info = cpudata->vmcb->ctrl.exitinfo1; + + if (info == 0) { + svm_exit_rdmsr(mach, vcpu, exit); + } else { + svm_exit_wrmsr(mach, vcpu, exit); + } +} + +static void svm_exit_npf(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; gpaddr_t gpa = cpudata->vmcb->ctrl.exitinfo2; - exit->reason = NVMM_EXIT_MEMORY; + exit->reason = NVMM_VCPU_EXIT_MEMORY; if (cpudata->vmcb->ctrl.exitinfo1 & PGEX_W) exit->u.mem.prot = PROT_WRITE; else if (cpudata->vmcb->ctrl.exitinfo1 & PGEX_X) @@ -1105,21 +1128,14 @@ svm_exit_npf(struct nvmm_machine *mach, } static void -svm_exit_insn(struct vmcb *vmcb, struct nvmm_exit *exit, uint64_t reason) -{ - exit->u.insn.npc = vmcb->ctrl.nrip; - exit->reason = reason; -} - -static void svm_exit_xsetbv(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct svm_cpudata *cpudata = vcpu->cpudata; struct vmcb *vmcb = cpudata->vmcb; uint64_t val; - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; val = (cpudata->gprs[NVMM_X64_GPR_RDX] << 32) | (vmcb->state.rax & 0xFFFFFFFF); @@ -1147,10 +1163,10 @@ error: } static void -svm_exit_invalid(struct nvmm_exit *exit, uint64_t code) +svm_exit_invalid(struct nvmm_vcpu_exit *exit, uint64_t code) { exit->u.inv.hwcode = code; - exit->reason = NVMM_EXIT_INVALID; + exit->reason = NVMM_VCPU_EXIT_INVALID; } /* -------------------------------------------------------------------------- */ @@ -1293,7 +1309,7 @@ svm_exit_evt(struct svm_cpudata *cpudata static int svm_vcpu_run(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct nvmm_comm_page *comm = vcpu->comm; struct svm_machdata *machdata = mach->machdata; @@ -1353,15 +1369,15 @@ svm_vcpu_run(struct nvmm_machine *mach, switch (vmcb->ctrl.exitcode) { case VMCB_EXITCODE_INTR: case VMCB_EXITCODE_NMI: - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; break; case VMCB_EXITCODE_VINTR: svm_event_waitexit_disable(vcpu, false); - exit->reason = NVMM_EXIT_INT_READY; + exit->reason = NVMM_VCPU_EXIT_INT_READY; break; case VMCB_EXITCODE_IRET: svm_event_waitexit_disable(vcpu, true); - exit->reason = NVMM_EXIT_NMI_READY; + exit->reason = NVMM_VCPU_EXIT_NMI_READY; break; case VMCB_EXITCODE_CPUID: svm_exit_cpuid(mach, vcpu, exit); @@ -1376,7 +1392,7 @@ svm_vcpu_run(struct nvmm_machine *mach, svm_exit_msr(mach, vcpu, exit); break; case VMCB_EXITCODE_SHUTDOWN: - exit->reason = NVMM_EXIT_SHUTDOWN; + exit->reason = NVMM_VCPU_EXIT_SHUTDOWN; break; case VMCB_EXITCODE_RDPMC: case VMCB_EXITCODE_RSM: @@ -1390,16 +1406,14 @@ svm_vcpu_run(struct nvmm_machine *mach, case VMCB_EXITCODE_SKINIT: case VMCB_EXITCODE_RDTSCP: svm_inject_ud(vcpu); - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; break; case VMCB_EXITCODE_MONITOR: - svm_exit_insn(vmcb, exit, NVMM_EXIT_MONITOR); + svm_exit_insn(vmcb, exit, NVMM_VCPU_EXIT_MONITOR); break; case VMCB_EXITCODE_MWAIT: - svm_exit_insn(vmcb, exit, NVMM_EXIT_MWAIT); - break; case VMCB_EXITCODE_MWAIT_CONDITIONAL: - svm_exit_insn(vmcb, exit, NVMM_EXIT_MWAIT_COND); + svm_exit_insn(vmcb, exit, NVMM_VCPU_EXIT_MWAIT); break; case VMCB_EXITCODE_XSETBV: svm_exit_xsetbv(mach, vcpu, exit); @@ -1423,7 +1437,7 @@ svm_vcpu_run(struct nvmm_machine *mach, if (curlwp->l_flag & LW_USERRET) { break; } - if (exit->reason != NVMM_EXIT_NONE) { + if (exit->reason != NVMM_VCPU_EXIT_NONE) { break; } } @@ -2114,6 +2128,67 @@ svm_vcpu_destroy(struct nvmm_machine *ma roundup(sizeof(*cpudata), PAGE_SIZE), UVM_KMF_WIRED); } +static int +svm_vcpu_configure(struct nvmm_cpu *vcpu, uint64_t op, void *data) +{ + struct svm_cpudata *cpudata = vcpu->cpudata; + struct nvmm_vcpu_conf_cpuid *cpuid; + size_t i; + + if (__predict_false(op != NVMM_VCPU_CONF_MD(NVMM_VCPU_CONF_CPUID))) { + return EINVAL; + } + cpuid = data; + + if (__predict_false(cpuid->mask && cpuid->exit)) { + return EINVAL; + } + if (__predict_false(cpuid->mask && + ((cpuid->u.mask.set.eax & cpuid->u.mask.del.eax) || + (cpuid->u.mask.set.ebx & cpuid->u.mask.del.ebx) || + (cpuid->u.mask.set.ecx & cpuid->u.mask.del.ecx) || + (cpuid->u.mask.set.edx & cpuid->u.mask.del.edx)))) { + return EINVAL; + } + + /* If unset, delete, to restore the default behavior. */ + if (!cpuid->mask && !cpuid->exit) { + for (i = 0; i < SVM_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + continue; + } + if (cpudata->cpuid[i].leaf == cpuid->leaf) { + cpudata->cpuidpresent[i] = false; + } + } + return 0; + } + + /* If already here, replace. */ + for (i = 0; i < SVM_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + continue; + } + if (cpudata->cpuid[i].leaf == cpuid->leaf) { + memcpy(&cpudata->cpuid[i], cpuid, + sizeof(struct nvmm_vcpu_conf_cpuid)); + return 0; + } + } + + /* Not here, insert. */ + for (i = 0; i < SVM_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + cpudata->cpuidpresent[i] = true; + memcpy(&cpudata->cpuid[i], cpuid, + sizeof(struct nvmm_vcpu_conf_cpuid)); + return 0; + } + } + + return ENOBUFS; +} + /* -------------------------------------------------------------------------- */ static void @@ -2153,44 +2228,7 @@ svm_machine_destroy(struct nvmm_machine static int svm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *data) { - struct nvmm_mach_conf_x86_cpuid *cpuid = data; - struct svm_machdata *machdata = (struct svm_machdata *)mach->machdata; - size_t i; - - if (__predict_false(op != NVMM_MACH_CONF_MD(NVMM_MACH_CONF_X86_CPUID))) { - return EINVAL; - } - - if (__predict_false((cpuid->set.eax & cpuid->del.eax) || - (cpuid->set.ebx & cpuid->del.ebx) || - (cpuid->set.ecx & cpuid->del.ecx) || - (cpuid->set.edx & cpuid->del.edx))) { - return EINVAL; - } - - /* If already here, replace. */ - for (i = 0; i < SVM_NCPUIDS; i++) { - if (!machdata->cpuidpresent[i]) { - continue; - } - if (machdata->cpuid[i].leaf == cpuid->leaf) { - memcpy(&machdata->cpuid[i], cpuid, - sizeof(struct nvmm_mach_conf_x86_cpuid)); - return 0; - } - } - - /* Not here, insert. */ - for (i = 0; i < SVM_NCPUIDS; i++) { - if (!machdata->cpuidpresent[i]) { - machdata->cpuidpresent[i] = true; - memcpy(&machdata->cpuid[i], cpuid, - sizeof(struct nvmm_mach_conf_x86_cpuid)); - return 0; - } - } - - return ENOBUFS; + panic("%s: impossible", __func__); } /* -------------------------------------------------------------------------- */ @@ -2359,14 +2397,17 @@ const struct nvmm_impl nvmm_x86_svm = { .init = svm_init, .fini = svm_fini, .capability = svm_capability, - .conf_max = NVMM_X86_NCONF, - .conf_sizes = svm_conf_sizes, + .mach_conf_max = NVMM_X86_MACH_NCONF, + .mach_conf_sizes = NULL, + .vcpu_conf_max = NVMM_X86_VCPU_NCONF, + .vcpu_conf_sizes = svm_vcpu_conf_sizes, .state_size = sizeof(struct nvmm_x64_state), .machine_create = svm_machine_create, .machine_destroy = svm_machine_destroy, .machine_configure = svm_machine_configure, .vcpu_create = svm_vcpu_create, .vcpu_destroy = svm_vcpu_destroy, + .vcpu_configure = svm_vcpu_configure, .vcpu_setstate = svm_vcpu_setstate, .vcpu_getstate = svm_vcpu_getstate, .vcpu_inject = svm_vcpu_inject, Index: src/sys/dev/nvmm/x86/nvmm_x86_vmx.c diff -u src/sys/dev/nvmm/x86/nvmm_x86_vmx.c:1.39 src/sys/dev/nvmm/x86/nvmm_x86_vmx.c:1.40 --- src/sys/dev/nvmm/x86/nvmm_x86_vmx.c:1.39 Sat Oct 12 06:31:04 2019 +++ src/sys/dev/nvmm/x86/nvmm_x86_vmx.c Wed Oct 23 07:01:11 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: nvmm_x86_vmx.c,v 1.39 2019/10/12 06:31:04 maxv Exp $ */ +/* $NetBSD: nvmm_x86_vmx.c,v 1.40 2019/10/23 07:01:11 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: nvmm_x86_vmx.c,v 1.39 2019/10/12 06:31:04 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nvmm_x86_vmx.c,v 1.40 2019/10/23 07:01:11 maxv Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -697,14 +697,12 @@ static uint64_t vmx_xcr0_mask __read_mos /* -------------------------------------------------------------------------- */ struct vmx_machdata { - bool cpuidpresent[VMX_NCPUIDS]; - struct nvmm_mach_conf_x86_cpuid cpuid[VMX_NCPUIDS]; volatile uint64_t mach_htlb_gen; }; -static const size_t vmx_conf_sizes[NVMM_X86_NCONF] = { - [NVMM_MACH_CONF_MD(NVMM_MACH_CONF_X86_CPUID)] = - sizeof(struct nvmm_mach_conf_x86_cpuid) +static const size_t vmx_vcpu_conf_sizes[NVMM_X86_VCPU_NCONF] = { + [NVMM_VCPU_CONF_MD(NVMM_VCPU_CONF_CPUID)] = + sizeof(struct nvmm_vcpu_conf_cpuid) }; struct vmx_cpudata { @@ -750,6 +748,10 @@ struct vmx_cpudata { uint64_t drs[NVMM_X64_NDR]; uint64_t gtsc; struct xsave_header gfpu __aligned(64); + + /* VCPU configuration. */ + bool cpuidpresent[VMX_NCPUIDS]; + struct nvmm_vcpu_conf_cpuid cpuid[VMX_NCPUIDS]; }; static const struct { @@ -973,7 +975,7 @@ vmx_event_waitexit_disable(struct nvmm_c } static inline int -vmx_event_has_error(uint64_t vector) +vmx_event_has_error(uint8_t vector) { switch (vector) { case 8: /* #DF */ @@ -996,30 +998,19 @@ vmx_vcpu_inject(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; struct vmx_cpudata *cpudata = vcpu->cpudata; int type = 0, err = 0, ret = EINVAL; - enum nvmm_event_type evtype; - uint64_t info, vector, error; + u_int evtype; + uint8_t vector; + uint64_t info, error; evtype = comm->event.type; vector = comm->event.vector; - error = comm->event.u.error; + error = comm->event.u.excp.error; __insn_barrier(); - if (__predict_false(vector >= 256)) { - return EINVAL; - } - vmx_vmcs_enter(vcpu); switch (evtype) { - case NVMM_EVENT_INTERRUPT_HW: - type = INTR_TYPE_EXT_INT; - if (vector == 2) { - type = INTR_TYPE_NMI; - vmx_event_waitexit_enable(vcpu, true); - } - err = 0; - break; - case NVMM_EVENT_EXCEPTION: + case NVMM_VCPU_EVENT_EXCP: if (vector == 2 || vector >= 32) goto out; if (vector == 3 || vector == 0) @@ -1027,15 +1018,23 @@ vmx_vcpu_inject(struct nvmm_cpu *vcpu) type = INTR_TYPE_HW_EXC; err = vmx_event_has_error(vector); break; + case NVMM_VCPU_EVENT_INTR: + type = INTR_TYPE_EXT_INT; + if (vector == 2) { + type = INTR_TYPE_NMI; + vmx_event_waitexit_enable(vcpu, true); + } + err = 0; + break; default: goto out; } info = - __SHIFTIN(vector, INTR_INFO_VECTOR) | - __SHIFTIN(type, INTR_INFO_TYPE) | - __SHIFTIN(err, INTR_INFO_ERROR) | - __SHIFTIN(1, INTR_INFO_VALID); + __SHIFTIN((uint64_t)vector, INTR_INFO_VECTOR) | + __SHIFTIN((uint64_t)type, INTR_INFO_TYPE) | + __SHIFTIN((uint64_t)err, INTR_INFO_ERROR) | + __SHIFTIN((uint64_t)1, INTR_INFO_VALID); vmx_vmwrite(VMCS_ENTRY_INTR_INFO, info); vmx_vmwrite(VMCS_ENTRY_EXCEPTION_ERROR, error); @@ -1053,9 +1052,9 @@ vmx_inject_ud(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; int ret __diagused; - comm->event.type = NVMM_EVENT_EXCEPTION; + comm->event.type = NVMM_VCPU_EVENT_EXCP; comm->event.vector = 6; - comm->event.u.error = 0; + comm->event.u.excp.error = 0; ret = vmx_vcpu_inject(vcpu); KASSERT(ret == 0); @@ -1067,9 +1066,9 @@ vmx_inject_gp(struct nvmm_cpu *vcpu) struct nvmm_comm_page *comm = vcpu->comm; int ret __diagused; - comm->event.type = NVMM_EVENT_EXCEPTION; + comm->event.type = NVMM_VCPU_EVENT_EXCP; comm->event.vector = 13; - comm->event.u.error = 0; + comm->event.u.excp.error = 0; ret = vmx_vcpu_inject(vcpu); KASSERT(ret == 0); @@ -1104,15 +1103,15 @@ vmx_inkernel_advance(void) } static void -vmx_exit_invalid(struct nvmm_exit *exit, uint64_t code) +vmx_exit_invalid(struct nvmm_vcpu_exit *exit, uint64_t code) { exit->u.inv.hwcode = code; - exit->reason = NVMM_EXIT_INVALID; + exit->reason = NVMM_VCPU_EXIT_INVALID; } static void vmx_exit_exc_nmi(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { uint64_t qual; @@ -1125,7 +1124,7 @@ vmx_exit_exc_nmi(struct nvmm_machine *ma goto error; } - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; return; error: @@ -1211,12 +1210,22 @@ vmx_inkernel_handle_cpuid(struct nvmm_cp } static void +vmx_exit_insn(struct nvmm_vcpu_exit *exit, uint64_t reason) +{ + uint64_t inslen, rip; + + inslen = vmx_vmread(VMCS_EXIT_INSTRUCTION_LENGTH); + rip = vmx_vmread(VMCS_GUEST_RIP); + exit->u.insn.npc = rip + inslen; + exit->reason = reason; +} + +static void vmx_exit_cpuid(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { - struct vmx_machdata *machdata = mach->machdata; struct vmx_cpudata *cpudata = vcpu->cpudata; - struct nvmm_mach_conf_x86_cpuid *cpuid; + struct nvmm_vcpu_conf_cpuid *cpuid; uint64_t eax, ecx; u_int descs[4]; size_t i; @@ -1233,36 +1242,42 @@ vmx_exit_cpuid(struct nvmm_machine *mach vmx_inkernel_handle_cpuid(vcpu, eax, ecx); for (i = 0; i < VMX_NCPUIDS; i++) { - cpuid = &machdata->cpuid[i]; - if (!machdata->cpuidpresent[i]) { + if (!cpudata->cpuidpresent[i]) { continue; } + cpuid = &cpudata->cpuid[i]; if (cpuid->leaf != eax) { continue; } + if (cpuid->exit) { + vmx_exit_insn(exit, NVMM_VCPU_EXIT_CPUID); + return; + } + KASSERT(cpuid->mask); + /* del */ - cpudata->gprs[NVMM_X64_GPR_RAX] &= ~cpuid->del.eax; - cpudata->gprs[NVMM_X64_GPR_RBX] &= ~cpuid->del.ebx; - cpudata->gprs[NVMM_X64_GPR_RCX] &= ~cpuid->del.ecx; - cpudata->gprs[NVMM_X64_GPR_RDX] &= ~cpuid->del.edx; + cpudata->gprs[NVMM_X64_GPR_RAX] &= ~cpuid->u.mask.del.eax; + cpudata->gprs[NVMM_X64_GPR_RBX] &= ~cpuid->u.mask.del.ebx; + cpudata->gprs[NVMM_X64_GPR_RCX] &= ~cpuid->u.mask.del.ecx; + cpudata->gprs[NVMM_X64_GPR_RDX] &= ~cpuid->u.mask.del.edx; /* set */ - cpudata->gprs[NVMM_X64_GPR_RAX] |= cpuid->set.eax; - cpudata->gprs[NVMM_X64_GPR_RBX] |= cpuid->set.ebx; - cpudata->gprs[NVMM_X64_GPR_RCX] |= cpuid->set.ecx; - cpudata->gprs[NVMM_X64_GPR_RDX] |= cpuid->set.edx; + cpudata->gprs[NVMM_X64_GPR_RAX] |= cpuid->u.mask.set.eax; + cpudata->gprs[NVMM_X64_GPR_RBX] |= cpuid->u.mask.set.ebx; + cpudata->gprs[NVMM_X64_GPR_RCX] |= cpuid->u.mask.set.ecx; + cpudata->gprs[NVMM_X64_GPR_RDX] |= cpuid->u.mask.set.edx; break; } vmx_inkernel_advance(); - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; } static void vmx_exit_hlt(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct vmx_cpudata *cpudata = vcpu->cpudata; uint64_t rflags; @@ -1275,7 +1290,7 @@ vmx_exit_hlt(struct nvmm_machine *mach, } vmx_inkernel_advance(); - exit->reason = NVMM_EXIT_HALTED; + exit->reason = NVMM_VCPU_EXIT_HALTED; } #define VMX_QUAL_CR_NUM __BITS(3,0) @@ -1427,7 +1442,7 @@ vmx_inkernel_handle_cr8(struct nvmm_mach static void vmx_exit_cr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { uint64_t qual; int ret; @@ -1453,7 +1468,7 @@ vmx_exit_cr(struct nvmm_machine *mach, s vmx_inject_gp(vcpu); } - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; } #define VMX_QUAL_IO_SIZE __BITS(2,0) @@ -1474,21 +1489,16 @@ vmx_exit_cr(struct nvmm_machine *mach, s static void vmx_exit_io(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { uint64_t qual, info, inslen, rip; qual = vmx_vmread(VMCS_EXIT_QUALIFICATION); info = vmx_vmread(VMCS_EXIT_INSTRUCTION_INFO); - exit->reason = NVMM_EXIT_IO; - - if (qual & VMX_QUAL_IO_IN) { - exit->u.io.type = NVMM_EXIT_IO_IN; - } else { - exit->u.io.type = NVMM_EXIT_IO_OUT; - } + exit->reason = NVMM_VCPU_EXIT_IO; + exit->u.io.in = (qual & VMX_QUAL_IO_IN) != 0; exit->u.io.port = __SHIFTOUT(qual, VMX_QUAL_IO_PORT); KASSERT(__SHIFTOUT(info, VMX_INFO_IO_SEG) < 6); @@ -1513,7 +1523,7 @@ vmx_exit_io(struct nvmm_machine *mach, s exit->u.io.rep = (qual & VMX_QUAL_IO_REP) != 0; exit->u.io.str = (qual & VMX_QUAL_IO_STR) != 0; - if ((exit->u.io.type == NVMM_EXIT_IO_IN) && exit->u.io.str) { + if (exit->u.io.in && exit->u.io.str) { exit->u.io.seg = NVMM_X64_SEG_ES; } @@ -1533,59 +1543,56 @@ static const uint64_t msr_ignore_list[] static bool vmx_inkernel_handle_msr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct vmx_cpudata *cpudata = vcpu->cpudata; uint64_t val; size_t i; - switch (exit->u.msr.type) { - case NVMM_EXIT_MSR_RDMSR: - if (exit->u.msr.msr == MSR_CR_PAT) { + if (exit->reason == NVMM_VCPU_EXIT_RDMSR) { + if (exit->u.rdmsr.msr == MSR_CR_PAT) { val = vmx_vmread(VMCS_GUEST_IA32_PAT); cpudata->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF); cpudata->gprs[NVMM_X64_GPR_RDX] = (val >> 32); goto handled; } - if (exit->u.msr.msr == MSR_MISC_ENABLE) { + if (exit->u.rdmsr.msr == MSR_MISC_ENABLE) { val = cpudata->gmsr_misc_enable; cpudata->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF); cpudata->gprs[NVMM_X64_GPR_RDX] = (val >> 32); goto handled; } for (i = 0; i < __arraycount(msr_ignore_list); i++) { - if (msr_ignore_list[i] != exit->u.msr.msr) + if (msr_ignore_list[i] != exit->u.rdmsr.msr) continue; val = 0; cpudata->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF); cpudata->gprs[NVMM_X64_GPR_RDX] = (val >> 32); goto handled; } - break; - case NVMM_EXIT_MSR_WRMSR: - if (exit->u.msr.msr == MSR_TSC) { - cpudata->gtsc = exit->u.msr.val; + } else { + if (exit->u.wrmsr.msr == MSR_TSC) { + cpudata->gtsc = exit->u.wrmsr.val; cpudata->gtsc_want_update = true; goto handled; } - if (exit->u.msr.msr == MSR_CR_PAT) { - val = exit->u.msr.val; + if (exit->u.wrmsr.msr == MSR_CR_PAT) { + val = exit->u.wrmsr.val; if (__predict_false(!nvmm_x86_pat_validate(val))) { goto error; } vmx_vmwrite(VMCS_GUEST_IA32_PAT, val); goto handled; } - if (exit->u.msr.msr == MSR_MISC_ENABLE) { + if (exit->u.wrmsr.msr == MSR_MISC_ENABLE) { /* Don't care. */ goto handled; } for (i = 0; i < __arraycount(msr_ignore_list); i++) { - if (msr_ignore_list[i] != exit->u.msr.msr) + if (msr_ignore_list[i] != exit->u.wrmsr.msr) continue; goto handled; } - break; } return false; @@ -1600,50 +1607,61 @@ error: } static void -vmx_exit_msr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit, bool rdmsr) +vmx_exit_rdmsr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, + struct nvmm_vcpu_exit *exit) { struct vmx_cpudata *cpudata = vcpu->cpudata; uint64_t inslen, rip; - if (rdmsr) { - exit->u.msr.type = NVMM_EXIT_MSR_RDMSR; - } else { - exit->u.msr.type = NVMM_EXIT_MSR_WRMSR; + exit->reason = NVMM_VCPU_EXIT_RDMSR; + exit->u.rdmsr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + + if (vmx_inkernel_handle_msr(mach, vcpu, exit)) { + exit->reason = NVMM_VCPU_EXIT_NONE; + return; } - exit->u.msr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + inslen = vmx_vmread(VMCS_EXIT_INSTRUCTION_LENGTH); + rip = vmx_vmread(VMCS_GUEST_RIP); + exit->u.rdmsr.npc = rip + inslen; - if (rdmsr) { - exit->u.msr.val = 0; - } else { - uint64_t rdx, rax; - rdx = cpudata->gprs[NVMM_X64_GPR_RDX]; - rax = cpudata->gprs[NVMM_X64_GPR_RAX]; - exit->u.msr.val = (rdx << 32) | (rax & 0xFFFFFFFF); - } + vmx_vcpu_state_provide(vcpu, NVMM_X64_STATE_GPRS); +} + +static void +vmx_exit_wrmsr(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, + struct nvmm_vcpu_exit *exit) +{ + struct vmx_cpudata *cpudata = vcpu->cpudata; + uint64_t rdx, rax, inslen, rip; + + rdx = cpudata->gprs[NVMM_X64_GPR_RDX]; + rax = cpudata->gprs[NVMM_X64_GPR_RAX]; + + exit->reason = NVMM_VCPU_EXIT_WRMSR; + exit->u.wrmsr.msr = (cpudata->gprs[NVMM_X64_GPR_RCX] & 0xFFFFFFFF); + exit->u.wrmsr.val = (rdx << 32) | (rax & 0xFFFFFFFF); if (vmx_inkernel_handle_msr(mach, vcpu, exit)) { - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; return; } - exit->reason = NVMM_EXIT_MSR; inslen = vmx_vmread(VMCS_EXIT_INSTRUCTION_LENGTH); rip = vmx_vmread(VMCS_GUEST_RIP); - exit->u.msr.npc = rip + inslen; + exit->u.wrmsr.npc = rip + inslen; vmx_vcpu_state_provide(vcpu, NVMM_X64_STATE_GPRS); } static void vmx_exit_xsetbv(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct vmx_cpudata *cpudata = vcpu->cpudata; uint16_t val; - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; val = (cpudata->gprs[NVMM_X64_GPR_RDX] << 32) | (cpudata->gprs[NVMM_X64_GPR_RAX] & 0xFFFFFFFF); @@ -1674,14 +1692,14 @@ error: static void vmx_exit_epf(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { uint64_t perm; gpaddr_t gpa; gpa = vmx_vmread(VMCS_GUEST_PHYSICAL_ADDRESS); - exit->reason = NVMM_EXIT_MEMORY; + exit->reason = NVMM_VCPU_EXIT_MEMORY; perm = vmx_vmread(VMCS_EXIT_QUALIFICATION); if (perm & VMX_EPT_VIOLATION_WRITE) exit->u.mem.prot = PROT_WRITE; @@ -1865,7 +1883,7 @@ vmx_exit_evt(struct vmx_cpudata *cpudata static int vmx_vcpu_run(struct nvmm_machine *mach, struct nvmm_cpu *vcpu, - struct nvmm_exit *exit) + struct nvmm_vcpu_exit *exit) { struct nvmm_comm_page *comm = vcpu->comm; struct vmx_machdata *machdata = mach->machdata; @@ -1948,7 +1966,7 @@ vmx_vcpu_run(struct nvmm_machine *mach, vmx_exit_exc_nmi(mach, vcpu, exit); break; case VMCS_EXITCODE_EXT_INT: - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; break; case VMCS_EXITCODE_CPUID: vmx_exit_cpuid(mach, vcpu, exit); @@ -1963,19 +1981,19 @@ vmx_vcpu_run(struct nvmm_machine *mach, vmx_exit_io(mach, vcpu, exit); break; case VMCS_EXITCODE_RDMSR: - vmx_exit_msr(mach, vcpu, exit, true); + vmx_exit_rdmsr(mach, vcpu, exit); break; case VMCS_EXITCODE_WRMSR: - vmx_exit_msr(mach, vcpu, exit, false); + vmx_exit_wrmsr(mach, vcpu, exit); break; case VMCS_EXITCODE_SHUTDOWN: - exit->reason = NVMM_EXIT_SHUTDOWN; + exit->reason = NVMM_VCPU_EXIT_SHUTDOWN; break; case VMCS_EXITCODE_MONITOR: - exit->reason = NVMM_EXIT_MONITOR; + vmx_exit_insn(exit, NVMM_VCPU_EXIT_MONITOR); break; case VMCS_EXITCODE_MWAIT: - exit->reason = NVMM_EXIT_MWAIT; + vmx_exit_insn(exit, NVMM_VCPU_EXIT_MWAIT); break; case VMCS_EXITCODE_XSETBV: vmx_exit_xsetbv(mach, vcpu, exit); @@ -1995,18 +2013,18 @@ vmx_vcpu_run(struct nvmm_machine *mach, case VMCS_EXITCODE_VMXOFF: case VMCS_EXITCODE_VMXON: vmx_inject_ud(vcpu); - exit->reason = NVMM_EXIT_NONE; + exit->reason = NVMM_VCPU_EXIT_NONE; break; case VMCS_EXITCODE_EPT_VIOLATION: vmx_exit_epf(mach, vcpu, exit); break; case VMCS_EXITCODE_INT_WINDOW: vmx_event_waitexit_disable(vcpu, false); - exit->reason = NVMM_EXIT_INT_READY; + exit->reason = NVMM_VCPU_EXIT_INT_READY; break; case VMCS_EXITCODE_NMI_WINDOW: vmx_event_waitexit_disable(vcpu, true); - exit->reason = NVMM_EXIT_NMI_READY; + exit->reason = NVMM_VCPU_EXIT_NMI_READY; break; default: vmx_exit_invalid(exit, exitcode); @@ -2023,7 +2041,7 @@ vmx_vcpu_run(struct nvmm_machine *mach, if (curlwp->l_flag & LW_USERRET) { break; } - if (exit->reason != NVMM_EXIT_NONE) { + if (exit->reason != NVMM_VCPU_EXIT_NONE) { break; } } @@ -2732,6 +2750,67 @@ vmx_vcpu_destroy(struct nvmm_machine *ma roundup(sizeof(*cpudata), PAGE_SIZE), UVM_KMF_WIRED); } +static int +vmx_vcpu_configure(struct nvmm_cpu *vcpu, uint64_t op, void *data) +{ + struct vmx_cpudata *cpudata = vcpu->cpudata; + struct nvmm_vcpu_conf_cpuid *cpuid; + size_t i; + + if (__predict_false(op != NVMM_VCPU_CONF_MD(NVMM_VCPU_CONF_CPUID))) { + return EINVAL; + } + cpuid = data; + + if (__predict_false(cpuid->mask && cpuid->exit)) { + return EINVAL; + } + if (__predict_false(cpuid->mask && + ((cpuid->u.mask.set.eax & cpuid->u.mask.del.eax) || + (cpuid->u.mask.set.ebx & cpuid->u.mask.del.ebx) || + (cpuid->u.mask.set.ecx & cpuid->u.mask.del.ecx) || + (cpuid->u.mask.set.edx & cpuid->u.mask.del.edx)))) { + return EINVAL; + } + + /* If unset, delete, to restore the default behavior. */ + if (!cpuid->mask && !cpuid->exit) { + for (i = 0; i < VMX_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + continue; + } + if (cpudata->cpuid[i].leaf == cpuid->leaf) { + cpudata->cpuidpresent[i] = false; + } + } + return 0; + } + + /* If already here, replace. */ + for (i = 0; i < VMX_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + continue; + } + if (cpudata->cpuid[i].leaf == cpuid->leaf) { + memcpy(&cpudata->cpuid[i], cpuid, + sizeof(struct nvmm_vcpu_conf_cpuid)); + return 0; + } + } + + /* Not here, insert. */ + for (i = 0; i < VMX_NCPUIDS; i++) { + if (!cpudata->cpuidpresent[i]) { + cpudata->cpuidpresent[i] = true; + memcpy(&cpudata->cpuid[i], cpuid, + sizeof(struct nvmm_vcpu_conf_cpuid)); + return 0; + } + } + + return ENOBUFS; +} + /* -------------------------------------------------------------------------- */ static void @@ -2777,44 +2856,7 @@ vmx_machine_destroy(struct nvmm_machine static int vmx_machine_configure(struct nvmm_machine *mach, uint64_t op, void *data) { - struct nvmm_mach_conf_x86_cpuid *cpuid = data; - struct vmx_machdata *machdata = (struct vmx_machdata *)mach->machdata; - size_t i; - - if (__predict_false(op != NVMM_MACH_CONF_MD(NVMM_MACH_CONF_X86_CPUID))) { - return EINVAL; - } - - if (__predict_false((cpuid->set.eax & cpuid->del.eax) || - (cpuid->set.ebx & cpuid->del.ebx) || - (cpuid->set.ecx & cpuid->del.ecx) || - (cpuid->set.edx & cpuid->del.edx))) { - return EINVAL; - } - - /* If already here, replace. */ - for (i = 0; i < VMX_NCPUIDS; i++) { - if (!machdata->cpuidpresent[i]) { - continue; - } - if (machdata->cpuid[i].leaf == cpuid->leaf) { - memcpy(&machdata->cpuid[i], cpuid, - sizeof(struct nvmm_mach_conf_x86_cpuid)); - return 0; - } - } - - /* Not here, insert. */ - for (i = 0; i < VMX_NCPUIDS; i++) { - if (!machdata->cpuidpresent[i]) { - machdata->cpuidpresent[i] = true; - memcpy(&machdata->cpuid[i], cpuid, - sizeof(struct nvmm_mach_conf_x86_cpuid)); - return 0; - } - } - - return ENOBUFS; + panic("%s: impossible", __func__); } /* -------------------------------------------------------------------------- */ @@ -3138,14 +3180,17 @@ const struct nvmm_impl nvmm_x86_vmx = { .init = vmx_init, .fini = vmx_fini, .capability = vmx_capability, - .conf_max = NVMM_X86_NCONF, - .conf_sizes = vmx_conf_sizes, + .mach_conf_max = NVMM_X86_MACH_NCONF, + .mach_conf_sizes = NULL, + .vcpu_conf_max = NVMM_X86_VCPU_NCONF, + .vcpu_conf_sizes = vmx_vcpu_conf_sizes, .state_size = sizeof(struct nvmm_x64_state), .machine_create = vmx_machine_create, .machine_destroy = vmx_machine_destroy, .machine_configure = vmx_machine_configure, .vcpu_create = vmx_vcpu_create, .vcpu_destroy = vmx_vcpu_destroy, + .vcpu_configure = vmx_vcpu_configure, .vcpu_setstate = vmx_vcpu_setstate, .vcpu_getstate = vmx_vcpu_getstate, .vcpu_inject = vmx_vcpu_inject, Index: src/tests/lib/libnvmm/h_io_assist.c diff -u src/tests/lib/libnvmm/h_io_assist.c:1.8 src/tests/lib/libnvmm/h_io_assist.c:1.9 --- src/tests/lib/libnvmm/h_io_assist.c:1.8 Sat Jun 8 07:27:44 2019 +++ src/tests/lib/libnvmm/h_io_assist.c Wed Oct 23 07:01:12 2019 @@ -1,7 +1,7 @@ -/* $NetBSD: h_io_assist.c,v 1.8 2019/06/08 07:27:44 maxv Exp $ */ +/* $NetBSD: h_io_assist.c,v 1.9 2019/10/23 07:01:12 maxv Exp $ */ /* - * Copyright (c) 2018 The NetBSD Foundation, Inc. + * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -242,25 +242,25 @@ handle_io(struct nvmm_machine *mach, str static void run_machine(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) { - struct nvmm_exit *exit = vcpu->exit; + struct nvmm_vcpu_exit *exit = vcpu->exit; while (1) { if (nvmm_vcpu_run(mach, vcpu) == -1) err(errno, "nvmm_vcpu_run"); switch (exit->reason) { - case NVMM_EXIT_NONE: + case NVMM_VCPU_EXIT_NONE: break; - case NVMM_EXIT_MSR: + case NVMM_VCPU_EXIT_RDMSR: /* Stop here. */ return; - case NVMM_EXIT_IO: + case NVMM_VCPU_EXIT_IO: handle_io(mach, vcpu); break; - case NVMM_EXIT_SHUTDOWN: + case NVMM_VCPU_EXIT_SHUTDOWN: printf("Shutting down!\n"); return; Index: src/tests/lib/libnvmm/h_mem_assist.c diff -u src/tests/lib/libnvmm/h_mem_assist.c:1.14 src/tests/lib/libnvmm/h_mem_assist.c:1.15 --- src/tests/lib/libnvmm/h_mem_assist.c:1.14 Mon Oct 14 10:39:24 2019 +++ src/tests/lib/libnvmm/h_mem_assist.c Wed Oct 23 07:01:12 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: h_mem_assist.c,v 1.14 2019/10/14 10:39:24 maxv Exp $ */ +/* $NetBSD: h_mem_assist.c,v 1.15 2019/10/23 07:01:12 maxv Exp $ */ /* * Copyright (c) 2018-2019 The NetBSD Foundation, Inc. @@ -90,25 +90,25 @@ handle_memory(struct nvmm_machine *mach, static void run_machine(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) { - struct nvmm_exit *exit = vcpu->exit; + struct nvmm_vcpu_exit *exit = vcpu->exit; while (1) { if (nvmm_vcpu_run(mach, vcpu) == -1) err(errno, "nvmm_vcpu_run"); switch (exit->reason) { - case NVMM_EXIT_NONE: + case NVMM_VCPU_EXIT_NONE: break; - case NVMM_EXIT_MSR: + case NVMM_VCPU_EXIT_RDMSR: /* Stop here. */ return; - case NVMM_EXIT_MEMORY: + case NVMM_VCPU_EXIT_MEMORY: handle_memory(mach, vcpu); break; - case NVMM_EXIT_SHUTDOWN: + case NVMM_VCPU_EXIT_SHUTDOWN: printf("Shutting down!\n"); return;