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);
 	}
 }