Re: tests wanted: vmm(4)/vmd(8) fd-ification
Dave Voutila <[email protected]> Wed, 05 Aug 2026 11:24:16 -0400
| Newsgroups | gmane.os.openbsd.tech |
|---|---|
| Message-ID | <[email protected]> |
Updated diff that applies to the tree again. Recent commit to vmd(8) broke the original diff. There's a different vmd commit I'm making later today but by my testing that shouldn't conflict with this diff. Dave Voutila <[email protected]> writes: > Calling on vmm(4)/vmd(8) users to give this diff a test please! > > This is a major breaking change I'm proposing to land soon. It changes > the vmm(4) design to create a new file type for each VM. When a VM is > created, the caller gets a new file descriptor for using ioctl(2) calls > against that VM. > > This fundamentally changes the design from knowing a "magic number" > (today, the vm id) to possessing a capability (the open file > descriptor). > > Why do this? > > - Better isolate which process can manipulate a VM. Today it's a hacky > combination of knowing the magic number and vmm(4) tracking an owning > PID. The current design scares me. > > - Better scope the vmm(4) API into basically a control plane vs. data > plane design. A privileged process with an open /dev/vmm special file > can launch/terminate VMs, but the manipulation of each VM and VCPU is > done via its own dedicated file. > > - Using files helps simplify the lifecycle management of VMs in the > kernel by tying their lifetime to a file. vmd(8) processes emulating > the VM will have their file descriptor closed by the kernel even if > the process is killed (e.g. via segfault). This simplifies cleanup of > dead VMs. > > Bonus: there's a slight chance this improves perf slightly, but since > that's not the goal I'm not benchmarking. Given a new ioctl(2) path for > the new VM file type, it may reduce contention on the kernel lock. > > Note: this is unlike Linux/KVM which uses not only a file per VM, but > one per VCPU. I see no reason for that level of complication at the moment. > > I don't expect detailed review yet given the size of this. I plan on > sending separate mails breaking this down into kernel vs. userland (the > userland stuff is a lot of deck chair shuffling) as well as updates to > vmm.4 man page and fstat(1). > > To test: > > 1. build and install new kernel and reboot > 2. symlink (recommended) or install headers in /usr/include: > amd64/vmmvar.h --> sys/arch/amd64/include/vmmvar.h > dev/vmm/vmm.h --> sys/dev/vmm/vmm.h > dev/ic/pspvar.h --> sys/dev/ic/pspvar.h Also, you need sys/file.h --> sys/sys/file.h if building fstat(1). > 3. build and install vmd(8) and vmctl(8) (if this fails, check your > headers in /usr/include/ point to the patched ones in the tree) > 4. run your existing VMs...you should see no discernable difference in > behavior. > > If things break or behavior changes, please report to me including: > > * cpu0 details from dmesg(1) > * details on how the vm is started (/etc/vm.conf contents or vmctl(8) > args) > * vmd(8) debug output if any (try running vmd in the foreground via > something like: # $(which vmd) -dvv > > ** I specifically need help testing SEV-related usage as I have no > commercial grade AMD hardware with all the cool SEV stuff. ** > > Once I land this I'll no longer lose sleep worrying about the current > design and can turn my attentions to MMIO/instruction emulation needed > for getting to SMP ;) > > Thanks! > diff refs/heads/master refs/heads/vmm-fd-jul26 commit - 4d6bf5918156b6f5baa19344f0431e7e5f352906 commit + 1590e7f5dabaecbd0fb0a33727a0c2ec33306997 blob - cf3afe729cc6dee1d6789ce566d9cfef7d6141e5 blob + 43836fe9639258c08ebaa50ea3a55cccb9d9e6ce --- regress/sys/arch/amd64/vmm/vcpu.c +++ regress/sys/arch/amd64/vmm/vcpu.c @@ -90,11 +90,10 @@ main(int argc, char **argv) struct vm_info_result *info = NULL, *ours = NULL; struct vm_resetcpu_params vresetp; struct vm_run_params vrunp; - struct vm_terminate_params vtp; struct vm_sharemem_params vsp; struct vm_mem_range *vmr; - int fd, ret = 1; + int fd, vm_fd = -1, ret = 1; size_t i; off_t off, reset = 0xFFFFFFF0, stack = 0x800; void *p; @@ -120,23 +119,21 @@ main(int argc, char **argv) if (ioctl(fd, VMM_IOC_CREATE, &vcp) == -1) err(1, "VMM_IOC_CREATE"); - printf("created vm %d named \"%s\"\n", vcp.vcp_id, vcp.vcp_name); + printf("created vm fd %d named \"%s\"\n", vcp.vcp_fd, vcp.vcp_name); + vm_fd = vcp.vcp_fd; /* * 2. Check we can create shared memory mappings. */ memset(&vsp, 0, sizeof(vsp)); - vsp.vsp_nmemranges = vcp.vcp_nmemranges; - memcpy(&vsp.vsp_memranges, &vcp.vcp_memranges, - sizeof(vsp.vsp_memranges)); - vsp.vsp_vm_id = vcp.vcp_id; + vsp.vsp_fd = vm_fd; /* Perform the shared mapping. */ - if (ioctl(fd, VMM_IOC_SHAREMEM, &vsp) == -1) + if (ioctl(vm_fd, VMM_IOC_SHAREMEM, &vsp) == -1) err(1, "VMM_IOC_SHAREMEM"); printf("created shared memory mappings\n"); - for (i = 0; i < vsp.vsp_nmemranges; i++) + for (i = 0; i < vcp.vcp_nmemranges; i++) vcp.vcp_memranges[i].vmr_va = vsp.vsp_va[i]; for (i = 0; i < vcp.vcp_nmemranges; i++) { @@ -178,8 +175,8 @@ main(int argc, char **argv) } /* We should see our reset vector instructions in the new mappings. */ - for (i = 0; i < vsp.vsp_nmemranges; i++) { - vmr = &vsp.vsp_memranges[i]; + for (i = 0; i < vcp.vcp_nmemranges; i++) { + vmr = &vcp.vcp_memranges[i]; p = (void*)vmr->vmr_va; if (i == LOW_MEM) { @@ -229,43 +226,38 @@ main(int argc, char **argv) } for (i = 0; i * sizeof(*info) < vip.vip_size; i++) { - if (info[i].vir_id == vcp.vcp_id) { + if (info[i].vir_creator_pid == getpid()) { ours = &info[i]; break; } } if (ours == NULL) { - warn("failed to find vm %uz", vcp.vcp_id); + warn("failed to find vm for pid %d", getpid()); goto out; } - if (ours->vir_id != vcp.vcp_id) { - warnx("expected vm id %uz, got %uz", vcp.vcp_id, ours->vir_id); - goto out; - } if (strncmp(ours->vir_name, VM_NAME, strlen(VM_NAME)) != 0) { warnx("expected vm name \"%s\", got \"%s\"", VM_NAME, ours->vir_name); goto out; } - printf("found vm %d named \"%s\"\n", vcp.vcp_id, ours->vir_name); + printf("found vm for pid %d named \"%s\"\n", getpid(), ours->vir_name); ours = NULL; /* * 4. Reset our VCPU and initialize register state. */ memset(&vresetp, 0, sizeof(vresetp)); - vresetp.vrp_vm_id = vcp.vcp_id; + vresetp.vrp_fd = vm_fd; vresetp.vrp_vcpu_id = 0; /* XXX SP */ memcpy(&vresetp.vrp_init_state, &vcpu_init_flat16, sizeof(vcpu_init_flat16)); - if (ioctl(fd, VMM_IOC_RESETCPU, &vresetp) == -1) { + if (ioctl(vm_fd, VMM_IOC_RESETCPU, &vresetp) == -1) { warn("VMM_IOC_RESETCPU"); goto out; } - printf("reset vcpu %d for vm %d\n", vresetp.vrp_vcpu_id, - vresetp.vrp_vm_id); + printf("reset vcpu %d for vm fd %d\n", vresetp.vrp_vcpu_id, vm_fd); /* * 5. Run the vcpu, expecting an immediate exit for IO assist. @@ -279,26 +271,20 @@ main(int argc, char **argv) memset(&vrunp, 0, sizeof(vrunp)); vrunp.vrp_exit = exit; vrunp.vrp_vcpu_id = 0; /* XXX SP */ - vrunp.vrp_vm_id = vcp.vcp_id; + vrunp.vrp_fd = vm_fd; vrunp.vrp_irqready = 1; - if (ioctl(fd, VMM_IOC_RUN, &vrunp) == -1) { + if (ioctl(vm_fd, VMM_IOC_RUN, &vrunp) == -1) { warn("VMM_IOC_RUN"); goto out; } - if (vrunp.vrp_vm_id != vcp.vcp_id) { - warnx("expected vm id %uz, got %uz", vcp.vcp_id, - vrunp.vrp_vm_id); - goto out; - } - switch (vrunp.vrp_exit_reason) { case SVM_VMEXIT_IOIO: case VMX_EXIT_IO: - printf("vcpu %d on vm %d exited for io assist @ ip = 0x%llx, " + printf("vcpu %d on vm fd %d exited for io assist @ ip = 0x%llx, " "cs.base = 0x%llx, ss.base = 0x%llx, rsp = 0x%llx\n", - vrunp.vrp_vcpu_id, vrunp.vrp_vm_id, + vrunp.vrp_vcpu_id, vm_fd, vrunp.vrp_exit->vrs.vrs_gprs[VCPU_REGS_RIP], vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_CS].vsi_base, vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_SS].vsi_base, @@ -333,7 +319,7 @@ main(int argc, char **argv) vrunp.vrp_inject.vie_errorcode = 0x11223344; vrunp.vrp_inject.vie_type = VCPU_INJECT_EX; printf("injecting exception 0x%x\n", vrunp.vrp_inject.vie_vector); - if (ioctl(fd, VMM_IOC_RUN, &vrunp) == -1) { + if (ioctl(vm_fd, VMM_IOC_RUN, &vrunp) == -1) { warn("VMM_IOC_RUN 2"); goto out; } @@ -341,8 +327,8 @@ main(int argc, char **argv) switch (vrunp.vrp_exit_reason) { case SVM_VMEXIT_IOIO: case VMX_EXIT_IO: - printf("vcpu %d on vm %d exited for io assist @ ip = 0x%llx, " - "cs.base = 0x%llx\n", vrunp.vrp_vcpu_id, vrunp.vrp_vm_id, + printf("vcpu %d on vm fd %d exited for io assist @ ip = 0x%llx, " + "cs.base = 0x%llx\n", vrunp.vrp_vcpu_id, vm_fd, vrunp.vrp_exit->vrs.vrs_gprs[VCPU_REGS_RIP], vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_CS].vsi_base); break; @@ -372,13 +358,13 @@ out: if (i > 0) printf(" "); printf("%02x", *(uint8_t*) - (vsp.vsp_memranges[UPPER_MEM].vmr_va + off + i)); + (vcp.vcp_memranges[UPPER_MEM].vmr_va + off + i)); } printf("\n--- STACK @ gpa 0x%llx ---\n", stack); for (i=0; i<16; i++) { if (i > 0) printf(" "); - printf("%02x", *(uint8_t*)(vsp.vsp_memranges[LOW_MEM].vmr_va + printf("%02x", *(uint8_t*)(vcp.vcp_memranges[LOW_MEM].vmr_va + stack - i - 1)); } printf("\n"); @@ -386,13 +372,8 @@ out: /* * 6. Terminate our VM and clean up. */ - memset(&vtp, 0, sizeof(vtp)); - vtp.vtp_vm_id = vcp.vcp_id; - if (ioctl(fd, VMM_IOC_TERM, &vtp) == -1) { - warn("VMM_IOC_TERM"); - ret = 1; - } else - printf("terminated vm %d\n", vtp.vtp_vm_id); + if (vm_fd != -1) + close(vm_fd); close(fd); free(info); blob - cc621a8eee3faf2521b1c9ba014b8dee77cacfc9 blob + 05ae299446902adcf3489b3a1dea4e7231fc18d9 --- share/man/man4/man4.amd64/vmm.4 +++ share/man/man4/man4.amd64/vmm.4 @@ -14,7 +14,7 @@ .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 5 2024 $ +.Dd $Mdocdate: March 2 2026 $ .Dt VMM 4 amd64 .Os .Sh NAME @@ -72,23 +72,53 @@ For more information, consult the CPU vendor's documen .Pp The following .Xr ioctl 2 -commands are provided for managing -.Nm -guests: +commands are provided through two interfaces: .Bl -tag -width Ds -offset indent +.It Pa /dev/vmm +Control device for creating VMs and querying system-wide VM information. +.It VM file descriptor +Per-VM handle returned by +.Dv VMM_IOC_CREATE . +The lifetime of a VM is tied to this descriptor; closing it tears down the VM. +.El +.Pp +The following ioctl calls apply to the +.Pa /dev/vmm +control device: +.Bl -tag -width Ds -offset indent .It Dv VMM_IOC_CREATE Fa "struct vm_create_params *" -Create a VM, initializing +Create a VM via +.Pa /dev/vmm , +initializing .Nm if not yet started. +Returns a VM file descriptor in +.Fa vcp_fd . (Does not start the VCPU.) -.It Dv VMM_IOC_RUN Fa "struct vm_run_params *" -Run a VCPU for a defined VM. -Return on VM-exit, when the VCPU stopped, or an error occurred. .It Dv VMM_IOC_INFO Fa "struct vm_info_params *" Get information about the VMs currently hosted by .Nm . .It Dv VMM_IOC_TERM Fa "struct vm_terminate_params *" -Terminate a given VM. +Request termination of the VM whose creator PID matches +.Fa vtp_creator_pid . +This ioctl is issued on +.Pa /dev/vmm . +It marks the VM for termination and requests running VCPUs to stop. +Final cleanup happens when the last VM file descriptor reference is closed. +.Pp +All other ioctls, including +.Dv VMM_IOC_INTR , +are unsupported on +.Pa /dev/vmm +and return +.Dv ENOTTY . +.El +.Pp +The following ioctl calls apply to the VM file descriptor returned by +.Dv VMM_IOC_CREATE . +.Bl -tag -width Ds -offset indent +.It Dv VMM_IOC_RUN Fa "struct vm_run_params *" +Run a VCPU and return on VM-exit, when the VCPU stopped, or on error. .It Dv VMM_IOC_RESETCPU Fa "struct vm_resetcpu_params *" Reset a VCPU to power-on-init state using the provided register state. .It Dv VMM_IOC_INTR Fa "struct vm_intr_params *" @@ -105,6 +135,8 @@ version) for a VM. Write paravirtualized hardware parameters (such as .Xr pvclock 4 guest physical address) for a VM. +.It Dv VMM_IOC_SHAREMEM Fa "struct vm_sharemem_params *" +Create shared mappings for the VM memory ranges into the calling process. .El .Sh SEE ALSO .Xr cpu 4 , blob - 787b65e29e167453c2d5492d5658833d5a75f5af blob + 1fd7df73f156a0968bbdeff7b7ae7235b1651ae3 --- sys/arch/amd64/amd64/vmm_machdep.c +++ sys/arch/amd64/amd64/vmm_machdep.c @@ -68,12 +68,8 @@ void *l1tf_flush_region; void vmx_dump_vmcs_field(uint16_t, const char *); int vmm_enabled(void); void vmm_activate_machdep(struct device *, int); -int vmmioctl_machdep(dev_t, u_long, caddr_t, int, struct proc *); int vmm_quiesce_vmx(void); -int vm_run(struct vm_run_params *); -int vm_intr_pending(struct vm_intr_params *); -int vm_rwregs(struct vm_rwregs_params *, int); -int vm_rwvmparams(struct vm_rwvmparams_params *, int); +int vm_intr_pending(struct vm *, struct vm_intr_params *); int vcpu_readregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *); int vcpu_readregs_svm(struct vcpu *, uint64_t, struct vcpu_reg_state *); int vcpu_writeregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *); @@ -148,7 +144,6 @@ void vmx_setmsrbw(struct vcpu *, uint32_t); void vmx_setmsrbrw(struct vcpu *, uint32_t); void svm_set_clean(struct vcpu *, uint32_t); void svm_set_dirty(struct vcpu *, uint32_t); -int svm_get_vmsa_pa(uint32_t, uint32_t, uint64_t *); int vmm_gpa_is_valid(struct vcpu *vcpu, paddr_t gpa, size_t obj_size); void vmm_init_pvclock(struct vcpu *, paddr_t); @@ -438,34 +433,6 @@ vmm_activate_machdep(struct device *self, int act) } } -int -vmmioctl_machdep(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) -{ - int ret; - - switch (cmd) { - case VMM_IOC_INTR: - ret = vm_intr_pending((struct vm_intr_params *)data); - break; - default: - DPRINTF("%s: unknown ioctl code 0x%lx\n", __func__, cmd); - ret = ENOTTY; - } - - return (ret); -} - -int -pledge_ioctl_vmm_machdep(struct proc *p, long com) -{ - switch (com) { - case VMM_IOC_INTR: - return (0); - } - - return (EPERM); -} - /* * vm_intr_pending * @@ -480,22 +447,14 @@ pledge_ioctl_vmm_machdep(struct proc *p, long com) * ENOENT: if the VM/VCPU defined by 'vip' cannot be found */ int -vm_intr_pending(struct vm_intr_params *vip) +vm_intr_pending(struct vm *vm, struct vm_intr_params *vip) { - struct vm *vm; struct vcpu *vcpu; #ifdef MULTIPROCESSOR struct cpu_info *ci; #endif - int error, ret = 0; + int ret = 0; - /* Find the desired VM */ - error = vm_find(vip->vip_vm_id, &vm); - - /* Not found? exit. */ - if (error != 0) - return (error); - vcpu = vm_find_vcpu(vm, vip->vip_vcpu_id); if (vcpu == NULL) { @@ -511,7 +470,6 @@ vm_intr_pending(struct vm_intr_params *vip) #endif out: - refcnt_rele_wake(&vm->vm_refcnt); return (ret); } @@ -531,19 +489,11 @@ out: * EINVAL: if an error occurred reading the registers of the guest */ int -vm_rwvmparams(struct vm_rwvmparams_params *vpp, int dir) +vm_rwvmparams(struct vm *vm, struct vm_rwvmparams_params *vpp, int dir) { - struct vm *vm; struct vcpu *vcpu; - int error, ret = 0; + int ret = 0; - /* Find the desired VM */ - error = vm_find(vpp->vpp_vm_id, &vm); - - /* Not found? exit. */ - if (error != 0) - return (error); - vcpu = vm_find_vcpu(vm, vpp->vpp_vcpu_id); if (vcpu == NULL) { @@ -565,7 +515,6 @@ vm_rwvmparams(struct vm_rwvmparams_params *vpp, int di } } out: - refcnt_rele_wake(&vm->vm_refcnt); return (ret); } @@ -587,20 +536,12 @@ out: * EPERM: if the vm cannot be accessed from the calling process */ int -vm_rwregs(struct vm_rwregs_params *vrwp, int dir) +vm_rwregs(struct vm *vm, struct vm_rwregs_params *vrwp, int dir) { - struct vm *vm; struct vcpu *vcpu; struct vcpu_reg_state *vrs = &vrwp->vrwp_regs; - int error, ret = 0; + int ret = 0; - /* Find the desired VM */ - error = vm_find(vrwp->vrwp_vm_id, &vm); - - /* Not found? exit. */ - if (error != 0) - return (error); - vcpu = vm_find_vcpu(vm, vrwp->vrwp_vcpu_id); if (vcpu == NULL) { @@ -623,7 +564,6 @@ vm_rwregs(struct vm_rwregs_params *vrwp, int dir) } rw_exit_write(&vcpu->vc_lock); out: - refcnt_rele_wake(&vm->vm_refcnt); return (ret); } @@ -3348,20 +3288,12 @@ vcpu_vmx_compute_ctrl(uint64_t ctrlval, uint16_t ctrl, * 0: the run loop exited and no help is needed from vmd(8) */ int -vm_run(struct vm_run_params *vrp) +vm_run(struct vm *vm, struct vm_run_params *vrp) { - struct vm *vm; struct vcpu *vcpu; int ret = 0, vcpu_rv = 0; u_int old, next; - /* - * Find desired VM - */ - ret = vm_find(vrp->vrp_vm_id, &vm); - if (ret) - return (ret); - vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id); if (vcpu == NULL) { ret = ENOENT; @@ -3419,7 +3351,6 @@ vm_run(struct vm_run_params *vrp) out_unlock: rw_exit_write(&vcpu->vc_lock); out: - refcnt_rele_wake(&vm->vm_refcnt); return (ret); } @@ -7412,13 +7343,13 @@ vcpu_state_decode(u_int state) * Return physical address of VMSA for specified VCPU. */ int -svm_get_vmsa_pa(uint32_t vmid, uint32_t vcpuid, uint64_t *vmsapa) +svm_get_vmsa_pa(struct proc *p, int fd, uint32_t vcpuid, uint64_t *vmsapa) { struct vm *vm; struct vcpu *vcpu; int error, ret = 0; - error = vm_find(vmid, &vm); + error = vm_find_file(fd, p, &vm); if (error) return (error); blob - 25b1618ad1ff6590eb030e9a89729da2c954c9d0 blob + 3f594cb58dcd585f50bd6ba89bf4fd14824d0b4b --- sys/arch/amd64/include/vmmvar.h +++ sys/arch/amd64/include/vmmvar.h @@ -23,6 +23,8 @@ #ifndef _LOCORE +struct vm; + #define VMM_HV_SIGNATURE "OpenBSDVMM58" /* VMX: Basic Exit Reasons */ @@ -477,7 +479,6 @@ struct vm_exit { struct vm_intr_params { /* Input parameters to VMM_IOC_INTR */ - uint32_t vip_vm_id; uint32_t vip_vcpu_id; uint16_t vip_intr; }; @@ -495,7 +496,6 @@ struct vm_rwregs_params { * Input/output parameters to VMM_IOC_READREGS / * VMM_IOC_WRITEREGS */ - uint32_t vrwp_vm_id; uint32_t vrwp_vcpu_id; uint64_t vrwp_mask; struct vcpu_reg_state vrwp_regs; @@ -1056,17 +1056,15 @@ void vmclear_on_cpu(struct cpu_info *); int vmm_probe_machdep(struct device *, void *, void *); void vmm_attach_machdep(struct device *, struct device *, void *); void vmm_activate_machdep(struct device *, int); -int vmmioctl_machdep(dev_t, u_long, caddr_t, int, struct proc *); -int pledge_ioctl_vmm_machdep(struct proc *, long); int vmm_start(void); int vmm_stop(void); int vm_impl_init(struct vm *, struct proc *); void vm_impl_deinit(struct vm *); int vcpu_init(struct vcpu *, struct vm_create_params *); void vcpu_deinit(struct vcpu *); -int vm_rwregs(struct vm_rwregs_params *, int); +int vm_rwregs(struct vm *, struct vm_rwregs_params *, int); int vcpu_reset_regs(struct vcpu *, struct vcpu_reg_state *); -int svm_get_vmsa_pa(uint32_t, uint32_t, uint64_t *); +int svm_get_vmsa_pa(struct proc *, int, uint32_t, uint64_t *); #endif /* _KERNEL */ blob - fb64df87384303d04ed6112d3f3866b261bb2899 blob + 47973afbc9688f9acab4991fac557abddef0e63f --- sys/arch/arm64/include/vmmvar.h +++ sys/arch/arm64/include/vmmvar.h @@ -63,7 +63,6 @@ struct vm_exit { struct vm_intr_params { /* Input parameters to VMM_IOC_INTR */ - uint32_t vip_vm_id; uint32_t vip_vcpu_id; uint16_t vip_intr; }; @@ -76,7 +75,6 @@ struct vm_rwregs_params { * Input/output parameters to VMM_IOC_READREGS / * VMM_IOC_WRITEREGS */ - uint32_t vrwp_vm_id; uint32_t vrwp_vcpu_id; uint64_t vrwp_mask; struct vcpu_reg_state vrwp_regs; blob - 48c463b28898dddfce5e5a7d77ddc4b1e7c57abb blob + d6765901c6c7a265a84a2c3e170827e414b0e7a9 --- sys/dev/ic/psp.c +++ sys/dev/ic/psp.c @@ -697,13 +697,14 @@ psp_activate(struct psp_softc *sc, struct psp_activate } int -psp_encrypt_state(struct psp_softc *sc, struct psp_encrypt_state *ues) +psp_encrypt_state(struct psp_softc *sc, struct psp_encrypt_state *ues, + struct proc *p) { struct psp_launch_update_vmsa luvmsa; uint64_t vmsa_paddr; int error; - error = svm_get_vmsa_pa(ues->vmid, ues->vcpuid, &vmsa_paddr); + error = svm_get_vmsa_pa(p, ues->vmfd, ues->vcpuid, &vmsa_paddr); if (error != 0) return (error); @@ -922,7 +923,8 @@ pspioctl(dev_t dev, u_long cmd, caddr_t data, int flag (struct psp_snp_platform_status *)data); break; case PSP_IOC_ENCRYPT_STATE: - error = psp_encrypt_state(sc, (struct psp_encrypt_state *)data); + error = psp_encrypt_state(sc, (struct psp_encrypt_state *)data, + p); break; default: error = ENOTTY; blob - d319357e8426770b15f9193dd8ba111f16e90ab5 blob + 2c2f24ade1c35ff21d8b94585da699d94c930e19 --- sys/dev/ic/pspvar.h +++ sys/dev/ic/pspvar.h @@ -154,7 +154,7 @@ struct psp_encrypt_state { /* Input parameters state encryption */ uint32_t handle; uint32_t asid; - uint32_t vmid; + int vmfd; uint32_t vcpuid; } __packed; blob - 42fc6933911239c956034f4f6c6538fe8486c980 blob + 9da1675a6ed73b9edc6e6e2e17bd89ee0c80b42f --- sys/dev/vmm/vmm.c +++ sys/dev/vmm/vmm.c @@ -18,12 +18,16 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/file.h> +#include <sys/filedesc.h> #include <sys/pool.h> #include <sys/pledge.h> #include <sys/proc.h> #include <sys/ioctl.h> #include <sys/malloc.h> #include <sys/signalvar.h> +#include <sys/stat.h> #include <uvm/uvm_extern.h> #include <uvm/uvm_aobj.h> @@ -36,6 +40,13 @@ struct vmm_softc *vmm_softc; struct pool vm_pool; struct pool vcpu_pool; +int vmm_probe(struct device *, void *, void *); +int vmm_activate(struct device *, int); +void vmm_attach(struct device *, struct device *, void *); +int vmmopen(dev_t, int, int, struct proc *); +int vmmclose(dev_t, int, int, struct proc *); +int vm_find_file(int, struct proc *, struct vm **); + struct cfdriver vmm_cd = { NULL, "vmm", DV_DULL, CD_SKIPHIBERNATE }; @@ -44,6 +55,39 @@ const struct cfattach vmm_ca = { sizeof(struct vmm_softc), vmm_probe, vmm_attach, NULL, vmm_activate }; +int pledge_ioctl_vmm_dev(struct proc *, long); +int vmm_dev_enter(void); +void vmm_dev_exit(void); +int vm_create(struct vm_create_params *, struct proc *, struct vm **); +size_t vm_create_check_mem_ranges(struct vm_create_params *); +int vm_create_file(struct vm_create_params *, struct proc *, struct vm *); +int vm_find_pid(pid_t, struct vm **); +int vm_get_info(struct vm_info_params *); +int vm_intr_pending(struct vm *, struct vm_intr_params *); +int vm_terminate(struct vm_terminate_params *); +int vm_resetcpu(struct vm *, struct vm_resetcpu_params *); +int vm_rwvmparams(struct vm *, struct vm_rwvmparams_params *, int); +int vm_share_mem(struct vm *, struct vm_sharemem_params *, struct proc *); +void vm_teardown(struct vm **); +void vm_request_stop(struct vm *); + +int vm_read(struct file *, struct uio *, int); +int vm_write(struct file *, struct uio *, int); +int vm_close(struct file *, struct proc *); +int vm_kqfilter(struct file *, struct knote *); +int vm_ioctl(struct file *, u_long, caddr_t, struct proc *); +int vm_stat(struct file *, struct stat *, struct proc *); + +static const struct fileops vmops = { + .fo_read = vm_read, + .fo_write = vm_write, + .fo_ioctl = vm_ioctl, + .fo_kqfilter = vm_kqfilter, + .fo_stat = vm_stat, + .fo_close = vm_close, + .fo_seek = NULL, /* lseek(2) checks for NULL. */ +}; + int vmm_probe(struct device *parent, void *match, void *aux) { @@ -154,126 +198,137 @@ vmmclose(dev_t dev, int flag, int mode, struct proc *p return 0; } -/* - * vm_find - * - * Function to find an existing VM by its identifier. - * Must be called under the global vm_lock. - * - * Parameters: - * id: The VM identifier. - * *res: A pointer to the VM or NULL if not found - * - * Return values: - * 0: if successful - * ENOENT: if the VM defined by 'id' cannot be found - * EPERM: if the VM cannot be accessed by the current process - */ int -vm_find(uint32_t id, struct vm **res) +vm_find_file(int fd, struct proc *p, struct vm **res) { - struct proc *p = curproc; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vm *vm = NULL; + + *res = NULL; + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + + if (fp->f_type != DTYPE_VMM) { + FRELE(fp, p); + return (EINVAL); + } + + vm = (struct vm *)fp->f_data; + refcnt_take(&vm->vm_refcnt); + *res = vm; + FRELE(fp, p); + + return (0); +} + +int +vm_find_pid(pid_t pid, struct vm **res) +{ struct vm *vm; - int ret = ENOENT; *res = NULL; + if (pid <= 0) + return (EINVAL); + rw_enter_read(&vmm_softc->vm_lock); SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) { - if (vm->vm_id == id) { - /* - * In the pledged VM process, only allow to find - * the VM that is running in the current process. - * The managing vmm parent process can lookup all - * all VMs and is indicated by PLEDGE_PROC. - */ - if (((p->p_pledge & - (PLEDGE_VMM | PLEDGE_PROC)) == PLEDGE_VMM) && - (vm->vm_creator_pid != p->p_p->ps_pid)) - ret = EPERM; - else { - refcnt_take(&vm->vm_refcnt); - *res = vm; - ret = 0; - } + if (vm->vm_creator_pid == pid) { + refcnt_take(&vm->vm_refcnt); + *res = vm; break; } } rw_exit_read(&vmm_softc->vm_lock); - if (ret == EPERM) - return (pledge_fail(p, EPERM, PLEDGE_VMM)); - return (ret); + if (*res == NULL) + return (ENOENT); + + return (0); } /* - * vmmioctl + * vmm_dev_enter * - * Main ioctl dispatch routine for /dev/vmm. Parses ioctl type and calls - * appropriate lower level handler routine. Returns result to ioctl caller. + * Acquire a reference to the vmm softc instance, sleeping if it's not + * currently active due to power management (i.e. suspend/resume). */ int -vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +vmm_dev_enter(void) { int ret; - KERNEL_UNLOCK(); - ret = rw_enter(&vmm_softc->sc_slock, RW_READ | RW_INTR); if (ret != 0) - goto out; + return (ret); while (vmm_softc->sc_status != VMM_ACTIVE) { ret = rwsleep_nsec(&vmm_softc->sc_status, &vmm_softc->sc_slock, PWAIT | PCATCH, "vmmresume", INFSLP); if (ret != 0) { rw_exit(&vmm_softc->sc_slock); - goto out; + return (ret); } } refcnt_take(&vmm_softc->sc_refcnt); rw_exit(&vmm_softc->sc_slock); + return (0); +} +/* + * vmm_dev_exit + * + * Release a reference to the vmm softc, waking any waiters. + */ +void +vmm_dev_exit(void) +{ + refcnt_rele_wake(&vmm_softc->sc_refcnt); +} + +/* + * vmmioctl + * + * Main ioctl dispatch for vmm(4) providing global operations for + * creating, terminating, and inspecting virtual machines. + */ +int +vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct vm *vm = NULL; + int ret = ENOTTY; + + KERNEL_UNLOCK(); + + ret = vmm_dev_enter(); + if (ret != 0) + goto out; + switch (cmd) { case VMM_IOC_CREATE: - if ((ret = vmm_start()) != 0) { + ret = vmm_start(); + if (ret) { vmm_stop(); break; } - ret = vm_create((struct vm_create_params *)data, p); + ret = vm_create((struct vm_create_params *)data, p, &vm); + if (ret) + break; + ret = vm_create_file((struct vm_create_params *)data, p, vm); break; - case VMM_IOC_RUN: - ret = vm_run((struct vm_run_params *)data); - break; case VMM_IOC_INFO: ret = vm_get_info((struct vm_info_params *)data); break; case VMM_IOC_TERM: ret = vm_terminate((struct vm_terminate_params *)data); break; - case VMM_IOC_RESETCPU: - ret = vm_resetcpu((struct vm_resetcpu_params *)data); - break; - case VMM_IOC_READREGS: - ret = vm_rwregs((struct vm_rwregs_params *)data, 0); - break; - case VMM_IOC_WRITEREGS: - ret = vm_rwregs((struct vm_rwregs_params *)data, 1); - break; - case VMM_IOC_READVMPARAMS: - ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 0); - break; - case VMM_IOC_WRITEVMPARAMS: - ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 1); - break; - case VMM_IOC_SHAREMEM: - ret = vm_share_mem((struct vm_sharemem_params *)data, p); - break; default: - ret = vmmioctl_machdep(dev, cmd, data, flag, p); + ret = ENOTTY; break; } - refcnt_rele_wake(&vmm_softc->sc_refcnt); + vmm_dev_exit(); out: KERNEL_LOCK(); @@ -281,39 +336,6 @@ out: } /* - * pledge_ioctl_vmm - * - * Restrict the allowed ioctls in a pledged process context. - * Is called from pledge_ioctl(). - */ -int -pledge_ioctl_vmm(struct proc *p, long com) -{ - switch (com) { - case VMM_IOC_CREATE: - case VMM_IOC_INFO: - case VMM_IOC_SHAREMEM: - /* The "parent" process in vmd forks and manages VMs */ - if (p->p_pledge & PLEDGE_PROC) - return (0); - break; - case VMM_IOC_TERM: - /* XXX VM processes should only terminate themselves */ - case VMM_IOC_RUN: - case VMM_IOC_RESETCPU: - case VMM_IOC_READREGS: - case VMM_IOC_WRITEREGS: - case VMM_IOC_READVMPARAMS: - case VMM_IOC_WRITEVMPARAMS: - return (0); - default: - return pledge_ioctl_vmm_machdep(p, com); - } - - return (EPERM); -} - -/* * vm_find_vcpu * * Lookup VMM VCPU by ID number @@ -353,7 +375,7 @@ vm_find_vcpu(struct vm *vm, uint32_t id) * various other errors from vcpu_init/vm_impl_init */ int -vm_create(struct vm_create_params *vcp, struct proc *p) +vm_create(struct vm_create_params *vcp, struct proc *p, struct vm **out) { int i, ret = EINVAL; size_t memsize; @@ -363,6 +385,8 @@ vm_create(struct vm_create_params *vcp, struct proc *p struct vm_mem_range *vmr; unsigned int uvmflags = 0; + *out = NULL; + memsize = vm_create_check_mem_ranges(vcp); if (memsize == 0) return (EINVAL); @@ -474,7 +498,6 @@ vm_create(struct vm_create_params *vcp, struct proc *p rw_enter_write(&vmm_softc->vm_lock); vmm_softc->vm_idx++; vm->vm_id = vmm_softc->vm_idx; - vcp->vcp_id = vm->vm_id; refcnt_init(&vm->vm_refcnt); SLIST_INSERT_HEAD(&vmm_softc->vm_list, vm, vm_link); @@ -484,6 +507,7 @@ vm_create(struct vm_create_params *vcp, struct proc *p memcpy(vcp->vcp_memranges, vm->vm_memranges, vcp->vcp_nmemranges * sizeof(vcp->vcp_memranges[0])); + *out = vm; return (0); err: @@ -639,10 +663,12 @@ vm_get_info(struct vm_info_params *vip) struct vm *vm; struct vcpu *vcpu; int i = 0, j; - size_t need, vm_ct; + size_t alloc, need, vm_ct; rw_enter_read(&vmm_softc->vm_lock); - vm_ct = vmm_softc->vm_ct; + vm_ct = 0; + SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) + vm_ct++; rw_exit_read(&vmm_softc->vm_lock); need = vm_ct * sizeof(struct vm_info_result); @@ -652,14 +678,19 @@ vm_get_info(struct vm_info_params *vip) return (0); } - out = malloc(need, M_DEVBUF, M_NOWAIT|M_ZERO); + if (vm_ct == 0) { + vip->vip_info_ct = 0; + vip->vip_size = 0; + return (0); + } + + alloc = need; + out = malloc(alloc, M_DEVBUF, M_NOWAIT|M_ZERO); if (out == NULL) { vip->vip_info_ct = 0; return (ENOMEM); } - vip->vip_info_ct = vm_ct; - rw_enter_read(&vmm_softc->vm_lock); SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) { refcnt_take(&vm->vm_refcnt); @@ -668,7 +699,6 @@ vm_get_info(struct vm_info_params *vip) out[i].vir_used_size = pmap_resident_count(vm->vm_pmap) * PAGE_SIZE; out[i].vir_ncpus = vm->vm_vcpu_ct; - out[i].vir_id = vm->vm_id; out[i].vir_creator_pid = vm->vm_creator_pid; strlcpy(out[i].vir_name, vm->vm_name, VMM_MAX_NAME_LEN); @@ -678,7 +708,7 @@ vm_get_info(struct vm_info_params *vip) vc_vcpu_link) { if (vcpu->vc_id == j) out[i].vir_vcpu_state[j] = - vcpu->vc_state; + atomic_load_int(&vcpu->vc_state); } } @@ -688,75 +718,91 @@ vm_get_info(struct vm_info_params *vip) break; /* Truncate to keep within bounds of 'out'. */ } rw_exit_read(&vmm_softc->vm_lock); + vip->vip_info_ct = i; + need = i * sizeof(struct vm_info_result); + vip->vip_size = need; if (copyout(out, vip->vip_info, need) == EFAULT) { - free(out, M_DEVBUF, need); + free(out, M_DEVBUF, alloc); return (EFAULT); } - free(out, M_DEVBUF, need); + free(out, M_DEVBUF, alloc); return (0); } -/* - * vm_terminate - * - * Terminates the VM indicated by 'vtp'. - * - * Parameters: - * vtp: structure defining the VM to terminate - * - * Return values: - * 0: the VM was terminated - * !0: the VM could not be located - */ +void +vm_request_stop(struct vm *vm) +{ + struct vcpu *vcpu; + u_int old; +#ifdef MULTIPROCESSOR + struct cpu_info *ci; +#endif + + SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) { + do { + old = atomic_load_int(&vcpu->vc_state); + if (old == VCPU_STATE_REQTERM || + old == VCPU_STATE_TERMINATED) + break; + } while (atomic_cas_uint(&vcpu->vc_state, old, + VCPU_STATE_REQTERM) != old); + +#ifdef MULTIPROCESSOR + /* + * If this vCPU is currently running in guest mode, nudge the + * host CPU so it exits promptly and observes REQTERM. + */ + if (old != VCPU_STATE_TERMINATED) { + ci = READ_ONCE(vcpu->vc_curcpu); + if (ci != NULL) + x86_send_ipi(ci, X86_IPI_NOP); + } +#endif + } +} + int vm_terminate(struct vm_terminate_params *vtp) { struct vm *vm; - int error, nvcpu, vm_id; + int error, relock; + relock = _kernel_lock_held(); + if (relock) + KERNEL_UNLOCK(); + + error = vm_find_pid(vtp->vtp_creator_pid, &vm); + if (error != 0) + goto out; + + /* Only proceed through remove once. */ + if (atomic_cas_uint(&vm->vm_dying, VMM_VM_ALIVE, VMM_VM_DYING) != + VMM_VM_ALIVE) { + /* + * Already terminating; kick again in case the caller is retrying + * while a VCPU is still running. + */ + vm_request_stop(vm); + refcnt_rele_wake(&vm->vm_refcnt); + error = EBUSY; + goto out; + } + /* - * Find desired VM + * Request VCPU exit. Teardown is deferred to vm_close() so TERM only + * initiates termination and does not wait for VM file references. */ - error = vm_find(vtp->vtp_vm_id, &vm); - if (error) - return (error); + vm_request_stop(vm); + refcnt_rele_wake(&vm->vm_refcnt); - /* Only proceed through remove and teardown once. */ - if (atomic_cas_uint(&vm->vm_dying, 0, 1) == 1) { - refcnt_rele_wake(&vm->vm_refcnt); - return (EBUSY); - } + error = 0; +out: + if (relock) + KERNEL_LOCK(); - /* Pop the vm out of the global vm list. */ - rw_enter_write(&vmm_softc->vm_lock); - SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link); - rw_exit_write(&vmm_softc->vm_lock); - - /* Drop the vm_list's reference to the vm. */ - if (refcnt_rele(&vm->vm_refcnt)) - panic("%s: vm %d(%p) vm_list refcnt drop was the last", - __func__, vm->vm_id, vm); - - /* Wait for our reference (taken from vm_find) is the last active. */ - refcnt_finalize(&vm->vm_refcnt, __func__); - - vm_id = vm->vm_id; - nvcpu = vm->vm_vcpu_ct; - - vm_teardown(&vm); - - if (vm_id > 0) { - rw_enter_write(&vmm_softc->vm_lock); - vmm_softc->vm_ct--; - vmm_softc->vcpu_ct -= nvcpu; - if (vmm_softc->vm_ct < 1) - vmm_stop(); - rw_exit_write(&vmm_softc->vm_lock); - } - - return (0); + return (error); } /* @@ -774,27 +820,16 @@ vm_terminate(struct vm_terminate_params *vtp) * EIO if the indicated VCPU failed to reset */ int -vm_resetcpu(struct vm_resetcpu_params *vrp) +vm_resetcpu(struct vm *vm, struct vm_resetcpu_params *vrp) { - struct vm *vm; struct vcpu *vcpu; - int error, ret = 0; + int ret = 0; - /* Find the desired VM */ - error = vm_find(vrp->vrp_vm_id, &vm); - - /* Not found? exit. */ - if (error != 0) { - DPRINTF("%s: vm id %u not found\n", __func__, - vrp->vrp_vm_id); - return (error); - } - vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id); if (vcpu == NULL) { - DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__, - vrp->vrp_vcpu_id, vrp->vrp_vm_id); + DPRINTF("%s: vcpu id %u not found\n", __func__, + vrp->vrp_vcpu_id); ret = ENOENT; goto out; } @@ -813,8 +848,6 @@ vm_resetcpu(struct vm_resetcpu_params *vrp) } rw_exit_write(&vcpu->vc_lock); out: - refcnt_rele_wake(&vm->vm_refcnt); - return (ret); } @@ -838,7 +871,7 @@ vcpu_must_stop(struct vcpu *vcpu) { struct proc *p = curproc; - if (vcpu->vc_state == VCPU_STATE_REQTERM) + if (atomic_load_int(&vcpu->vc_state) == VCPU_STATE_REQTERM) return (1); if (SIGPENDING(p) != 0) return (1); @@ -856,58 +889,26 @@ vcpu_must_stop(struct vcpu *vcpu) * other errno on uvm_map or uvm_map_immutable failures */ int -vm_share_mem(struct vm_sharemem_params *vsp, struct proc *p) +vm_share_mem(struct vm *vm, struct vm_sharemem_params *vsp, struct proc *p) { int ret = EINVAL, unmap = 0; - size_t i, failed_uao = 0, n; - struct vm *vm; - struct vm_mem_range *src, *dst; + size_t i, failed_uao = 0; + struct vm_mem_range *vmr; struct uvm_object *uao; unsigned int uvmflags; - ret = vm_find(vsp->vsp_vm_id, &vm); - if (ret) - return (ret); - - /* Check we have the expected number of ranges. */ - if (vm->vm_nmemranges != vsp->vsp_nmemranges) - goto out; - n = vm->vm_nmemranges; - - /* Check their types, sizes, and gpa's (implying page alignment). */ - for (i = 0; i < n; i++) { - src = &vm->vm_memranges[i]; - dst = &vsp->vsp_memranges[i]; - - /* - * The vm memranges were already checked during creation, so - * compare to them to confirm validity of mapping request. - */ - if (src->vmr_type != dst->vmr_type) - goto out; - if (src->vmr_gpa != dst->vmr_gpa) - goto out; - if (src->vmr_size != dst->vmr_size) - goto out; - - /* The virtual addresses will be chosen by uvm_map(). */ - if (vsp->vsp_va[i] != 0) - goto out; - } - /* Share each UVM aobj with the calling process. */ uvmflags = UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE, MAP_INHERIT_NONE, MADV_NORMAL, UVM_FLAG_CONCEAL); - for (i = 0; i < n; i++) { - dst = &vsp->vsp_memranges[i]; - if (dst->vmr_type == VM_MEM_MMIO) + for (i = 0; i < vm->vm_nmemranges; i++) { + vmr = &vm->vm_memranges[i]; + if (vmr->vmr_type == VM_MEM_MMIO) continue; uao = vm->vm_memory_slot[i]; KASSERT(uao != NULL); - ret = uvm_map(&p->p_p->ps_vmspace->vm_map, &vsp->vsp_va[i], - dst->vmr_size, uao, 0, 0, uvmflags); + vmr->vmr_size, uao, 0, 0, uvmflags); if (ret) { printf("%s: uvm_map failed: %d\n", __func__, ret); unmap = (i > 0) ? 1 : 0; @@ -917,7 +918,7 @@ vm_share_mem(struct vm_sharemem_params *vsp, struct pr uao_reference(uao); /* Add a reference for the process. */ ret = uvm_map_immutable(&p->p_p->ps_vmspace->vm_map, - vsp->vsp_va[i], vsp->vsp_va[i] + dst->vmr_size, 1); + vsp->vsp_va[i], vsp->vsp_va[i] + vmr->vmr_size, 1); if (ret) { printf("%s: uvm_map_immutable failed: %d\n", __func__, ret); @@ -931,11 +932,223 @@ out: if (unmap) { /* Unmap mapped aobjs, which drops the process's reference. */ for (i = 0; i < failed_uao; i++) { - dst = &vsp->vsp_memranges[i]; + vmr = &vm->vm_memranges[i]; uvm_unmap(&p->p_p->ps_vmspace->vm_map, - vsp->vsp_va[i], vsp->vsp_va[i] + dst->vmr_size); + vsp->vsp_va[i], vsp->vsp_va[i] + vmr->vmr_size); } } + return (ret); +} + +int +vm_create_file(struct vm_create_params *vcp, struct proc *p, struct vm *vm) +{ + int fd = -1, ret = 0; + struct file *fp = NULL; + struct filedesc *fdp = p->p_fd; + + fdplock(fdp); + + ret = falloc(p, &fp, &fd); + if (ret) + goto err; + + fp->f_flag = FREAD | FWRITE; + fp->f_type = DTYPE_VMM; + fp->f_data = vm; + fp->f_ops = &vmops; + + fdinsert(fdp, fd, 0, fp); + vcp->vcp_fd = fd; + + /* Take a reference for the file descriptor. */ + refcnt_take(&vm->vm_refcnt); + FRELE(fp, p); +err: + fdpunlock(fdp); + + return (ret); +} + +int +vm_read(struct file *fp, struct uio *uio, int fflags) +{ + return (ENXIO); +} + +int +vm_write(struct file *fp, struct uio *uio, int fflags) +{ + return (ENXIO); +} + +int +vm_kqfilter(struct file *fp, struct knote *kn) +{ + return (EINVAL); +} + +/* + * vm_ioctl + * + * Dispatcher for all virtual machine operations for the vm referenced + * by the file fp. + */ +int +vm_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p) +{ + struct vm *vm = (struct vm *)fp->f_data; + int ret = 0; + + if (vm == NULL) + return (ENXIO); + + KERNEL_ASSERT_UNLOCKED(); + + refcnt_take(&vm->vm_refcnt); + ret = vmm_dev_enter(); + if (ret != 0) + goto out; + + if (atomic_load_int(&vm->vm_dying) != VMM_VM_ALIVE) { + if (cmd == VMM_IOC_RUN) { + ((struct vm_run_params *)data)->vrp_exit_reason = + VM_EXIT_TERMINATED; + ret = 0; + } else { + ret = EBUSY; + } + goto out_active; + } + + switch (cmd) { + case VMM_IOC_RUN: + ret = vm_run(vm, (struct vm_run_params *)data); + break; + case VMM_IOC_RESETCPU: + ret = vm_resetcpu(vm, (struct vm_resetcpu_params *)data); + break; + case VMM_IOC_READREGS: + ret = vm_rwregs(vm, (struct vm_rwregs_params *)data, 0); + break; + case VMM_IOC_WRITEREGS: + ret = vm_rwregs(vm, (struct vm_rwregs_params *)data, 1); + break; + case VMM_IOC_READVMPARAMS: + ret = vm_rwvmparams(vm, (struct vm_rwvmparams_params *)data, 0); + break; + case VMM_IOC_WRITEVMPARAMS: + ret = vm_rwvmparams(vm, (struct vm_rwvmparams_params *)data, 1); + break; + case VMM_IOC_SHAREMEM: + ret = vm_share_mem(vm, (struct vm_sharemem_params *)data, p); + break; + case VMM_IOC_INTR: + ret = vm_intr_pending(vm, (struct vm_intr_params *)data); + break; + default: + ret = ENOTTY; + break; + } + +out_active: + vmm_dev_exit(); +out: refcnt_rele_wake(&vm->vm_refcnt); return (ret); } + +int +vm_close(struct file *fp, struct proc *p) +{ + int nvcpu, vm_id; + int teardown_owner = 0; + int remove_from_list = 0; + int account = 0; + int relock; + struct vm *vm = (struct vm *)fp->f_data; + + if (vm == NULL) + return (0); + + /* + * vm_close is called from multiple contexts within the kernel, + * inside and outside of vmm(4). Some callers hold the kernel lock. + * Since vmm(4) operates without the kernel lock, we need to + * unlock and relock before return. + */ + relock = _kernel_lock_held(); + if (relock) + KERNEL_UNLOCK(); + + fp->f_ops = NULL; + fp->f_data = NULL; + + /* No active teardown owner; this close owns teardown now. */ + if (atomic_cas_uint(&vm->vm_dying, VMM_VM_ALIVE, VMM_VM_TEARDOWN) == + VMM_VM_ALIVE) { + teardown_owner = 1; + remove_from_list = 1; + account = 1; + vm_request_stop(vm); + } else if (atomic_cas_uint(&vm->vm_dying, VMM_VM_DYING, + VMM_VM_TEARDOWN) == VMM_VM_DYING) { + teardown_owner = 1; + remove_from_list = 1; + account = 1; + vm_request_stop(vm); + } + if (!teardown_owner) { + refcnt_rele_wake(&vm->vm_refcnt); + if (relock) + KERNEL_LOCK(); + return (0); + } + + if (remove_from_list) { + /* Remove the vm from the global vm list. */ + rw_enter_write(&vmm_softc->vm_lock); + SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link); + rw_exit_write(&vmm_softc->vm_lock); + + /* Drop the vm_list's reference. */ + if (refcnt_rele(&vm->vm_refcnt)) + panic("%s: vm refcnt is 0 (%p)", __func__, vm); + } + + /* Wait for all VM references, including this close's, to drain. */ + refcnt_finalize(&vm->vm_refcnt, __func__); + + vm_id = vm->vm_id; + nvcpu = vm->vm_vcpu_ct; + vm_teardown(&vm); + + if (account && vm_id > 0) { + rw_enter_write(&vmm_softc->vm_lock); + vmm_softc->vm_ct--; + vmm_softc->vcpu_ct -= nvcpu; + if (vmm_softc->vm_ct < 1) + vmm_stop(); + rw_exit_write(&vmm_softc->vm_lock); + } + + if (relock) + KERNEL_LOCK(); + return (0); +} + +int +vm_stat(struct file *fp, struct stat *st, struct proc *p) +{ + struct vm *vm = (struct vm *)fp->f_data; + + if (vm == NULL) + return (0); + + memset(st, 0, sizeof(*st)); + st->st_mode = S_IFCHR; + st->st_blksize = PAGE_SIZE; + st->st_blocks = pmap_resident_count(vm->vm_pmap); + + return (0); +} blob - f50c974f67d68da56fc7fc3469eb27348b2c249e blob + 6b5195b0fbb3ea71a7d7953c68c943c04a5475de --- sys/dev/vmm/vmm.h +++ sys/dev/vmm/vmm.h @@ -54,11 +54,10 @@ struct vm_create_params { char vcp_name[VMM_MAX_NAME_LEN]; int vcp_sev; int vcp_seves; - - /* Output parameter from VMM_IOC_CREATE */ - uint32_t vcp_id; - uint32_t vcp_poscbit; - uint32_t vcp_asid[VMM_MAX_VCPUS]; + /* Output parameters from VMM_IOC_CREATE */ + uint32_t vcp_poscbit; + uint32_t vcp_asid[VMM_MAX_VCPUS]; + int vcp_fd; }; struct vm_info_result { @@ -68,7 +67,6 @@ struct vm_info_result { size_t vir_ncpus; uint8_t vir_vcpu_state[VMM_MAX_VCPUS_PER_VM]; pid_t vir_creator_pid; - uint32_t vir_id; char vir_name[VMM_MAX_NAME_LEN]; }; @@ -83,29 +81,22 @@ struct vm_info_params { struct vm_terminate_params { /* Input parameters to VMM_IOC_TERM */ - uint32_t vtp_vm_id; + pid_t vtp_creator_pid; }; struct vm_resetcpu_params { /* Input parameters to VMM_IOC_RESETCPU */ - uint32_t vrp_vm_id; uint32_t vrp_vcpu_id; struct vcpu_reg_state vrp_init_state; }; struct vm_sharemem_params { - /* Input parameters to VMM_IOC_SHAREMEM */ - uint32_t vsp_vm_id; - size_t vsp_nmemranges; - struct vm_mem_range vsp_memranges[VMM_MAX_MEM_RANGES]; - /* Output parameters from VMM_IOC_SHAREMEM */ vaddr_t vsp_va[VMM_MAX_MEM_RANGES]; }; struct vm_run_params { /* Input parameters to VMM_IOC_RUN */ - uint32_t vrp_vm_id; uint32_t vrp_vcpu_id; struct vcpu_inject_event vrp_inject; uint8_t vrp_intr_pending; /* Additional intrs pending? */ @@ -125,7 +116,6 @@ struct vm_run_params { struct vm_rwvmparams_params { /* Input parameters to VMM_IOC_READVMPARAMS/VMM_IOC_WRITEVMPARAMS */ - uint32_t vpp_vm_id; uint32_t vpp_vcpu_id; uint32_t vpp_mask; paddr_t vpp_pvclock_system_gpa; @@ -163,6 +153,10 @@ enum { VCPU_STATE_UNKNOWN, }; +#define VMM_VM_ALIVE 0U +#define VMM_VM_DYING 1U +#define VMM_VM_TEARDOWN 2U + /* * Virtual Machine * @@ -187,7 +181,7 @@ struct vm { char vm_name[VMM_MAX_NAME_LEN]; struct refcnt vm_refcnt; /* [a] */ - unsigned int vm_dying; /* [a] */ + unsigned int vm_dying; /* [a] VMM_VM_* */ struct vcpu_head vm_vcpu_list; /* [v] */ uint32_t vm_vcpu_ct; /* [v] */ @@ -244,25 +238,11 @@ extern struct pool vcpu_pool; extern struct cfdriver vmm_cd; extern const struct cfattach vmm_ca; -int vmm_probe(struct device *, void *, void *); -int vmm_activate(struct device *, int); -void vmm_attach(struct device *, struct device *, void *); -int vmmopen(dev_t, int, int, struct proc *); -int vmmclose(dev_t, int, int, struct proc *); -int vm_find(uint32_t, struct vm **); -int vmmioctl_machdep(dev_t, u_long, caddr_t, int, struct proc *); -int pledge_ioctl_vmm(struct proc *, long); +int vm_find_file(int, struct proc *, struct vm **); struct vcpu *vm_find_vcpu(struct vm *, uint32_t); -int vm_create(struct vm_create_params *, struct proc *); -size_t vm_create_check_mem_ranges(struct vm_create_params *); -void vm_teardown(struct vm **); -int vm_get_info(struct vm_info_params *); -int vm_terminate(struct vm_terminate_params *); -int vm_resetcpu(struct vm_resetcpu_params *); -int vm_rwvmparams(struct vm_rwvmparams_params *, int); +int vm_rwvmparams(struct vm *, struct vm_rwvmparams_params *, int); int vcpu_must_stop(struct vcpu *); -int vm_share_mem(struct vm_sharemem_params *, struct proc *); -int vm_run(struct vm_run_params *); +int vm_run(struct vm *, struct vm_run_params *); #ifdef VMM_DEBUG void dump_vcpu(struct vcpu *); blob - a891eecd1182383629cb4923bee2e879a840c447 blob + e5c5dbf15ab0262e080ad902b5713cb02dceb9e1 --- sys/kern/kern_pledge.c +++ sys/kern/kern_pledge.c @@ -75,6 +75,7 @@ #include "vmm.h" #include "psp.h" #include <machine/conf.h> +#include <dev/vmm/vmm.h> #endif #include "drm.h" @@ -1348,12 +1349,30 @@ pledge_ioctl(struct proc *p, long com, struct file *fp #if NVMM > 0 if ((pledge & PLEDGE_VMM)) { + if (fp->f_type == DTYPE_VMM) { + switch (com) { + case VMM_IOC_RUN: + case VMM_IOC_RESETCPU: + case VMM_IOC_READREGS: + case VMM_IOC_WRITEREGS: + case VMM_IOC_READVMPARAMS: + case VMM_IOC_WRITEVMPARAMS: + case VMM_IOC_SHAREMEM: + case VMM_IOC_INTR: + return (0); + default: + break; + } + } if (fp->f_type == DTYPE_VNODE && vp->v_type == VCHR && cdevsw[major(vp->v_rdev)].d_open == vmmopen) { - error = pledge_ioctl_vmm(p, com); - if (error == 0) - return 0; + switch (com) { + case VMM_IOC_CREATE: + case VMM_IOC_INFO: + case VMM_IOC_TERM: + return (0); + } } } #endif blob - 25c8267ae5d1761a3c872157d68507d1d4be1e9c blob + dfec6d00cb98178ba3ca5155c121f57f9bfa17a6 --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -1293,8 +1293,8 @@ morespace: if (error) goto fail; - /* kqueue descriptors cannot be copied */ - if (fp->f_type == DTYPE_KQUEUE) { + /* kqueue and vmm descriptors cannot be copied */ + if (fp->f_type == DTYPE_KQUEUE || fp->f_type == DTYPE_VMM) { error = EINVAL; goto fail; } blob - b29b43122a4755a18c9b0b0cd3733566f09dda5b blob + 7aa3c1cedbc827cce93b3abdd611d82ee89ce81f --- sys/sys/file.h +++ sys/sys/file.h @@ -46,6 +46,7 @@ #define DTYPE_KQUEUE 4 /* event queue */ #define DTYPE_DMABUF 5 /* DMA buffer (for DRM) */ #define DTYPE_SYNC 6 /* sync file (for DRM) */ +#define DTYPE_VMM 7 /* vmm(4) virtual machine */ #ifdef _KERNEL struct proc; blob - 3379671505b1ffe9f7ef66c32fc8e57113730a0d blob + dcf07d38bde5c7062a4ad614e7ce9777cf3819d1 --- sys/sys/pledge.h +++ sys/sys/pledge.h @@ -128,7 +128,7 @@ int pledge_sockopt(struct proc *p, int set, int level, int pledge_socket(struct proc *p, int domain, unsigned int state); int pledge_ioctl(struct proc *p, long com, struct file *); int pledge_ioctl_drm(struct proc *p, long com, dev_t device); -int pledge_ioctl_vmm(struct proc *p, long com); +int pledge_ioctl_vmm_dev(struct proc *p, long com); int pledge_ioctl_psp(struct proc *p, long com); int pledge_flock(struct proc *p); int pledge_fcntl(struct proc *p, int cmd); blob - bbafef0e084b639c6c7c47d67188b1d52988191b blob + 6c8446643d730bf88e3271f586732e7d90e8131b --- usr.bin/fstat/fstat.c +++ usr.bin/fstat/fstat.c @@ -127,6 +127,7 @@ void print_inet6_details(struct kinfo_file *); void print_sock_details(struct kinfo_file *); void socktrans(struct kinfo_file *); void vtrans(struct kinfo_file *); +void vmmtrans(struct kinfo_file *); const char *inet6_addrstr(struct in6_addr *); int signame_to_signum(char *); void hide(void *p); @@ -414,6 +415,10 @@ fstat_dofile(struct kinfo_file *kf) if (checkfile == 0) kqueuetrans(kf); break; + case DTYPE_VMM: + if (checkfile == 0) + vmmtrans(kf); + break; default: if (vflg) { warnx("unknown file type %d for file %d of pid %ld", @@ -525,6 +530,18 @@ vtrans(struct kinfo_file *kf) } void +vmmtrans(struct kinfo_file *kf) +{ + PREFIX(kf->fd_fd); + + printf(" "); + + printf("vmm "); + hide((void *)(uintptr_t)kf->f_data); + putchar('\n'); +} + +void pipetrans(struct kinfo_file *kf) { void *maxaddr; blob - b05fb152b254d31155d1d75cf3603dbe69be4306 blob + 26b77db4c52b83ab25bb4f750efd30b087ceb1e0 --- usr.sbin/vmd/arm64_vm.c +++ usr.sbin/vmd/arm64_vm.c @@ -104,13 +104,13 @@ intr_ack(struct vmd_vm *vm) } void -vcpu_assert_irq(uint32_t vm_id, uint32_t vcpu_id, int irq) +vcpu_assert_irq(int fd, uint32_t vcpu_id, int irq) { fatalx("%s: unimplemented", __func__); } void -vcpu_deassert_irq(uint32_t vm_id, uint32_t vcpu_id, int irq) +vcpu_deassert_irq(int fd, uint32_t vcpu_id, int irq) { fatalx("%s: unimplemented", __func__); } blob - 09907635f0690399c4aa7ab2d804baf372df97b3 blob + 25fd0cbcf5f345041e659da67afa09e7857b7aee --- usr.sbin/vmd/dhcp.c +++ usr.sbin/vmd/dhcp.c @@ -139,7 +139,7 @@ dhcp_request(struct virtio_dev *dev, char *buf, size_t if (vionet->pxeboot) { strlcpy(resp.file, "auto_install", sizeof resp.file); - vm = vm_getbyid(dev->vmm_id); + vm = vm_getbyvmid(dev->vm_id); if (vm && res_hnok(vm->vm_params.vmc_name)) hostname = vm->vm_params.vmc_name; } blob - 00b0945da33e131f897f18a0fb692cc660f97114 blob + 469dc82f6eb439b97f7cce5baa38015199572673 --- usr.sbin/vmd/i8253.c +++ usr.sbin/vmd/i8253.c @@ -73,29 +73,29 @@ i8253_pipe_dispatch(int fd, short event, void *arg) * Initialize the emulated i8253 PIT. * * Parameters: - * vm_id: vmm(4)-assigned ID of the VM + * vm_fd: file descriptor of the VM */ void -i8253_init(uint32_t vm_id) +i8253_init(int vm_fd) { memset(&i8253_channel, 0, sizeof(struct i8253_channel)); clock_gettime(CLOCK_MONOTONIC, &i8253_channel[0].ts); i8253_channel[0].start = 0xFFFF; i8253_channel[0].mode = TIMER_INTTC; i8253_channel[0].last_r = 1; - i8253_channel[0].vm_id = vm_id; + i8253_channel[0].vm_fd = vm_fd; i8253_channel[0].state = 0; i8253_channel[1].start = 0xFFFF; i8253_channel[1].mode = TIMER_INTTC; i8253_channel[1].last_r = 1; - i8253_channel[1].vm_id = vm_id; + i8253_channel[1].vm_fd = vm_fd; i8253_channel[1].state = 0; i8253_channel[2].start = 0xFFFF; i8253_channel[2].mode = TIMER_INTTC; i8253_channel[2].last_r = 1; - i8253_channel[2].vm_id = vm_id; + i8253_channel[2].vm_fd = vm_fd; i8253_channel[2].state = 0; evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]); @@ -370,7 +370,7 @@ i8253_fire(int fd, short type, void *arg) struct timeval tv; struct i8253_channel *ctr = (struct i8253_channel *)arg; - vcpu_assert_irq(ctr->vm_id, 0, 0); + vcpu_assert_irq(ctr->vm_fd, 0, 0); if (ctr->mode != TIMER_INTTC) { timerclear(&tv); blob - c938c42d4cb9030011b009928ba8e2df5483da37 blob + cd173ac300a3532eab1ee5c45cd9aa4fde3d6b29 --- usr.sbin/vmd/i8253.h +++ usr.sbin/vmd/i8253.h @@ -39,12 +39,12 @@ struct i8253_channel { uint8_t mode; /* counter mode */ uint8_t rbs; /* channel is in readback status mode */ struct event timer; /* timer event for this counter */ - uint32_t vm_id; /* owning VM id */ + int vm_fd; /* owning VM fd */ int in_use; /* denotes if this counter was ever used */ uint8_t state; /* 0 if channel is counting, 1 if fired */ }; -void i8253_init(uint32_t); +void i8253_init(int); void i8253_reset(uint8_t); void i8253_fire(int, short, void *); uint8_t vcpu_exit_i8253(struct vm_run_params *); blob - 0105cfae4292ce1fd50e82c93cdc45a569acc46c blob + 39b3c9d9d41c86acf2687801a5a0d7b5dc996b59 --- usr.sbin/vmd/mc146818.c +++ usr.sbin/vmd/mc146818.c @@ -50,7 +50,7 @@ struct mc146818 { time_t now; uint8_t idx; uint8_t regs[NVRAM_SIZE]; - uint32_t vm_id; + int vm_fd; struct event sec; struct timeval sec_tv; struct event per; @@ -158,12 +158,12 @@ rtc_fireper(int fd, short type, void *arg) * Initializes the emulated RTC/NVRAM * * Parameters: - * vm_id: VM ID to which this RTC belongs + * vm_fd: vm file descriptor to which this RTC belongs * memlo: size of memory in bytes between 16MB .. 4GB * memhi: size of memory in bytes after 4GB */ void -mc146818_init(uint32_t vm_id, uint64_t memlo, uint64_t memhi) +mc146818_init(int vm_fd, uint64_t memlo, uint64_t memhi) { memset(&rtc, 0, sizeof(rtc)); time(&rtc.now); @@ -182,7 +182,7 @@ mc146818_init(uint32_t vm_id, uint64_t memlo, uint64_t rtc.regs[NVRAM_SMP_COUNT] = 0; rtc_updateregs(); - rtc.vm_id = vm_id; + rtc.vm_fd = vm_fd; timerclear(&rtc.sec_tv); rtc.sec_tv.tv_sec = 1; @@ -192,7 +192,7 @@ mc146818_init(uint32_t vm_id, uint64_t memlo, uint64_t evtimer_set(&rtc.sec, rtc_fire1, NULL); evtimer_add(&rtc.sec, &rtc.sec_tv); - evtimer_set(&rtc.per, rtc_fireper, (void *)(intptr_t)rtc.vm_id); + evtimer_set(&rtc.per, rtc_fireper, (void *)(intptr_t)rtc.vm_fd); vm_pipe_init(&dev_pipe, mc146818_pipe_dispatch); event_add(&dev_pipe.read_ev, NULL); blob - e7cef801303643a57cb905e1698a8dae3b7f5a9b blob + 64118764de1e79e6aa3dcdf9964819819a03217c --- usr.sbin/vmd/mc146818.h +++ usr.sbin/vmd/mc146818.h @@ -15,7 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -void mc146818_init(uint32_t, uint64_t, uint64_t); +void mc146818_init(int, uint64_t, uint64_t); uint8_t vcpu_exit_mc146818(struct vm_run_params *vrp); void mc146818_stop(void); void mc146818_start(void); blob - 97970e2155b0890e1914d923d8bcedc1e5dc93ab blob + 01164b93dffc9ab87897b9c5be6b1b6c57970127 --- usr.sbin/vmd/ns8250.c +++ usr.sbin/vmd/ns8250.c @@ -36,7 +36,7 @@ struct ns8250_dev com1_dev; static struct vm_dev_pipe dev_pipe; static void com_rcv_event(int, short, void *); -static void com_rcv(struct ns8250_dev *, uint32_t, uint32_t); +static void com_rcv(struct ns8250_dev *, uint32_t); /* * ns8250_pipe_dispatch @@ -78,12 +78,12 @@ ratelimit(int fd, short type, void *arg) com1_dev.regs.iir |= IIR_TXRDY; com1_dev.regs.iir &= ~IIR_NOPEND; - vcpu_assert_irq(com1_dev.vmid, 0, com1_dev.irq); + vcpu_assert_irq(com1_dev.vm_fd, 0, com1_dev.irq); mutex_unlock(&com1_dev.mutex); } void -ns8250_init(int fd, uint32_t vmid) +ns8250_init(int fd, int vm_fd) { int ret; @@ -95,9 +95,9 @@ ns8250_init(int fd, uint32_t vmid) } com1_dev.fd = fd; + com1_dev.vm_fd = vm_fd; com1_dev.irq = 4; com1_dev.portid = NS8250_COM1; - com1_dev.vmid = vmid; com1_dev.byte_out = 0; com1_dev.regs.divlo = 1; com1_dev.baudrate = 115200; @@ -118,7 +118,7 @@ ns8250_init(int fd, uint32_t vmid) com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10; event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST, - com_rcv_event, (void *)(intptr_t)vmid); + com_rcv_event, (void *)(intptr_t)vm_fd); /* * Whenever fd is writable implies that the pty slave is connected. @@ -126,7 +126,7 @@ ns8250_init(int fd, uint32_t vmid) * be reached. */ event_set(&com1_dev.wake, com1_dev.fd, EV_WRITE, - com_rcv_event, (void *)(intptr_t)vmid); + com_rcv_event, (void *)(intptr_t)vm_fd); event_add(&com1_dev.wake, NULL); /* Rate limiter for simulating baud rate */ @@ -150,12 +150,12 @@ com_rcv_event(int fd, short kind, void *arg) } if ((com1_dev.regs.lsr & LSR_RXRDY) == 0) - com_rcv(&com1_dev, (uintptr_t)arg, 0); + com_rcv(&com1_dev, 0); /* If pending interrupt, inject */ if ((com1_dev.regs.iir & IIR_NOPEND) == 0) { /* XXX: vcpu_id */ - vcpu_assert_irq((uintptr_t)arg, 0, com1_dev.irq); + vcpu_assert_irq(com1_dev.vm_fd, 0, com1_dev.irq); } mutex_unlock(&com1_dev.mutex); @@ -192,7 +192,7 @@ com_rcv_handle_break(struct ns8250_dev *com, uint8_t c * Must be called with the mutex of the com device acquired */ static void -com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32_t vcpu_id) +com_rcv(struct ns8250_dev *com, uint32_t vcpu_id) { char buf[2]; ssize_t sz; @@ -245,7 +245,7 @@ com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32 * interrupt to inject, or 0xFF if nothing to inject */ uint8_t -vcpu_process_com_data(struct vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id) +vcpu_process_com_data(struct vm_exit *vei, uint32_t vcpu_id) { /* * vei_dir == VEI_DIR_OUT : out instruction @@ -612,8 +612,7 @@ vcpu_exit_com(struct vm_run_params *vrp) vcpu_process_com_scr(vei); break; case COM1_DATA: - intr = vcpu_process_com_data(vei, vrp->vrp_vm_id, - vrp->vrp_vcpu_id); + intr = vcpu_process_com_data(vei, vrp->vrp_vcpu_id); break; } blob - 7ff4f524d92f1a89eee796b01e3049a1e026db7f blob + e4ec36d1da494536b71086930de0d4df6386e772 --- usr.sbin/vmd/ns8250.h +++ usr.sbin/vmd/ns8250.h @@ -68,16 +68,16 @@ struct ns8250_dev { struct timeval rate_tv; enum ns8250_portid portid; int fd; + int vm_fd; int irq; - uint32_t vmid; uint64_t byte_out; uint32_t baudrate; uint32_t pause_ct; }; -void ns8250_init(int, uint32_t); +void ns8250_init(int, int); uint8_t vcpu_exit_com(struct vm_run_params *); -uint8_t vcpu_process_com_data(struct vm_exit *, uint32_t, uint32_t); +uint8_t vcpu_process_com_data(struct vm_exit *, uint32_t); void vcpu_process_com_lcr(struct vm_exit *); void vcpu_process_com_lsr(struct vm_exit *); void vcpu_process_com_ier(struct vm_exit *); blob - b60f51711dbb30d2ab9d5f917607c77d3dee28eb blob + e24c794037233838424b137df11ae5d212157262 --- usr.sbin/vmd/psp.c +++ usr.sbin/vmd/psp.c @@ -174,15 +174,14 @@ psp_launch_update(uint32_t handle, vaddr_t v, size_t l * log it for now. */ int -psp_encrypt_state(uint32_t handle, uint32_t asid, uint32_t vmid, - uint32_t vcpuid) +psp_encrypt_state(uint32_t handle, uint32_t asid, int vmfd, uint32_t vcpuid) { struct psp_encrypt_state es; memset(&es, 0, sizeof(es)); es.handle = handle; es.asid = asid; - es.vmid = vmid; + es.vmfd = vmfd; es.vcpuid = vcpuid; if (ioctl(env->vmd_psp_fd, PSP_IOC_ENCRYPT_STATE, &es) < 0) { blob - 0fc14a3ce218bfbf85417191c40c6456e204ce89 blob + 57b428a8a12a478e166033d4435345ade089eb21 --- usr.sbin/vmd/sev.c +++ usr.sbin/vmd/sev.c @@ -208,7 +208,7 @@ sev_encrypt_state(struct vmd_vm *vm, int vcpu_id) return (0); if (psp_encrypt_state(vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id], - vm->vm_vmmid, vcpu_id)) { + vm->vm_fd, vcpu_id)) { log_warnx("%s: failed to encrypt state: 0x%x 0x%x 0x%0x 0x%0x", __func__, vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id], vm->vm_vmid, vcpu_id); blob - 6523170fc35095565d2b6627c2adea9667e52d48 blob + adc14142bfd8daa0834a9d2343c0cdc37a5f8981 --- usr.sbin/vmd/vioblk.c +++ usr.sbin/vmd/vioblk.c @@ -61,7 +61,7 @@ disk_type(enum vm_disk_fmt type) } __dead void -vioblk_main(int fd, int fd_vmm) +vioblk_main(int fd, int vm_fd) { struct virtio_dev dev; struct vioblk_dev *vioblk = NULL; @@ -99,9 +99,9 @@ vioblk_main(int fd, int fd_vmm) vioblk = &dev.vioblk; log_debug("%s: got viblk dev. num disk fds = %d, sync fd = %d, " - "async fd = %d, capacity = %lld seg_max = %u, vmm fd = %d", + "async fd = %d, capacity = %lld seg_max = %u, vm fd = %d", __func__, vioblk->ndisk_fd, dev.sync_fd, dev.async_fd, - vioblk->capacity, vioblk->seg_max, fd_vmm); + vioblk->capacity, vioblk->seg_max, vm_fd); /* Receive our vm information from the vm process. */ memset(&vm, 0, sizeof(vm)); @@ -117,7 +117,7 @@ vioblk_main(int fd, int fd_vmm) log_procinit("vm/%s/vioblk%d", vm.vm_params.vmc_name, vioblk->idx); /* Now that we have our vm information, we can remap memory. */ - ret = remap_guest_mem(&vm, fd_vmm); + ret = remap_guest_mem(&vm, vm_fd); if (ret) { log_warnx("failed to remap guest memory"); goto fail; @@ -126,7 +126,7 @@ vioblk_main(int fd, int fd_vmm) /* * We no longer need /dev/vmm access. */ - close_fd(fd_vmm); + close_fd(vm_fd); if (pledge("stdio", NULL) == -1) fatal("pledge2"); blob - 5ba13f4e9ce09879dc39ff5ca6ce149865810976 blob + d1c7e3feb97767265c410410941f46a932850942 --- usr.sbin/vmd/vionet.c +++ usr.sbin/vmd/vionet.c @@ -105,7 +105,7 @@ pthread_rwlock_t lock = NULL; /* Guards device config int rx_enabled = 0; /* 1: we expect to read the tap, 0: wait for notify. */ __dead void -vionet_main(int fd, int fd_vmm) +vionet_main(int fd, int vm_fd) { struct virtio_dev dev; struct vionet_dev *vionet = NULL; @@ -141,8 +141,8 @@ vionet_main(int fd, int fd_vmm) vionet = &dev.vionet; log_debug("%s: got vionet dev. tap fd = %d, syncfd = %d, asyncfd = %d" - ", vmm fd = %d", __func__, vionet->data_fd, dev.sync_fd, - dev.async_fd, fd_vmm); + ", vm fd = %d", __func__, vionet->data_fd, dev.sync_fd, + dev.async_fd, vm_fd); /* Receive our vm information from the vm process. */ memset(&vm, 0, sizeof(vm)); @@ -157,7 +157,7 @@ vionet_main(int fd, int fd_vmm) log_procinit("vm/%s/vionet%d", vm.vm_params.vmc_name, vionet->idx); /* Now that we have our vm information, we can remap memory. */ - ret = remap_guest_mem(&vm, fd_vmm); + ret = remap_guest_mem(&vm, vm_fd); if (ret) { fatal("%s: failed to remap", __func__); goto fail; @@ -166,7 +166,7 @@ vionet_main(int fd, int fd_vmm) /* * We no longer need /dev/vmm access. */ - close_fd(fd_vmm); + close_fd(vm_fd); if (pledge("stdio", NULL) == -1) fatal("pledge2"); blob - 355ce4069ff94b056a5f649dca64e4f49acf66ad blob + b012d39fcfe5ff475c962d58d38b312eb27ed640 --- usr.sbin/vmd/vioscsi.c +++ usr.sbin/vmd/vioscsi.c @@ -57,7 +57,7 @@ static uint32_t vioscsi_read(struct virtio_dev *, stru static int vioscsi_write(struct virtio_dev *, struct viodev_msg *); __dead void -vioscsi_main(int fd, int fd_vmm) +vioscsi_main(int fd, int vm_fd) { struct virtio_dev dev; struct vioscsi_dev *vioscsi = NULL; @@ -90,8 +90,8 @@ vioscsi_main(int fd, int fd_vmm) vioscsi = &dev.vioscsi; log_debug("%s: got vioscsi dev. cdrom fd = %d, syncfd = %d, " - "asyncfd = %d, vmm fd = %d", __func__, vioscsi->cdrom_fd, - dev.sync_fd, dev.async_fd, fd_vmm); + "asyncfd = %d, vm fd = %d", __func__, vioscsi->cdrom_fd, + dev.sync_fd, dev.async_fd, vm_fd); /* Receive our vm information from the vm process. */ memset(&vm, 0, sizeof(vm)); @@ -107,16 +107,16 @@ vioscsi_main(int fd, int fd_vmm) log_procinit("vm/%s/vioscsi", vm.vm_params.vmc_name); /* Now that we have our vm information, we can remap memory. */ - ret = remap_guest_mem(&vm, fd_vmm); + ret = remap_guest_mem(&vm, vm_fd); if (ret) { log_warnx("failed to remap guest memory"); goto fail; } /* - * We no longer need /dev/vmm access. + * We no longer need VM fd access. */ - close_fd(fd_vmm); + close_fd(vm_fd); if (pledge("stdio", NULL) == -1) fatal("pledge2"); blob - 03a6e761dfc183f147434028b796ec1c03f3790d blob + e24a5b4478ce61ba85514c93c1e4570023bd7cef --- usr.sbin/vmd/virtio.c +++ usr.sbin/vmd/virtio.c @@ -33,6 +33,7 @@ #include <errno.h> #include <event.h> +#include <fcntl.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -696,7 +697,7 @@ virtio_io_isr(int dir, uint16_t reg, uint32_t *data, u if (dir == VEI_DIR_IN) { *data = dev->isr; dev->isr = 0; - vcpu_deassert_irq(dev->vmm_id, 0, dev->irq); + vcpu_deassert_irq(dev->vm_fd, 0, dev->irq); } return (0); @@ -795,7 +796,7 @@ vmmci_ctl(struct virtio_dev *dev, unsigned int cmd) /* Trigger interrupt */ dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE; - vcpu_assert_irq(dev->vmm_id, 0, dev->irq); + vcpu_assert_irq(dev->vm_fd, 0, dev->irq); /* Add ACK timeout */ tv.tv_sec = VMMCI_TIMEOUT_SHORT; @@ -807,7 +808,7 @@ vmmci_ctl(struct virtio_dev *dev, unsigned int cmd) v->cmd = cmd; dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE; - vcpu_assert_irq(dev->vmm_id, 0, dev->irq); + vcpu_assert_irq(dev->vm_fd, 0, dev->irq); } else { log_debug("%s: RTC sync skipped (guest does not " "support RTC sync)", __func__); @@ -851,7 +852,7 @@ vmmci_ack(struct virtio_dev *dev, unsigned int cmd) */ if (v->cmd == 0) { log_debug("%s: vm %u requested shutdown", __func__, - dev->vmm_id); + dev->vm_fd); vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_SHORT); return; } @@ -866,13 +867,13 @@ vmmci_ack(struct virtio_dev *dev, unsigned int cmd) */ if (cmd == v->cmd) { log_debug("%s: vm %u acknowledged shutdown request", - __func__, dev->vmm_id); + __func__, dev->vm_fd); vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_LONG); } break; case VMMCI_SYNCRTC: log_debug("%s: vm %u acknowledged RTC sync request", - __func__, dev->vmm_id); + __func__, dev->vm_fd); v->cmd = VMMCI_NONE; break; default: @@ -891,7 +892,7 @@ vmmci_timeout(int fd, short type, void *arg) fatalx("%s: device is not a vmmci device", __func__); v = &dev->vmmci; - log_debug("vm %u shutdown", dev->vmm_id); + log_debug("vm %u shutdown", dev->vm_fd); vm_shutdown(v->cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN); } @@ -980,7 +981,7 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_ case VIRTIO_CONFIG_ISR_STATUS: *data = dev->isr; dev->isr = 0; - vcpu_deassert_irq(dev->vmm_id, 0, dev->irq); + vcpu_deassert_irq(dev->vm_fd, 0, dev->irq); break; } } @@ -1111,7 +1112,7 @@ virtio_init(struct vmd_vm *vm, int child_cdrom, /* Device specific initializiation. */ dev->dev_type = VMD_DEVTYPE_NET; - dev->vmm_id = vm->vm_vmmid; + dev->vm_fd = vm->vm_fd; dev->vionet.data_fd = child_taps[i]; /* MAC address has been assigned by the parent */ @@ -1180,7 +1181,7 @@ virtio_init(struct vmd_vm *vm, int child_cdrom, /* Device specific initialization. */ dev->dev_type = VMD_DEVTYPE_DISK; - dev->vmm_id = vm->vm_vmmid; + dev->vm_fd = vm->vm_fd; dev->vioblk.seg_max = VIOBLK_SEG_MAX_DEFAULT; /* @@ -1228,7 +1229,7 @@ virtio_init(struct vmd_vm *vm, int child_cdrom, /* Device specific initialization. */ dev->dev_type = VMD_DEVTYPE_SCSI; - dev->vmm_id = vm->vm_vmmid; + dev->vm_fd = vm->vm_fd; dev->vioscsi.cdrom_fd = child_cdrom; dev->vioscsi.locked = 0; dev->vioscsi.lba = 0; @@ -1421,7 +1422,7 @@ virtio_dev_init(struct vmd_vm *vm, struct virtio_dev * dev->irq = pci_get_dev_irq(pci_id); dev->isr = 0; dev->vm_id = vm->vm_vmid; - dev->vmm_id = vm->vm_vmmid; + dev->vm_fd = vm->vm_fd; dev->device_feature = features; @@ -1684,14 +1685,24 @@ virtio_dev_launch(struct vmd_vm *vm, struct virtio_dev if (virtio_dev_closefds(dev_entry) == -1) fatalx("unable to close other virtio devs"); } + /* + * Device helpers only need the VM file descriptor passed via + * argv -i for remap_guest_mem(); close inherited control fds. + */ + if (env->vmd_vmm_fd != -1 && env->vmd_vmm_fd != vm->vm_fd) + close_fd(env->vmd_vmm_fd); + if (env->vmd_psp_fd != -1 && env->vmd_psp_fd != vm->vm_fd) + close_fd(env->vmd_psp_fd); memset(num, 0, sizeof(num)); snprintf(num, sizeof(num), "%d", sync_fds[1]); memset(vmm_fd, 0, sizeof(vmm_fd)); - snprintf(vmm_fd, sizeof(vmm_fd), "%d", env->vmd_vmm_fd); + snprintf(vmm_fd, sizeof(vmm_fd), "%d", vm->vm_fd); memset(vm_name, 0, sizeof(vm_name)); snprintf(vm_name, sizeof(vm_name), "%s", vm->vm_params.vmc_name); + if (vm->vm_fd > 0) + fcntl(vm->vm_fd, F_SETFD, 0); /* keep vm fd across exec */ t[0] = dev->dev_type; t[1] = '\0'; @@ -1820,14 +1831,14 @@ virtio_dispatch_dev(int fd, short event, void *arg) static int handle_dev_msg(struct viodev_msg *msg, struct virtio_dev *gdev) { - uint32_t vmm_id = gdev->vmm_id; + int vm_fd = gdev->vm_fd; switch (msg->type) { case VIODEV_MSG_KICK: if (msg->state == INTR_STATE_ASSERT) - vcpu_assert_irq(vmm_id, msg->vcpu, msg->irq); + vcpu_assert_irq(vm_fd, msg->vcpu, msg->irq); else if (msg->state == INTR_STATE_DEASSERT) - vcpu_deassert_irq(vmm_id, msg->vcpu, msg->irq); + vcpu_deassert_irq(vm_fd, msg->vcpu, msg->irq); break; case VIODEV_MSG_READY: log_debug("%s: device reports ready", __func__); @@ -1931,9 +1942,9 @@ virtio_pci_io(int dir, uint16_t reg, uint32_t *data, u * device performs a register read. */ if (msg.state == INTR_STATE_ASSERT) - vcpu_assert_irq(dev->vmm_id, msg.vcpu, msg.irq); + vcpu_assert_irq(dev->vm_fd, msg.vcpu, msg.irq); else if (msg.state == INTR_STATE_DEASSERT) - vcpu_deassert_irq(dev->vmm_id, msg.vcpu, msg.irq); + vcpu_deassert_irq(dev->vm_fd, msg.vcpu, msg.irq); } else { log_warnx("%s: expected IO_READ, got %d", __func__, msg.type); blob - 0af586eb6c5e599d8641020555f2f57411154b03 blob + ba591e18bd8dd5952bbd2090bdad573d387bff27 --- usr.sbin/vmd/virtio.h +++ usr.sbin/vmd/virtio.h @@ -341,10 +341,10 @@ struct virtio_dev { /* Multi-process enabled. */ struct vioblk_dev vioblk; struct vionet_dev vionet; + struct vioscsi_dev vioscsi; /* In-process only. */ struct vmmci_dev vmmci; - struct vioscsi_dev vioscsi; }; struct virtio_io_cfg cfg; /* Virtio 0.9 */ @@ -369,8 +369,8 @@ struct virtio_dev { int sync_fd; /* fd for synchronous channel */ int async_fd; /* fd for async channel */ + int vm_fd; /* vmm(4) vm file descriptor [r] */ uint32_t vm_id; /* vmd(8) vm identifier [r] */ - uint32_t vmm_id; /* vmm(4) vm identifier [r] */ pid_t dev_pid; /* pid of emulator process */ char dev_type; /* device type (as char) */ SLIST_ENTRY(virtio_dev) dev_next; blob - ff4551814eafc8d0284ae40e63d53824737d8431 blob + 6d9dd95ed0c33bd783d6d5475aabe82d0c7392c8 --- usr.sbin/vmd/vm.c +++ usr.sbin/vmd/vm.c @@ -194,10 +194,8 @@ start_vm(struct vmd_vm *vm, int fd) errno = ret; log_warn("could not create vm"); } - - /* Let the vmm process know we failed by sending a 0 vm id. */ - vm->vm_vmmid = 0; - atomicio(vwrite, fd, &vm->vm_vmmid, sizeof(vm->vm_vmmid)); + /* Let the vmm process know we failed by sending the error code. */ + atomicio(vwrite, fd, &ret, sizeof(ret)); return (ret); } @@ -217,14 +215,10 @@ start_vm(struct vmd_vm *vm, int fd) log_warn("failed to set nonblocking mode on console"); return (1); } - - /* - * We now let the vmm process know we were successful by sending it our - * vmm(4) assigned vm id. - */ - if (atomicio(vwrite, fd, &vm->vm_vmmid, sizeof(vm->vm_vmmid)) != - sizeof(vm->vm_vmmid)) { - log_warn("failed to send created vm id to vmm process"); + /* We now let the vmm process know we were successful. */ + ret = 0; + if (atomicio(vwrite, fd, &ret, sizeof(ret)) != sizeof(ret)) { + log_warn("failed to send vm start status to vmm process"); return (1); } @@ -485,7 +479,7 @@ unpause_vm(struct vmd_vm *vm) * the register state provided * * Parameters - * vmid: VM ID to reset + * fd: vm file descriptor to reset * vcpu_id: VCPU ID to reset * vrs: the register state to initialize * @@ -495,18 +489,17 @@ unpause_vm(struct vmd_vm *vm) * valid) */ int -vcpu_reset(uint32_t vmid, uint32_t vcpu_id, struct vcpu_reg_state *vrs) +vcpu_reset(int fd, uint32_t vcpu_id, struct vcpu_reg_state *vrs) { struct vm_resetcpu_params vrp; memset(&vrp, 0, sizeof(vrp)); - vrp.vrp_vm_id = vmid; vrp.vrp_vcpu_id = vcpu_id; memcpy(&vrp.vrp_init_state, vrs, sizeof(struct vcpu_reg_state)); - log_debug("%s: resetting vcpu %d for vm %d", __func__, vcpu_id, vmid); + log_debug("%s: resetting vcpu %d", __func__, vcpu_id); - if (ioctl(env->vmd_vmm_fd, VMM_IOC_RESETCPU, &vrp) == -1) + if (ioctl(fd, VMM_IOC_RESETCPU, &vrp) == -1) return (errno); return (0); @@ -556,10 +549,15 @@ vmm_create_vm(struct vmd_vm *vm) vcp.vcp_sev = vmc->vmc_sev; vcp.vcp_seves = vmc->vmc_seves; - if (ioctl(env->vmd_vmm_fd, VMM_IOC_CREATE, &vcp) == -1) + if (ioctl(env->vmd_vmm_fd, VMM_IOC_CREATE, &vcp) == -1) { + close_fd(env->vmd_vmm_fd); + env->vmd_vmm_fd = -1; return (errno); + } + close_fd(env->vmd_vmm_fd); + env->vmd_vmm_fd = -1; - vm->vm_vmmid = vcp.vcp_id; + vm->vm_fd = vcp.vcp_fd; for (i = 0; i < vcp.vcp_ncpus; i++) vm->vm_sev_asid[i] = vcp.vcp_asid[i]; for (i = 0; i < vmc->vmc_nmemranges; i++) @@ -640,10 +638,9 @@ run_vm(struct vmd_vm *vm, struct vcpu_reg_state *vrs) /* caller will exit, so skip freeing */ return (ENOMEM); } - vrp[i]->vrp_vm_id = vm->vm_vmmid; vrp[i]->vrp_vcpu_id = i; - if (vcpu_reset(vm->vm_vmmid, i, vrs)) { + if (vcpu_reset(vm->vm_fd, i, vrs)) { log_warnx("cannot reset vcpu %zu", i); return (EIO); } @@ -893,7 +890,7 @@ vcpu_run_loop(void *arg) /* Still more interrupts pending? */ vrp->vrp_intr_pending = intr_pending(current_vm); - if (ioctl(env->vmd_vmm_fd, VMM_IOC_RUN, vrp) == -1) { + if (ioctl(current_vm->vm_fd, VMM_IOC_RUN, vrp) == -1) { /* If run ioctl failed, exit */ ret = errno; log_warn("%s: vm %d / vcpu %d run ioctl failed", @@ -930,17 +927,16 @@ vcpu_run_loop(void *arg) } int -vcpu_intr(uint32_t vmm_id, uint32_t vcpu_id, uint8_t intr) +vcpu_intr(int fd, uint32_t vcpu_id, uint8_t intr) { struct vm_intr_params vip; memset(&vip, 0, sizeof(vip)); - vip.vip_vm_id = vmm_id; vip.vip_vcpu_id = vcpu_id; /* XXX always 0? */ vip.vip_intr = intr; - if (ioctl(env->vmd_vmm_fd, VMM_IOC_INTR, &vip) == -1) + if (ioctl(fd, VMM_IOC_INTR, &vip) == -1) return (errno); return (0); @@ -1095,7 +1091,7 @@ vm_pipe_recv(struct vm_dev_pipe *p) * Returns 0 on success or an errno in event of failure. */ int -remap_guest_mem(struct vmd_vm *vm, int vmm_fd) +remap_guest_mem(struct vmd_vm *vm, int vm_fd) { size_t i; struct vm_sharemem_params vsp; @@ -1105,17 +1101,12 @@ remap_guest_mem(struct vmd_vm *vm, int vmm_fd) /* Initialize using our original creation parameters. */ memset(&vsp, 0, sizeof(vsp)); - vsp.vsp_nmemranges = vm->vm_params.vmc_nmemranges; - vsp.vsp_vm_id = vm->vm_vmmid; - memcpy(&vsp.vsp_memranges, &vm->vm_params.vmc_memranges, - sizeof(vsp.vsp_memranges)); - /* Ask vmm(4) to enter a shared mapping to guest memory. */ - if (ioctl(vmm_fd, VMM_IOC_SHAREMEM, &vsp) == -1) + if (ioctl(vm_fd, VMM_IOC_SHAREMEM, &vsp) == -1) return (errno); /* Update with the location of the new mappings. */ - for (i = 0; i < vsp.vsp_nmemranges; i++) + for (i = 0; i < vm->vm_params.vmc_nmemranges; i++) vm->vm_params.vmc_memranges[i].vmr_va = vsp.vsp_va[i]; return (0); blob - 45be7b08b02cb06e87b3f34360743510332727ae blob + 0c998bd8242ff364364bba1bcef2bd67cfce2c22 --- usr.sbin/vmd/vmd.c +++ usr.sbin/vmd/vmd.c @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <limits.h> #include <event.h> #include <fcntl.h> #include <pwd.h> @@ -238,7 +239,7 @@ vmd_dispatch_control(int fd, struct privsep_proc *p, s } else { vid.vid_id = vm->vm_vmid; } - } else if ((vm = vm_getbyid(vid.vid_id)) == NULL) { + } else if ((vm = vm_getbyvmid(vid.vid_id)) == NULL) { res = ENOENT; cmd = type == IMSG_VMDOP_PAUSE_VM ? IMSG_VMDOP_PAUSE_VM_RESPONSE @@ -323,7 +324,6 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struc if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) break; vm->vm_pid = vmr.vmr_pid; - vm->vm_vmmid = vmr.vmr_id; /* * If the peerid is not -1, forward the response back to the @@ -599,13 +599,13 @@ main(int argc, char **argv) break; case 'V': vm_launch = VMD_LAUNCH_VM; - vm_fd = strtonum(optarg, 0, 128, &errp); + vm_fd = strtonum(optarg, 0, INT_MAX, &errp); if (errp) fatalx("invalid vm fd"); break; case 'X': vm_launch = VMD_LAUNCH_DEV; - vm_fd = strtonum(optarg, 0, 128, &errp); + vm_fd = strtonum(optarg, 0, INT_MAX, &errp); if (errp) fatalx("invalid device fd"); break; @@ -620,13 +620,13 @@ main(int argc, char **argv) } break; case 'i': - vmm_fd = strtonum(optarg, 0, 128, &errp); + vmm_fd = strtonum(optarg, 0, INT_MAX, &errp); if (errp) fatalx("invalid vmm fd"); break; case 'j': /* -1 means no PSP available */ - psp_fd = strtonum(optarg, -1, 128, &errp); + psp_fd = strtonum(optarg, -1, INT_MAX, &errp); if (errp) fatalx("invalid psp fd"); break; @@ -962,43 +962,7 @@ vm_getbyvmid(uint32_t vmid) return (NULL); } -/* Find a vm in the list by it's vmm(4) id. */ struct vmd_vm * -vm_getbyid(uint32_t id) -{ - struct vmd_vm *vm; - - if (id == 0) - return (NULL); - TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { - if (vm->vm_vmmid == id) // XXX check this - return (vm); - } - - return (NULL); -} - -/* Translate a kernel/vmm(4) vm id to a vmd(8) id. */ -uint32_t -vm_id2vmid(uint32_t id, struct vmd_vm *vm) -{ - if (vm == NULL && (vm = vm_getbyid(id)) == NULL) - return (0); - DPRINTF("%s: vmm id %u is vmid %u", __func__, - id, vm->vm_vmid); - return (vm->vm_vmid); -} - -uint32_t -vm_vmid2id(uint32_t vmid, struct vmd_vm *vm) -{ - if (vm == NULL && (vm = vm_getbyvmid(vmid)) == NULL) - return (0); - DPRINTF("%s: vmid %u is vmm id %u", __func__, vmid, vm->vm_vmmid); - return (vm->vm_vmmid); -} - -struct vmd_vm * vm_getbyname(const char *name) { struct vmd_vm *vm; @@ -1221,6 +1185,7 @@ vm_register(struct privsep *ps, struct vmop_create_par vmc = &vm->vm_params; vm->vm_pid = -1; vm->vm_tty = -1; + vm->vm_fd = -1; vm->vm_kernel = -1; vm->vm_state &= ~VM_STATE_PAUSED; blob - bfd2402fb03a98a0d0e08b20b18030519b410b89 blob + 260e4fbf72cd7eb78f44e845776092c715b2fc6f --- usr.sbin/vmd/vmd.h +++ usr.sbin/vmd/vmd.h @@ -308,7 +308,7 @@ struct vmd_vm { pid_t vm_pid; uid_t vm_uid; uint32_t vm_vmid; /* vmd(8) identifier */ - uint32_t vm_vmmid; /* vmm(4) identifier */ + int vm_fd; /* vmm(4) vm file descriptor */ uint32_t vm_peerid; /* AMD SEV features */ @@ -471,10 +471,7 @@ ssize_t decode_udp_ip_header(unsigned char *, size_t, /* vmd.c */ int vmd_reload(unsigned int, const char *); -struct vmd_vm *vm_getbyid(uint32_t); struct vmd_vm *vm_getbyvmid(uint32_t); -uint32_t vm_id2vmid(uint32_t, struct vmd_vm *); -uint32_t vm_vmid2id(uint32_t, struct vmd_vm *); struct vmd_vm *vm_getbyname(const char *); struct vmd_vm *vm_getbypid(pid_t); void vm_stop(struct vmd_vm *, int, const char *); @@ -525,7 +522,7 @@ void create_memory_map(struct vmd_vm *); int load_firmware(struct vmd_vm *, struct vcpu_reg_state *); int init_emulated_hw(struct vmd_vm *, int, int[][VM_MAX_BASE_PER_DISK], int *); -int vcpu_reset(uint32_t, uint32_t, struct vcpu_reg_state *); +int vcpu_reset(int, uint32_t, struct vcpu_reg_state *); void pause_vm_md(struct vmd_vm *); void unpause_vm_md(struct vmd_vm *); void *hvaddr_mem(paddr_t, size_t); @@ -536,8 +533,8 @@ int read_mem(paddr_t, void *, size_t); int intr_ack(struct vmd_vm *); int intr_pending(struct vmd_vm *); void intr_toggle_el(struct vmd_vm *, int, int); -void vcpu_assert_irq(uint32_t, uint32_t, int); -void vcpu_deassert_irq(uint32_t, uint32_t, int); +void vcpu_assert_irq(int, uint32_t, int); +void vcpu_deassert_irq(int, uint32_t, int); int vcpu_exit(struct vm_run_params *); uint8_t vcpu_exit_pci(struct vm_run_params *); @@ -551,7 +548,7 @@ void get_input_data(struct vm_exit *, uint32_t *); void vcpu_halt(uint32_t); void vcpu_unhalt(uint32_t); void vcpu_signal_run(uint32_t); -int vcpu_intr(uint32_t, uint32_t, uint8_t); +int vcpu_intr(int, uint32_t, uint8_t); void vm_main(int, int); void mutex_lock(pthread_mutex_t *); void mutex_unlock(pthread_mutex_t *); @@ -600,7 +597,7 @@ int psp_df_flush(void); int psp_get_gstate(uint32_t, uint32_t *, uint32_t *, uint8_t *); int psp_launch_start(uint32_t *, int); int psp_launch_update(uint32_t, vaddr_t, size_t); -int psp_encrypt_state(uint32_t, uint32_t, uint32_t, uint32_t); +int psp_encrypt_state(uint32_t, uint32_t, int, uint32_t); int psp_launch_measure(uint32_t); int psp_launch_finish(uint32_t); int psp_activate(uint32_t, uint32_t); blob - 47b393cb5742bb3c2cf411aea27b32234c8ae04f blob + 2da0e8da179f32238ec2c7b8cb5c6c1ba0b4b894 --- usr.sbin/vmd/vmm.c +++ usr.sbin/vmd/vmm.c @@ -21,6 +21,7 @@ #include <sys/queue.h> #include <sys/wait.h> #include <sys/socket.h> +#include <signal.h> #include <dev/vmm/vmm.h> @@ -45,7 +46,7 @@ int vmm_start_vm(struct imsg *, uint32_t *, pid_t *); int vmm_dispatch_parent(int, struct privsep_proc *, struct imsg *); void vmm_run(struct privsep *, struct privsep_proc *, void *); void vmm_dispatch_vm(int, short, void *); -int terminate_vm(struct vm_terminate_params *); +int terminate_vm(pid_t); int get_info_vm(struct privsep *, struct imsg *, int); int opentap(char *); @@ -108,7 +109,6 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, st struct privsep *ps = p->p_ps; int res = 0, cmd = IMSG_NONE, verbose; struct vmd_vm *vm = NULL; - struct vm_terminate_params vtp; struct vmop_id vid; struct vmop_result vmr; struct vmop_addr_result var; @@ -153,9 +153,6 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, st break; case IMSG_VMDOP_START_VM_END: res = vmm_start_vm(imsg, &id, &vm_pid); - /* Check if the ID can be mapped correctly */ - if (res == 0 && (id = vm_id2vmid(id, NULL)) == 0) - res = ENOENT; cmd = IMSG_VMDOP_START_VM_RESPONSE; break; case IMSG_VMDOP_TERMINATE_VM_REQUEST: @@ -171,10 +168,8 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, st res = ENOENT; } else if ((vm = vm_getbyvmid(id)) != NULL) { if (flags & VMOP_FORCE) { - vtp.vtp_vm_id = vm_vmid2id(vm->vm_vmid, vm); vm->vm_state |= VM_STATE_SHUTDOWN; - (void)terminate_vm(&vtp); - res = 0; + res = terminate_vm(vm->vm_pid); } else if (!(vm->vm_state & VM_STATE_SHUTDOWN)) { log_debug("%s: sending shutdown request" " to vm %d", __func__, id); @@ -199,7 +194,7 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, st * Check to see if the VM process is still * active. If not, return VMD_VM_STOP_INVALID. */ - if (vm_vmid2id(vm->vm_vmid, vm) == 0) { + if (kill(vm->vm_pid, 0) == -1 && errno == ESRCH) { log_debug("%s: no vm running anymore", __func__); res = VMD_VM_STOP_INVALID; @@ -327,11 +322,10 @@ void vmm_sighdlr(int sig, short event, void *arg) { struct privsep *ps = arg; - int status, ret = 0; + int status, ret; pid_t pid; struct vmop_result vmr; struct vmd_vm *vm; - struct vm_terminate_params vtp; log_debug("%s: handling signal %d", __func__, sig); switch (sig) { @@ -340,6 +334,7 @@ vmm_sighdlr(int sig, short event, void *arg) pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) continue; + ret = 0; if (WIFEXITED(status) || WIFSIGNALED(status)) { vm = vm_getbypid(pid); @@ -354,24 +349,21 @@ vmm_sighdlr(int sig, short event, void *arg) if (WIFEXITED(status)) ret = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + ret = EIO; /* Don't reboot on pending shutdown */ if (ret == EAGAIN && (vm->vm_state & VM_STATE_SHUTDOWN)) ret = 0; - /* XXX check this */ - vtp.vtp_vm_id = vm->vm_vmmid; + log_debug("%s: vm %s exited (id %d)", + __func__, vm->vm_params.vmc_name, + vm->vm_vmid); - if (terminate_vm(&vtp) == 0) - log_debug("%s: terminated vm %s" - " (id %d)", __func__, - vm->vm_params.vmc_name, - vm->vm_vmid); - memset(&vmr, 0, sizeof(vmr)); vmr.vmr_result = ret; - vmr.vmr_id = vm_id2vmid(vm->vm_vmmid, vm); + vmr.vmr_id = vm->vm_vmid; if (proc_compose_imsg(ps, PROC_PARENT, IMSG_VMDOP_TERMINATE_VM_EVENT, vm->vm_peerid, -1, &vmr, sizeof(vmr)) == -1) @@ -397,14 +389,11 @@ vmm_sighdlr(int sig, short event, void *arg) void vmm_shutdown(void) { - struct vm_terminate_params vtp; struct vmd_vm *vm, *vm_next; TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) { - vtp.vtp_vm_id = vm_vmid2id(vm->vm_vmid, vm); - /* XXX suspend or request graceful shutdown */ - (void)terminate_vm(&vtp); + (void)terminate_vm(vm->vm_pid); vm_remove(vm, __func__); } } @@ -514,23 +503,18 @@ vmm_dispatch_vm(int fd, short event, void *arg) imsg_event_add(iev); } -/* - * terminate_vm - * - * Requests vmm(4) to terminate the VM whose ID is provided in the - * supplied vm_terminate_params structure (vtp->vtp_vm_id) - * - * Parameters - * vtp: vm_terminate_params struct containing the ID of the VM to terminate - * - * Return values: - * 0: success - * !0: ioctl to vmm(4) failed (eg, ENOENT if the supplied VM is not valid) - */ int -terminate_vm(struct vm_terminate_params *vtp) +terminate_vm(pid_t pid) { - if (ioctl(env->vmd_vmm_fd, VMM_IOC_TERM, vtp) == -1) + struct vm_terminate_params vtp; + + if (pid <= 0) + return (EINVAL); + + memset(&vtp, 0, sizeof(vtp)); + vtp.vtp_creator_pid = pid; + + if (ioctl(env->vmd_vmm_fd, VMM_IOC_TERM, &vtp) == -1) return (errno); return (0); @@ -588,7 +572,7 @@ opentap(char *ifname) * * Parameters: * imsg: The VM data structure that is including the VM create parameters. - * id: Returns the VM id as reported by the kernel and obtained from the VM. + * id: Returns the vmd(8) VM identifier. * pid: Returns the VM pid to the parent. * * Return values: @@ -686,22 +670,19 @@ vmm_start_vm(struct imsg *imsg, uint32_t *id, pid_t *p goto err; } - /* Read back the kernel-generated vm id from the child */ - sz = atomicio(read, fds[0], &vm->vm_vmmid, - sizeof(vm->vm_vmmid)); - if (sz != sizeof(vm->vm_vmmid)) { - log_debug("%s: failed to receive vm id from vm %s", + /* Read back the VM start status from the child. */ + sz = atomicio(read, fds[0], &ret, sizeof(ret)); + if (sz != sizeof(ret)) { + log_debug("%s: failed to receive vm start status from vm %s", __func__, vm->vm_params.vmc_name); - /* vmd could not allocate memory for the vm. */ - ret = ENOMEM; + ret = EIO; goto err; } - /* Check for an invalid id. This indicates child failure. */ - if (vm->vm_vmmid == 0) + if (ret != 0) goto err; - *id = vm->vm_vmmid; + *id = vm->vm_vmid; *pid = vm->vm_pid; /* Wire up our pipe into the event handling. */ @@ -724,6 +705,8 @@ vmm_start_vm(struct imsg *imsg, uint32_t *id, pid_t *p close(dev_null); } + if (env->vmd_vmm_fd > 0) + fcntl(env->vmd_vmm_fd, F_SETFD, 0); /* /dev/vmm fd */ if (env->vmd_psp_fd > 0) fcntl(env->vmd_psp_fd, F_SETFD, 0); /* psp device fd */ @@ -795,8 +778,8 @@ get_info_vm(struct privsep *ps, struct imsg *imsg, int size_t ct, i; struct vm_info_params vip; struct vm_info_result *info; - struct vm_terminate_params vtp; struct vmop_info_result vir; + struct vmd_vm *vm; uint32_t peer_id; /* @@ -839,11 +822,10 @@ get_info_vm(struct privsep *ps, struct imsg *imsg, int ct = vip.vip_size / sizeof(struct vm_info_result); for (i = 0; i < ct; i++) { if (terminate) { - vtp.vtp_vm_id = info[i].vir_id; - if ((ret = terminate_vm(&vtp)) != 0) + if ((ret = terminate_vm(info[i].vir_creator_pid)) != 0) break; - log_debug("%s: terminated vm %s (id %d)", __func__, - info[i].vir_name, info[i].vir_id); + log_debug("%s: terminated vm %s (pid %d)", __func__, + info[i].vir_name, info[i].vir_creator_pid); continue; } @@ -854,7 +836,9 @@ get_info_vm(struct privsep *ps, struct imsg *imsg, int memcpy(vir.vir_vcpu_state, info[i].vir_vcpu_state, sizeof(vir.vir_vcpu_state)); vir.vir_creator_pid = info[i].vir_creator_pid; - vir.vir_id = vm_id2vmid(info[i].vir_id, NULL); + if ((vm = vm_getbypid(info[i].vir_creator_pid)) == NULL) + continue; + vir.vir_id = vm->vm_vmid; memcpy(vir.vir_name, info[i].vir_name, sizeof(vir.vir_name)); peer_id = imsg_get_id(imsg); blob - 1b5ade90f9b3152a37bcc54a2fadd6e8a71fbac7 blob + 235cb1f2c53747da6955850a2f94e7e8aad3aba3 --- usr.sbin/vmd/x86_vm.c +++ usr.sbin/vmd/x86_vm.c @@ -368,7 +368,7 @@ init_emulated_hw(struct vmd_vm *vm, int child_cdrom, memset(&ioports_map, 0, sizeof(io_fn_t) * MAX_PORTS); /* Init i8253 PIT */ - i8253_init(vm->vm_vmmid); + i8253_init(vm->vm_fd); ioports_map[TIMER_CTRL] = vcpu_exit_i8253; ioports_map[TIMER_BASE + TIMER_CNTR0] = vcpu_exit_i8253; ioports_map[TIMER_BASE + TIMER_CNTR1] = vcpu_exit_i8253; @@ -376,7 +376,7 @@ init_emulated_hw(struct vmd_vm *vm, int child_cdrom, ioports_map[PCKBC_AUX] = vcpu_exit_i8253_misc; /* Init mc146818 RTC */ - mc146818_init(vm->vm_vmmid, memlo, memhi); + mc146818_init(vm->vm_fd, memlo, memhi); ioports_map[IO_RTC] = vcpu_exit_mc146818; ioports_map[IO_RTC + 1] = vcpu_exit_mc146818; @@ -390,7 +390,7 @@ init_emulated_hw(struct vmd_vm *vm, int child_cdrom, ioports_map[ELCR1] = vcpu_exit_elcr; /* Init ns8250 UART */ - ns8250_init(con_fd, vm->vm_vmmid); + ns8250_init(con_fd, vm->vm_fd); for (i = COM1_DATA; i <= COM1_SCR; i++) ioports_map[i] = vcpu_exit_com; @@ -482,7 +482,7 @@ vcpu_exit_inout(struct vm_run_params *vrp) vei->vrs.vrs_gprs[VCPU_REGS_RIP] += vei->vei.vei_insn_len; if (intr != 0xFF) - vcpu_assert_irq(vrp->vrp_vm_id, vrp->vrp_vcpu_id, intr); + vcpu_assert_irq(current_vm->vm_fd, vrp->vrp_vcpu_id, intr); } /* @@ -883,17 +883,17 @@ hvaddr_mem(paddr_t gpa, size_t len) * Injects the specified IRQ on the supplied vcpu/vm * * Parameters: - * vm_id: VMM vm ID to inject to + * fd: vmm(4) vm file descriptor to inject to * vcpu_id: VCPU ID to inject to * irq: IRQ to inject */ void -vcpu_assert_irq(uint32_t vmm_id, uint32_t vcpu_id, int irq) +vcpu_assert_irq(int fd, uint32_t vcpu_id, int irq) { i8259_assert_irq(irq); if (i8259_is_pending()) { - if (vcpu_intr(vmm_id, vcpu_id, 1)) + if (vcpu_intr(fd, vcpu_id, 1)) fatalx("%s: can't assert INTR", __func__); vcpu_unhalt(vcpu_id); @@ -907,19 +907,19 @@ vcpu_assert_irq(uint32_t vmm_id, uint32_t vcpu_id, int * Clears the specified IRQ on the supplied vcpu/vm * * Parameters: - * vm_id: VMM vm ID to clear in + * fd: vmm(4) vm file descriptor to clear in * vcpu_id: VCPU ID to clear in * irq: IRQ to clear */ void -vcpu_deassert_irq(uint32_t vmm_id, uint32_t vcpu_id, int irq) +vcpu_deassert_irq(int fd, uint32_t vcpu_id, int irq) { i8259_deassert_irq(irq); if (!i8259_is_pending()) { - if (vcpu_intr(vmm_id, vcpu_id, 0)) - fatalx("%s: can't deassert INTR for vmm_id %d, " - "vcpu_id %d", __func__, vmm_id, vcpu_id); + if (vcpu_intr(fd, vcpu_id, 0)) + fatalx("%s: can't deassert INTR for vm fd %d, " + "vcpu_id %d", __func__, fd, vcpu_id); } }