1*ebaf145fSbluhm /* $OpenBSD: vmm.c,v 1.3 2024/08/27 09:16:03 bluhm Exp $ */ 23a0db596Smlarkin /* 33a0db596Smlarkin * Copyright (c) 2014-2023 Mike Larkin <mlarkin@openbsd.org> 43a0db596Smlarkin * 53a0db596Smlarkin * Permission to use, copy, modify, and distribute this software for any 63a0db596Smlarkin * purpose with or without fee is hereby granted, provided that the above 73a0db596Smlarkin * copyright notice and this permission notice appear in all copies. 83a0db596Smlarkin * 93a0db596Smlarkin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 103a0db596Smlarkin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 113a0db596Smlarkin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 123a0db596Smlarkin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 133a0db596Smlarkin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 143a0db596Smlarkin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 153a0db596Smlarkin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 163a0db596Smlarkin */ 173a0db596Smlarkin 183a0db596Smlarkin #include <sys/param.h> 193a0db596Smlarkin #include <sys/systm.h> 203a0db596Smlarkin #include <sys/device.h> 213a0db596Smlarkin #include <sys/pool.h> 223a0db596Smlarkin #include <sys/pledge.h> 233a0db596Smlarkin #include <sys/proc.h> 243a0db596Smlarkin #include <sys/ioctl.h> 253a0db596Smlarkin #include <sys/malloc.h> 263a0db596Smlarkin #include <sys/signalvar.h> 273a0db596Smlarkin 283a0db596Smlarkin #include <machine/vmmvar.h> 293a0db596Smlarkin 303a0db596Smlarkin #include <dev/vmm/vmm.h> 313a0db596Smlarkin 323a0db596Smlarkin struct vmm_softc *vmm_softc; 333a0db596Smlarkin struct pool vm_pool; 343a0db596Smlarkin struct pool vcpu_pool; 353a0db596Smlarkin 363a0db596Smlarkin struct cfdriver vmm_cd = { 373a0db596Smlarkin NULL, "vmm", DV_DULL, CD_SKIPHIBERNATE 383a0db596Smlarkin }; 393a0db596Smlarkin 403a0db596Smlarkin const struct cfattach vmm_ca = { 413a0db596Smlarkin sizeof(struct vmm_softc), vmm_probe, vmm_attach, NULL, vmm_activate 423a0db596Smlarkin }; 433a0db596Smlarkin 443a0db596Smlarkin int 453a0db596Smlarkin vmm_probe(struct device *parent, void *match, void *aux) 463a0db596Smlarkin { 473a0db596Smlarkin const char **busname = (const char **)aux; 483a0db596Smlarkin 493a0db596Smlarkin if (strcmp(*busname, vmm_cd.cd_name) != 0) 503a0db596Smlarkin return (0); 513a0db596Smlarkin return (1); 523a0db596Smlarkin } 533a0db596Smlarkin 543a0db596Smlarkin void 553a0db596Smlarkin vmm_attach(struct device *parent, struct device *self, void *aux) 563a0db596Smlarkin { 573a0db596Smlarkin struct vmm_softc *sc = (struct vmm_softc *)self; 583a0db596Smlarkin 593a0db596Smlarkin rw_init(&sc->sc_slock, "vmmslk"); 603a0db596Smlarkin sc->sc_status = VMM_ACTIVE; 613a0db596Smlarkin refcnt_init(&sc->sc_refcnt); 623a0db596Smlarkin 633a0db596Smlarkin sc->vcpu_ct = 0; 643a0db596Smlarkin sc->vcpu_max = VMM_MAX_VCPUS; 653a0db596Smlarkin sc->vm_ct = 0; 663a0db596Smlarkin sc->vm_idx = 0; 673a0db596Smlarkin 683a0db596Smlarkin SLIST_INIT(&sc->vm_list); 693a0db596Smlarkin rw_init(&sc->vm_lock, "vm_list"); 703a0db596Smlarkin 713a0db596Smlarkin pool_init(&vm_pool, sizeof(struct vm), 0, IPL_MPFLOOR, PR_WAITOK, 723a0db596Smlarkin "vmpool", NULL); 733a0db596Smlarkin pool_init(&vcpu_pool, sizeof(struct vcpu), 64, IPL_MPFLOOR, PR_WAITOK, 743a0db596Smlarkin "vcpupl", NULL); 753a0db596Smlarkin 763a0db596Smlarkin vmm_attach_machdep(parent, self, aux); 773a0db596Smlarkin 783a0db596Smlarkin vmm_softc = sc; 793a0db596Smlarkin printf("\n"); 803a0db596Smlarkin } 813a0db596Smlarkin 823a0db596Smlarkin int 833a0db596Smlarkin vmm_activate(struct device *self, int act) 843a0db596Smlarkin { 853a0db596Smlarkin switch (act) { 863a0db596Smlarkin case DVACT_QUIESCE: 873a0db596Smlarkin /* Block device users as we're suspending operation. */ 883a0db596Smlarkin rw_enter_write(&vmm_softc->sc_slock); 893a0db596Smlarkin KASSERT(vmm_softc->sc_status == VMM_ACTIVE); 903a0db596Smlarkin vmm_softc->sc_status = VMM_SUSPENDED; 913a0db596Smlarkin rw_exit_write(&vmm_softc->sc_slock); 923a0db596Smlarkin 933a0db596Smlarkin /* Wait for any device users to finish. */ 943a0db596Smlarkin refcnt_finalize(&vmm_softc->sc_refcnt, "vmmsusp"); 953a0db596Smlarkin 963a0db596Smlarkin vmm_activate_machdep(self, act); 973a0db596Smlarkin break; 983a0db596Smlarkin case DVACT_WAKEUP: 993a0db596Smlarkin vmm_activate_machdep(self, act); 1003a0db596Smlarkin 1013a0db596Smlarkin /* Set the device back to active. */ 1023a0db596Smlarkin rw_enter_write(&vmm_softc->sc_slock); 1033a0db596Smlarkin KASSERT(vmm_softc->sc_status == VMM_SUSPENDED); 1043a0db596Smlarkin refcnt_init(&vmm_softc->sc_refcnt); 1053a0db596Smlarkin vmm_softc->sc_status = VMM_ACTIVE; 1063a0db596Smlarkin rw_exit_write(&vmm_softc->sc_slock); 1073a0db596Smlarkin 1083a0db596Smlarkin /* Notify any waiting device users. */ 1093a0db596Smlarkin wakeup(&vmm_softc->sc_status); 1103a0db596Smlarkin break; 1113a0db596Smlarkin } 1123a0db596Smlarkin 1133a0db596Smlarkin return (0); 1143a0db596Smlarkin } 1153a0db596Smlarkin 1163a0db596Smlarkin /* 1173a0db596Smlarkin * vmmopen 1183a0db596Smlarkin * 1193a0db596Smlarkin * Called during open of /dev/vmm. 1203a0db596Smlarkin * 1213a0db596Smlarkin * Parameters: 1223a0db596Smlarkin * dev, flag, mode, p: These come from the character device and are 1233a0db596Smlarkin * all unused for this function 1243a0db596Smlarkin * 1253a0db596Smlarkin * Return values: 1263a0db596Smlarkin * ENODEV: if vmm(4) didn't attach or no supported CPUs detected 1273a0db596Smlarkin * 0: successful open 1283a0db596Smlarkin */ 1293a0db596Smlarkin int 1303a0db596Smlarkin vmmopen(dev_t dev, int flag, int mode, struct proc *p) 1313a0db596Smlarkin { 1323a0db596Smlarkin /* Don't allow open if we didn't attach */ 1333a0db596Smlarkin if (vmm_softc == NULL) 1343a0db596Smlarkin return (ENODEV); 1353a0db596Smlarkin 1363a0db596Smlarkin /* Don't allow open if we didn't detect any supported CPUs */ 1373a0db596Smlarkin if (vmm_softc->mode == VMM_MODE_UNKNOWN) 1383a0db596Smlarkin return (ENODEV); 1393a0db596Smlarkin 1403a0db596Smlarkin return 0; 1413a0db596Smlarkin } 1423a0db596Smlarkin 1433a0db596Smlarkin /* 1443a0db596Smlarkin * vmmclose 1453a0db596Smlarkin * 1463a0db596Smlarkin * Called when /dev/vmm is closed. Presently unused. 1473a0db596Smlarkin */ 1483a0db596Smlarkin int 1493a0db596Smlarkin vmmclose(dev_t dev, int flag, int mode, struct proc *p) 1503a0db596Smlarkin { 1513a0db596Smlarkin return 0; 1523a0db596Smlarkin } 1533a0db596Smlarkin 1543a0db596Smlarkin /* 1553a0db596Smlarkin * vm_find 1563a0db596Smlarkin * 1573a0db596Smlarkin * Function to find an existing VM by its identifier. 1583a0db596Smlarkin * Must be called under the global vm_lock. 1593a0db596Smlarkin * 1603a0db596Smlarkin * Parameters: 1613a0db596Smlarkin * id: The VM identifier. 1623a0db596Smlarkin * *res: A pointer to the VM or NULL if not found 1633a0db596Smlarkin * 1643a0db596Smlarkin * Return values: 1653a0db596Smlarkin * 0: if successful 1663a0db596Smlarkin * ENOENT: if the VM defined by 'id' cannot be found 1673a0db596Smlarkin * EPERM: if the VM cannot be accessed by the current process 1683a0db596Smlarkin */ 1693a0db596Smlarkin int 1703a0db596Smlarkin vm_find(uint32_t id, struct vm **res) 1713a0db596Smlarkin { 1723a0db596Smlarkin struct proc *p = curproc; 1733a0db596Smlarkin struct vm *vm; 1743a0db596Smlarkin int ret = ENOENT; 1753a0db596Smlarkin 1763a0db596Smlarkin *res = NULL; 1773a0db596Smlarkin 1783a0db596Smlarkin rw_enter_read(&vmm_softc->vm_lock); 1793a0db596Smlarkin SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) { 1803a0db596Smlarkin if (vm->vm_id == id) { 1813a0db596Smlarkin /* 1823a0db596Smlarkin * In the pledged VM process, only allow to find 1833a0db596Smlarkin * the VM that is running in the current process. 1843a0db596Smlarkin * The managing vmm parent process can lookup all 1853a0db596Smlarkin * all VMs and is indicated by PLEDGE_PROC. 1863a0db596Smlarkin */ 1873a0db596Smlarkin if (((p->p_p->ps_pledge & 1883a0db596Smlarkin (PLEDGE_VMM | PLEDGE_PROC)) == PLEDGE_VMM) && 1893a0db596Smlarkin (vm->vm_creator_pid != p->p_p->ps_pid)) 1903a0db596Smlarkin ret = EPERM; 1913a0db596Smlarkin else { 1923a0db596Smlarkin refcnt_take(&vm->vm_refcnt); 1933a0db596Smlarkin *res = vm; 1943a0db596Smlarkin ret = 0; 1953a0db596Smlarkin } 1963a0db596Smlarkin break; 1973a0db596Smlarkin } 1983a0db596Smlarkin } 1993a0db596Smlarkin rw_exit_read(&vmm_softc->vm_lock); 2003a0db596Smlarkin 2013a0db596Smlarkin if (ret == EPERM) 2023a0db596Smlarkin return (pledge_fail(p, EPERM, PLEDGE_VMM)); 2033a0db596Smlarkin return (ret); 2043a0db596Smlarkin } 2053a0db596Smlarkin 2063a0db596Smlarkin /* 2073a0db596Smlarkin * vmmioctl 2083a0db596Smlarkin * 2093a0db596Smlarkin * Main ioctl dispatch routine for /dev/vmm. Parses ioctl type and calls 2103a0db596Smlarkin * appropriate lower level handler routine. Returns result to ioctl caller. 2113a0db596Smlarkin */ 2123a0db596Smlarkin int 2133a0db596Smlarkin vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 2143a0db596Smlarkin { 2153a0db596Smlarkin int ret; 2163a0db596Smlarkin 2173a0db596Smlarkin KERNEL_UNLOCK(); 2183a0db596Smlarkin 2193a0db596Smlarkin ret = rw_enter(&vmm_softc->sc_slock, RW_READ | RW_INTR); 2203a0db596Smlarkin if (ret != 0) 2213a0db596Smlarkin goto out; 2223a0db596Smlarkin while (vmm_softc->sc_status != VMM_ACTIVE) { 2233a0db596Smlarkin ret = rwsleep_nsec(&vmm_softc->sc_status, &vmm_softc->sc_slock, 2243a0db596Smlarkin PWAIT | PCATCH, "vmmresume", INFSLP); 2253a0db596Smlarkin if (ret != 0) { 2263a0db596Smlarkin rw_exit(&vmm_softc->sc_slock); 2273a0db596Smlarkin goto out; 2283a0db596Smlarkin } 2293a0db596Smlarkin } 2303a0db596Smlarkin refcnt_take(&vmm_softc->sc_refcnt); 2313a0db596Smlarkin rw_exit(&vmm_softc->sc_slock); 2323a0db596Smlarkin 2333a0db596Smlarkin switch (cmd) { 2343a0db596Smlarkin case VMM_IOC_CREATE: 2353a0db596Smlarkin if ((ret = vmm_start()) != 0) { 2363a0db596Smlarkin vmm_stop(); 2373a0db596Smlarkin break; 2383a0db596Smlarkin } 2393a0db596Smlarkin ret = vm_create((struct vm_create_params *)data, p); 2403a0db596Smlarkin break; 2413a0db596Smlarkin case VMM_IOC_RUN: 2423a0db596Smlarkin ret = vm_run((struct vm_run_params *)data); 2433a0db596Smlarkin break; 2443a0db596Smlarkin case VMM_IOC_INFO: 2453a0db596Smlarkin ret = vm_get_info((struct vm_info_params *)data); 2463a0db596Smlarkin break; 2473a0db596Smlarkin case VMM_IOC_TERM: 2483a0db596Smlarkin ret = vm_terminate((struct vm_terminate_params *)data); 2493a0db596Smlarkin break; 2503a0db596Smlarkin case VMM_IOC_RESETCPU: 2513a0db596Smlarkin ret = vm_resetcpu((struct vm_resetcpu_params *)data); 2523a0db596Smlarkin break; 2533a0db596Smlarkin case VMM_IOC_READREGS: 2543a0db596Smlarkin ret = vm_rwregs((struct vm_rwregs_params *)data, 0); 2553a0db596Smlarkin break; 2563a0db596Smlarkin case VMM_IOC_WRITEREGS: 2573a0db596Smlarkin ret = vm_rwregs((struct vm_rwregs_params *)data, 1); 2583a0db596Smlarkin break; 2593a0db596Smlarkin case VMM_IOC_READVMPARAMS: 2603a0db596Smlarkin ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 0); 2613a0db596Smlarkin break; 2623a0db596Smlarkin case VMM_IOC_WRITEVMPARAMS: 2633a0db596Smlarkin ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 1); 2643a0db596Smlarkin break; 2653c817da7Sdv case VMM_IOC_SHAREMEM: 2663c817da7Sdv ret = vm_share_mem((struct vm_sharemem_params *)data, p); 2673c817da7Sdv break; 2683a0db596Smlarkin default: 2693a0db596Smlarkin ret = vmmioctl_machdep(dev, cmd, data, flag, p); 2703a0db596Smlarkin break; 2713a0db596Smlarkin } 2723a0db596Smlarkin 2733a0db596Smlarkin refcnt_rele_wake(&vmm_softc->sc_refcnt); 2743a0db596Smlarkin out: 2753a0db596Smlarkin KERNEL_LOCK(); 2763a0db596Smlarkin 2773a0db596Smlarkin return (ret); 2783a0db596Smlarkin } 2793a0db596Smlarkin 2803a0db596Smlarkin /* 2813a0db596Smlarkin * pledge_ioctl_vmm 2823a0db596Smlarkin * 2833a0db596Smlarkin * Restrict the allowed ioctls in a pledged process context. 2843a0db596Smlarkin * Is called from pledge_ioctl(). 2853a0db596Smlarkin */ 2863a0db596Smlarkin int 2873a0db596Smlarkin pledge_ioctl_vmm(struct proc *p, long com) 2883a0db596Smlarkin { 2893a0db596Smlarkin switch (com) { 2903a0db596Smlarkin case VMM_IOC_CREATE: 2913a0db596Smlarkin case VMM_IOC_INFO: 2923c817da7Sdv case VMM_IOC_SHAREMEM: 2933a0db596Smlarkin /* The "parent" process in vmd forks and manages VMs */ 2943a0db596Smlarkin if (p->p_p->ps_pledge & PLEDGE_PROC) 2953a0db596Smlarkin return (0); 2963a0db596Smlarkin break; 2973a0db596Smlarkin case VMM_IOC_TERM: 2983a0db596Smlarkin /* XXX VM processes should only terminate themselves */ 2993a0db596Smlarkin case VMM_IOC_RUN: 3003a0db596Smlarkin case VMM_IOC_RESETCPU: 3013a0db596Smlarkin case VMM_IOC_READREGS: 3023a0db596Smlarkin case VMM_IOC_WRITEREGS: 3033a0db596Smlarkin case VMM_IOC_READVMPARAMS: 3043a0db596Smlarkin case VMM_IOC_WRITEVMPARAMS: 3053a0db596Smlarkin return (0); 3063a0db596Smlarkin default: 3073a0db596Smlarkin return pledge_ioctl_vmm_machdep(p, com); 3083a0db596Smlarkin } 3093a0db596Smlarkin 3103a0db596Smlarkin return (EPERM); 3113a0db596Smlarkin } 3123a0db596Smlarkin 3133a0db596Smlarkin /* 3143a0db596Smlarkin * vm_find_vcpu 3153a0db596Smlarkin * 3163a0db596Smlarkin * Lookup VMM VCPU by ID number 3173a0db596Smlarkin * 3183a0db596Smlarkin * Parameters: 3193a0db596Smlarkin * vm: vm structure 3203a0db596Smlarkin * id: index id of vcpu 3213a0db596Smlarkin * 3223a0db596Smlarkin * Returns pointer to vcpu structure if successful, NULL otherwise 3233a0db596Smlarkin */ 3243a0db596Smlarkin struct vcpu * 3253a0db596Smlarkin vm_find_vcpu(struct vm *vm, uint32_t id) 3263a0db596Smlarkin { 3273a0db596Smlarkin struct vcpu *vcpu; 3283a0db596Smlarkin 3293a0db596Smlarkin if (vm == NULL) 3303a0db596Smlarkin return (NULL); 3313a0db596Smlarkin 3323a0db596Smlarkin SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) { 3333a0db596Smlarkin if (vcpu->vc_id == id) 3343a0db596Smlarkin return (vcpu); 3353a0db596Smlarkin } 3363a0db596Smlarkin 3373a0db596Smlarkin return (NULL); 3383a0db596Smlarkin } 3393a0db596Smlarkin 3403a0db596Smlarkin /* 3413a0db596Smlarkin * vm_create 3423a0db596Smlarkin * 3433a0db596Smlarkin * Creates the in-memory VMM structures for the VM defined by 'vcp'. The 3443a0db596Smlarkin * parent of this VM shall be the process defined by 'p'. 3453a0db596Smlarkin * This function does not start the VCPU(s) - see vm_start. 3463a0db596Smlarkin * 3473a0db596Smlarkin * Return Values: 3483a0db596Smlarkin * 0: the create operation was successful 3493a0db596Smlarkin * ENOMEM: out of memory 3503a0db596Smlarkin * various other errors from vcpu_init/vm_impl_init 3513a0db596Smlarkin */ 3523a0db596Smlarkin int 3533a0db596Smlarkin vm_create(struct vm_create_params *vcp, struct proc *p) 3543a0db596Smlarkin { 3553a0db596Smlarkin int i, ret; 3563a0db596Smlarkin size_t memsize; 3573a0db596Smlarkin struct vm *vm; 3583a0db596Smlarkin struct vcpu *vcpu; 3593a0db596Smlarkin 3603a0db596Smlarkin memsize = vm_create_check_mem_ranges(vcp); 3613a0db596Smlarkin if (memsize == 0) 3623a0db596Smlarkin return (EINVAL); 3633a0db596Smlarkin 3643a0db596Smlarkin /* XXX - support UP only (for now) */ 3653a0db596Smlarkin if (vcp->vcp_ncpus != 1) 3663a0db596Smlarkin return (EINVAL); 3673a0db596Smlarkin 3683a0db596Smlarkin /* Bail early if we're already at vcpu capacity. */ 3693a0db596Smlarkin rw_enter_read(&vmm_softc->vm_lock); 3703a0db596Smlarkin if (vmm_softc->vcpu_ct + vcp->vcp_ncpus > vmm_softc->vcpu_max) { 3713a0db596Smlarkin DPRINTF("%s: maximum vcpus (%lu) reached\n", __func__, 3723a0db596Smlarkin vmm_softc->vcpu_max); 3733a0db596Smlarkin rw_exit_read(&vmm_softc->vm_lock); 3743a0db596Smlarkin return (ENOMEM); 3753a0db596Smlarkin } 3763a0db596Smlarkin rw_exit_read(&vmm_softc->vm_lock); 3773a0db596Smlarkin 3783a0db596Smlarkin /* Instantiate and configure the new vm. */ 3793a0db596Smlarkin vm = pool_get(&vm_pool, PR_WAITOK | PR_ZERO); 3803a0db596Smlarkin 3813a0db596Smlarkin vm->vm_creator_pid = p->p_p->ps_pid; 3823a0db596Smlarkin vm->vm_nmemranges = vcp->vcp_nmemranges; 3833a0db596Smlarkin memcpy(vm->vm_memranges, vcp->vcp_memranges, 3843a0db596Smlarkin vm->vm_nmemranges * sizeof(vm->vm_memranges[0])); 3853a0db596Smlarkin vm->vm_memory_size = memsize; 3863a0db596Smlarkin strncpy(vm->vm_name, vcp->vcp_name, VMM_MAX_NAME_LEN - 1); 3873a0db596Smlarkin 3883a0db596Smlarkin if (vm_impl_init(vm, p)) { 3893a0db596Smlarkin printf("failed to init arch-specific features for vm %p\n", vm); 3903a0db596Smlarkin vm_teardown(&vm); 3913a0db596Smlarkin return (ENOMEM); 3923a0db596Smlarkin } 3933a0db596Smlarkin 3943a0db596Smlarkin vm->vm_vcpu_ct = 0; 3953a0db596Smlarkin 3963a0db596Smlarkin /* Initialize each VCPU defined in 'vcp' */ 3973a0db596Smlarkin SLIST_INIT(&vm->vm_vcpu_list); 3983a0db596Smlarkin for (i = 0; i < vcp->vcp_ncpus; i++) { 3993a0db596Smlarkin vcpu = pool_get(&vcpu_pool, PR_WAITOK | PR_ZERO); 4003a0db596Smlarkin 4013a0db596Smlarkin vcpu->vc_parent = vm; 402*ebaf145fSbluhm vcpu->vc_id = vm->vm_vcpu_ct; 403*ebaf145fSbluhm vm->vm_vcpu_ct++; 404*ebaf145fSbluhm if ((ret = vcpu_init(vcpu, vcp)) != 0) { 4053a0db596Smlarkin printf("failed to init vcpu %d for vm %p\n", i, vm); 4063a0db596Smlarkin vm_teardown(&vm); 4073a0db596Smlarkin return (ret); 4083a0db596Smlarkin } 4093a0db596Smlarkin /* Publish vcpu to list, inheriting the reference. */ 4103a0db596Smlarkin SLIST_INSERT_HEAD(&vm->vm_vcpu_list, vcpu, vc_vcpu_link); 4113a0db596Smlarkin } 4123a0db596Smlarkin 4133a0db596Smlarkin /* Attempt to register the vm now that it's configured. */ 4143a0db596Smlarkin rw_enter_write(&vmm_softc->vm_lock); 4153a0db596Smlarkin 4163a0db596Smlarkin if (vmm_softc->vcpu_ct + vm->vm_vcpu_ct > vmm_softc->vcpu_max) { 4173a0db596Smlarkin /* Someone already took our capacity. */ 4183a0db596Smlarkin printf("%s: maximum vcpus (%lu) reached\n", __func__, 4193a0db596Smlarkin vmm_softc->vcpu_max); 4203a0db596Smlarkin rw_exit_write(&vmm_softc->vm_lock); 4213a0db596Smlarkin vm_teardown(&vm); 4223a0db596Smlarkin return (ENOMEM); 4233a0db596Smlarkin } 4243a0db596Smlarkin 4253a0db596Smlarkin /* Update the global index and identify the vm. */ 4263a0db596Smlarkin vmm_softc->vm_idx++; 4273a0db596Smlarkin vm->vm_id = vmm_softc->vm_idx; 4283a0db596Smlarkin vcp->vcp_id = vm->vm_id; 4293a0db596Smlarkin 4303a0db596Smlarkin /* Publish the vm into the list and update counts. */ 4313a0db596Smlarkin refcnt_init(&vm->vm_refcnt); 4323a0db596Smlarkin SLIST_INSERT_HEAD(&vmm_softc->vm_list, vm, vm_link); 4333a0db596Smlarkin vmm_softc->vm_ct++; 4343a0db596Smlarkin vmm_softc->vcpu_ct += vm->vm_vcpu_ct; 4353a0db596Smlarkin 4363a0db596Smlarkin rw_exit_write(&vmm_softc->vm_lock); 4373a0db596Smlarkin 4383a0db596Smlarkin return (0); 4393a0db596Smlarkin } 4403a0db596Smlarkin 4413a0db596Smlarkin /* 4423a0db596Smlarkin * vm_create_check_mem_ranges 4433a0db596Smlarkin * 4443a0db596Smlarkin * Make sure that the guest physical memory ranges given by the user process 4453a0db596Smlarkin * do not overlap and are in ascending order. 4463a0db596Smlarkin * 4473a0db596Smlarkin * The last physical address may not exceed VMM_MAX_VM_MEM_SIZE. 4483a0db596Smlarkin * 4493a0db596Smlarkin * Return Values: 4503a0db596Smlarkin * The total memory size in bytes if the checks were successful 4513a0db596Smlarkin * 0: One of the memory ranges was invalid or VMM_MAX_VM_MEM_SIZE was 4523a0db596Smlarkin * exceeded 4533a0db596Smlarkin */ 4543a0db596Smlarkin size_t 4553a0db596Smlarkin vm_create_check_mem_ranges(struct vm_create_params *vcp) 4563a0db596Smlarkin { 4573a0db596Smlarkin size_t i, memsize = 0; 4583a0db596Smlarkin struct vm_mem_range *vmr, *pvmr; 4593a0db596Smlarkin const paddr_t maxgpa = VMM_MAX_VM_MEM_SIZE; 4603a0db596Smlarkin 4613a0db596Smlarkin if (vcp->vcp_nmemranges == 0 || 4623a0db596Smlarkin vcp->vcp_nmemranges > VMM_MAX_MEM_RANGES) { 4633a0db596Smlarkin DPRINTF("invalid number of guest memory ranges\n"); 4643a0db596Smlarkin return (0); 4653a0db596Smlarkin } 4663a0db596Smlarkin 4673a0db596Smlarkin for (i = 0; i < vcp->vcp_nmemranges; i++) { 4683a0db596Smlarkin vmr = &vcp->vcp_memranges[i]; 4693a0db596Smlarkin 4703a0db596Smlarkin /* Only page-aligned addresses and sizes are permitted */ 4713a0db596Smlarkin if ((vmr->vmr_gpa & PAGE_MASK) || (vmr->vmr_va & PAGE_MASK) || 4723a0db596Smlarkin (vmr->vmr_size & PAGE_MASK) || vmr->vmr_size == 0) { 4733a0db596Smlarkin DPRINTF("memory range %zu is not page aligned\n", i); 4743a0db596Smlarkin return (0); 4753a0db596Smlarkin } 4763a0db596Smlarkin 4773a0db596Smlarkin /* Make sure that VMM_MAX_VM_MEM_SIZE is not exceeded */ 4783a0db596Smlarkin if (vmr->vmr_gpa >= maxgpa || 4793a0db596Smlarkin vmr->vmr_size > maxgpa - vmr->vmr_gpa) { 4803a0db596Smlarkin DPRINTF("exceeded max memory size\n"); 4813a0db596Smlarkin return (0); 4823a0db596Smlarkin } 4833a0db596Smlarkin 4843a0db596Smlarkin /* 4853a0db596Smlarkin * Make sure that all virtual addresses are within the address 4863a0db596Smlarkin * space of the process and that they do not wrap around. 4873a0db596Smlarkin * Calling uvm_share() when creating the VM will take care of 4883a0db596Smlarkin * further checks. 4893a0db596Smlarkin */ 4903a0db596Smlarkin if (vmr->vmr_va < VM_MIN_ADDRESS || 4913a0db596Smlarkin vmr->vmr_va >= VM_MAXUSER_ADDRESS || 4923a0db596Smlarkin vmr->vmr_size >= VM_MAXUSER_ADDRESS - vmr->vmr_va) { 4933a0db596Smlarkin DPRINTF("guest va not within range or wraps\n"); 4943a0db596Smlarkin return (0); 4953a0db596Smlarkin } 4963a0db596Smlarkin 4973a0db596Smlarkin /* 4983a0db596Smlarkin * Make sure that guest physical memory ranges do not overlap 4993a0db596Smlarkin * and that they are ascending. 5003a0db596Smlarkin */ 5013a0db596Smlarkin if (i > 0 && pvmr->vmr_gpa + pvmr->vmr_size > vmr->vmr_gpa) { 5023a0db596Smlarkin DPRINTF("guest range %zu overlaps or !ascending\n", i); 5033a0db596Smlarkin return (0); 5043a0db596Smlarkin } 5053a0db596Smlarkin 5063a0db596Smlarkin /* 5073a0db596Smlarkin * No memory is mappable in MMIO ranges, so don't count towards 5083a0db596Smlarkin * the total guest memory size. 5093a0db596Smlarkin */ 5103a0db596Smlarkin if (vmr->vmr_type != VM_MEM_MMIO) 5113a0db596Smlarkin memsize += vmr->vmr_size; 5123a0db596Smlarkin pvmr = vmr; 5133a0db596Smlarkin } 5143a0db596Smlarkin 5153a0db596Smlarkin return (memsize); 5163a0db596Smlarkin } 5173a0db596Smlarkin 5183a0db596Smlarkin /* 5193a0db596Smlarkin * vm_teardown 5203a0db596Smlarkin * 5213a0db596Smlarkin * Tears down (destroys) the vm indicated by 'vm'. 5223a0db596Smlarkin * 5233a0db596Smlarkin * Assumes the vm is already removed from the global vm list (or was never 5243a0db596Smlarkin * added). 5253a0db596Smlarkin * 5263a0db596Smlarkin * Parameters: 5273a0db596Smlarkin * vm: vm to be torn down 5283a0db596Smlarkin */ 5293a0db596Smlarkin void 5303a0db596Smlarkin vm_teardown(struct vm **target) 5313a0db596Smlarkin { 5323a0db596Smlarkin size_t nvcpu = 0; 5333a0db596Smlarkin struct vcpu *vcpu, *tmp; 5343a0db596Smlarkin struct vm *vm = *target; 5353a0db596Smlarkin struct vmspace *vm_vmspace; 5363a0db596Smlarkin 5373a0db596Smlarkin KERNEL_ASSERT_UNLOCKED(); 5383a0db596Smlarkin 5393a0db596Smlarkin /* Free VCPUs */ 5403a0db596Smlarkin SLIST_FOREACH_SAFE(vcpu, &vm->vm_vcpu_list, vc_vcpu_link, tmp) { 5413a0db596Smlarkin SLIST_REMOVE(&vm->vm_vcpu_list, vcpu, vcpu, vc_vcpu_link); 5423a0db596Smlarkin vcpu_deinit(vcpu); 5433a0db596Smlarkin 5443a0db596Smlarkin pool_put(&vcpu_pool, vcpu); 5453a0db596Smlarkin nvcpu++; 5463a0db596Smlarkin } 5473a0db596Smlarkin 5483a0db596Smlarkin vm_impl_deinit(vm); 5493a0db596Smlarkin 5503a0db596Smlarkin /* teardown guest vmspace */ 5513a0db596Smlarkin KERNEL_LOCK(); 5523a0db596Smlarkin vm_vmspace = vm->vm_vmspace; 5533a0db596Smlarkin if (vm_vmspace != NULL) { 5543a0db596Smlarkin vm->vm_vmspace = NULL; 5553a0db596Smlarkin uvmspace_free(vm_vmspace); 5563a0db596Smlarkin } 5573a0db596Smlarkin KERNEL_UNLOCK(); 5583a0db596Smlarkin 5593a0db596Smlarkin pool_put(&vm_pool, vm); 5603a0db596Smlarkin *target = NULL; 5613a0db596Smlarkin } 5623a0db596Smlarkin 5633a0db596Smlarkin /* 5643a0db596Smlarkin * vm_get_info 5653a0db596Smlarkin * 5663a0db596Smlarkin * Returns information about the VM indicated by 'vip'. The 'vip_size' field 5673a0db596Smlarkin * in the 'vip' parameter is used to indicate the size of the caller's buffer. 5683a0db596Smlarkin * If insufficient space exists in that buffer, the required size needed is 5693a0db596Smlarkin * returned in vip_size and the number of VM information structures returned 5703a0db596Smlarkin * in vip_info_count is set to 0. The caller should then try the ioctl again 5713a0db596Smlarkin * after allocating a sufficiently large buffer. 5723a0db596Smlarkin * 5733a0db596Smlarkin * Parameters: 5743a0db596Smlarkin * vip: information structure identifying the VM to query 5753a0db596Smlarkin * 5763a0db596Smlarkin * Return values: 5773a0db596Smlarkin * 0: the operation succeeded 5783a0db596Smlarkin * ENOMEM: memory allocation error during processing 5793a0db596Smlarkin * EFAULT: error copying data to user process 5803a0db596Smlarkin */ 5813a0db596Smlarkin int 5823a0db596Smlarkin vm_get_info(struct vm_info_params *vip) 5833a0db596Smlarkin { 5843a0db596Smlarkin struct vm_info_result *out; 5853a0db596Smlarkin struct vm *vm; 5863a0db596Smlarkin struct vcpu *vcpu; 5873a0db596Smlarkin int i = 0, j; 5883a0db596Smlarkin size_t need, vm_ct; 5893a0db596Smlarkin 5903a0db596Smlarkin rw_enter_read(&vmm_softc->vm_lock); 5913a0db596Smlarkin vm_ct = vmm_softc->vm_ct; 5923a0db596Smlarkin rw_exit_read(&vmm_softc->vm_lock); 5933a0db596Smlarkin 5943a0db596Smlarkin need = vm_ct * sizeof(struct vm_info_result); 5953a0db596Smlarkin if (vip->vip_size < need) { 5963a0db596Smlarkin vip->vip_info_ct = 0; 5973a0db596Smlarkin vip->vip_size = need; 5983a0db596Smlarkin return (0); 5993a0db596Smlarkin } 6003a0db596Smlarkin 6013a0db596Smlarkin out = malloc(need, M_DEVBUF, M_NOWAIT|M_ZERO); 6023a0db596Smlarkin if (out == NULL) { 6033a0db596Smlarkin vip->vip_info_ct = 0; 6043a0db596Smlarkin return (ENOMEM); 6053a0db596Smlarkin } 6063a0db596Smlarkin 6073a0db596Smlarkin vip->vip_info_ct = vm_ct; 6083a0db596Smlarkin 6093a0db596Smlarkin rw_enter_read(&vmm_softc->vm_lock); 6103a0db596Smlarkin SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) { 6113a0db596Smlarkin refcnt_take(&vm->vm_refcnt); 6123a0db596Smlarkin 6133a0db596Smlarkin out[i].vir_memory_size = vm->vm_memory_size; 6143a0db596Smlarkin out[i].vir_used_size = 6153a0db596Smlarkin pmap_resident_count(vm->vm_map->pmap) * PAGE_SIZE; 6163a0db596Smlarkin out[i].vir_ncpus = vm->vm_vcpu_ct; 6173a0db596Smlarkin out[i].vir_id = vm->vm_id; 6183a0db596Smlarkin out[i].vir_creator_pid = vm->vm_creator_pid; 6193a0db596Smlarkin strlcpy(out[i].vir_name, vm->vm_name, VMM_MAX_NAME_LEN); 6203a0db596Smlarkin 6213a0db596Smlarkin for (j = 0; j < vm->vm_vcpu_ct; j++) { 6223a0db596Smlarkin out[i].vir_vcpu_state[j] = VCPU_STATE_UNKNOWN; 6233a0db596Smlarkin SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, 6243a0db596Smlarkin vc_vcpu_link) { 6253a0db596Smlarkin if (vcpu->vc_id == j) 6263a0db596Smlarkin out[i].vir_vcpu_state[j] = 6273a0db596Smlarkin vcpu->vc_state; 6283a0db596Smlarkin } 6293a0db596Smlarkin } 6303a0db596Smlarkin 6313a0db596Smlarkin refcnt_rele_wake(&vm->vm_refcnt); 6323a0db596Smlarkin i++; 6333a0db596Smlarkin if (i == vm_ct) 6343a0db596Smlarkin break; /* Truncate to keep within bounds of 'out'. */ 6353a0db596Smlarkin } 6363a0db596Smlarkin rw_exit_read(&vmm_softc->vm_lock); 6373a0db596Smlarkin 6383a0db596Smlarkin if (copyout(out, vip->vip_info, need) == EFAULT) { 6393a0db596Smlarkin free(out, M_DEVBUF, need); 6403a0db596Smlarkin return (EFAULT); 6413a0db596Smlarkin } 6423a0db596Smlarkin 6433a0db596Smlarkin free(out, M_DEVBUF, need); 6443a0db596Smlarkin return (0); 6453a0db596Smlarkin } 6463a0db596Smlarkin 6473a0db596Smlarkin /* 6483a0db596Smlarkin * vm_terminate 6493a0db596Smlarkin * 6503a0db596Smlarkin * Terminates the VM indicated by 'vtp'. 6513a0db596Smlarkin * 6523a0db596Smlarkin * Parameters: 6533a0db596Smlarkin * vtp: structure defining the VM to terminate 6543a0db596Smlarkin * 6553a0db596Smlarkin * Return values: 6563a0db596Smlarkin * 0: the VM was terminated 6573a0db596Smlarkin * !0: the VM could not be located 6583a0db596Smlarkin */ 6593a0db596Smlarkin int 6603a0db596Smlarkin vm_terminate(struct vm_terminate_params *vtp) 6613a0db596Smlarkin { 6623a0db596Smlarkin struct vm *vm; 6633a0db596Smlarkin int error, nvcpu, vm_id; 6643a0db596Smlarkin 6653a0db596Smlarkin /* 6663a0db596Smlarkin * Find desired VM 6673a0db596Smlarkin */ 6683a0db596Smlarkin error = vm_find(vtp->vtp_vm_id, &vm); 6693a0db596Smlarkin if (error) 6703a0db596Smlarkin return (error); 6713a0db596Smlarkin 6723a0db596Smlarkin /* Pop the vm out of the global vm list. */ 6733a0db596Smlarkin rw_enter_write(&vmm_softc->vm_lock); 6743a0db596Smlarkin SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link); 6753a0db596Smlarkin rw_exit_write(&vmm_softc->vm_lock); 6763a0db596Smlarkin 6773a0db596Smlarkin /* Drop the vm_list's reference to the vm. */ 6783a0db596Smlarkin if (refcnt_rele(&vm->vm_refcnt)) 6793a0db596Smlarkin panic("%s: vm %d(%p) vm_list refcnt drop was the last", 6803a0db596Smlarkin __func__, vm->vm_id, vm); 6813a0db596Smlarkin 6823a0db596Smlarkin /* Wait for our reference (taken from vm_find) is the last active. */ 6833a0db596Smlarkin refcnt_finalize(&vm->vm_refcnt, __func__); 6843a0db596Smlarkin 6853a0db596Smlarkin vm_id = vm->vm_id; 6863a0db596Smlarkin nvcpu = vm->vm_vcpu_ct; 6873a0db596Smlarkin 6883a0db596Smlarkin vm_teardown(&vm); 6893a0db596Smlarkin 6903a0db596Smlarkin if (vm_id > 0) { 6913a0db596Smlarkin rw_enter_write(&vmm_softc->vm_lock); 6923a0db596Smlarkin vmm_softc->vm_ct--; 6933a0db596Smlarkin vmm_softc->vcpu_ct -= nvcpu; 6943a0db596Smlarkin if (vmm_softc->vm_ct < 1) 6953a0db596Smlarkin vmm_stop(); 6963a0db596Smlarkin rw_exit_write(&vmm_softc->vm_lock); 6973a0db596Smlarkin } 6983a0db596Smlarkin 6993a0db596Smlarkin return (0); 7003a0db596Smlarkin } 7013a0db596Smlarkin 7023a0db596Smlarkin /* 7033a0db596Smlarkin * vm_resetcpu 7043a0db596Smlarkin * 7053a0db596Smlarkin * Resets the vcpu defined in 'vrp' to power-on-init register state 7063a0db596Smlarkin * 7073a0db596Smlarkin * Parameters: 7083a0db596Smlarkin * vrp: ioctl structure defining the vcpu to reset (see vmmvar.h) 7093a0db596Smlarkin * 7103a0db596Smlarkin * Returns 0 if successful, or various error codes on failure: 7113a0db596Smlarkin * ENOENT if the VM id contained in 'vrp' refers to an unknown VM or 7123a0db596Smlarkin * if vrp describes an unknown vcpu for this VM 7133a0db596Smlarkin * EBUSY if the indicated VCPU is not stopped 7143a0db596Smlarkin * EIO if the indicated VCPU failed to reset 7153a0db596Smlarkin */ 7163a0db596Smlarkin int 7173a0db596Smlarkin vm_resetcpu(struct vm_resetcpu_params *vrp) 7183a0db596Smlarkin { 7193a0db596Smlarkin struct vm *vm; 7203a0db596Smlarkin struct vcpu *vcpu; 7213a0db596Smlarkin int error, ret = 0; 7223a0db596Smlarkin 7233a0db596Smlarkin /* Find the desired VM */ 7243a0db596Smlarkin error = vm_find(vrp->vrp_vm_id, &vm); 7253a0db596Smlarkin 7263a0db596Smlarkin /* Not found? exit. */ 7273a0db596Smlarkin if (error != 0) { 7283a0db596Smlarkin DPRINTF("%s: vm id %u not found\n", __func__, 7293a0db596Smlarkin vrp->vrp_vm_id); 7303a0db596Smlarkin return (error); 7313a0db596Smlarkin } 7323a0db596Smlarkin 7333a0db596Smlarkin vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id); 7343a0db596Smlarkin 7353a0db596Smlarkin if (vcpu == NULL) { 7363a0db596Smlarkin DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__, 7373a0db596Smlarkin vrp->vrp_vcpu_id, vrp->vrp_vm_id); 7383a0db596Smlarkin ret = ENOENT; 7393a0db596Smlarkin goto out; 7403a0db596Smlarkin } 7413a0db596Smlarkin 7423a0db596Smlarkin rw_enter_write(&vcpu->vc_lock); 7433a0db596Smlarkin if (vcpu->vc_state != VCPU_STATE_STOPPED) 7443a0db596Smlarkin ret = EBUSY; 7453a0db596Smlarkin else { 7463a0db596Smlarkin if (vcpu_reset_regs(vcpu, &vrp->vrp_init_state)) { 7473a0db596Smlarkin printf("%s: failed\n", __func__); 7483a0db596Smlarkin #ifdef VMM_DEBUG 7493a0db596Smlarkin dump_vcpu(vcpu); 7503a0db596Smlarkin #endif /* VMM_DEBUG */ 7513a0db596Smlarkin ret = EIO; 7523a0db596Smlarkin } 7533a0db596Smlarkin } 7543a0db596Smlarkin rw_exit_write(&vcpu->vc_lock); 7553a0db596Smlarkin out: 7563a0db596Smlarkin refcnt_rele_wake(&vm->vm_refcnt); 7573a0db596Smlarkin 7583a0db596Smlarkin return (ret); 7593a0db596Smlarkin } 7603a0db596Smlarkin 7613a0db596Smlarkin /* 7623a0db596Smlarkin * vcpu_must_stop 7633a0db596Smlarkin * 7643a0db596Smlarkin * Check if we need to (temporarily) stop running the VCPU for some reason, 7653a0db596Smlarkin * such as: 7663a0db596Smlarkin * - the VM was requested to terminate 7673a0db596Smlarkin * - the proc running this VCPU has pending signals 7683a0db596Smlarkin * 7693a0db596Smlarkin * Parameters: 7703a0db596Smlarkin * vcpu: the VCPU to check 7713a0db596Smlarkin * 7723a0db596Smlarkin * Return values: 7733a0db596Smlarkin * 1: the VM owning this VCPU should stop 7743a0db596Smlarkin * 0: no stop is needed 7753a0db596Smlarkin */ 7763a0db596Smlarkin int 7773a0db596Smlarkin vcpu_must_stop(struct vcpu *vcpu) 7783a0db596Smlarkin { 7793a0db596Smlarkin struct proc *p = curproc; 7803a0db596Smlarkin 7813a0db596Smlarkin if (vcpu->vc_state == VCPU_STATE_REQTERM) 7823a0db596Smlarkin return (1); 7833a0db596Smlarkin if (SIGPENDING(p) != 0) 7843a0db596Smlarkin return (1); 7853a0db596Smlarkin return (0); 7863a0db596Smlarkin } 7873c817da7Sdv 7883c817da7Sdv /* 7893c817da7Sdv * vm_share_mem 7903c817da7Sdv * 7913c817da7Sdv * Share a uvm mapping for the vm guest memory ranges into the calling process. 7923c817da7Sdv * 7933c817da7Sdv * Return values: 7943c817da7Sdv * 0: if successful 7953c817da7Sdv * ENOENT: if the vm cannot be found by vm_find 7963c817da7Sdv * EPERM: if the vm cannot be accessed by the current process 7973c817da7Sdv * EINVAL: if the provide memory ranges fail checks 7983c817da7Sdv * ENOMEM: if uvm_share fails to find available memory in the destination map 7993c817da7Sdv */ 8003c817da7Sdv int 8013c817da7Sdv vm_share_mem(struct vm_sharemem_params *vsp, struct proc *p) 8023c817da7Sdv { 8033c817da7Sdv int ret = EINVAL; 8043c817da7Sdv size_t i, n; 8053c817da7Sdv struct vm *vm; 8063c817da7Sdv struct vm_mem_range *src, *dst; 8073c817da7Sdv 8083c817da7Sdv ret = vm_find(vsp->vsp_vm_id, &vm); 8093c817da7Sdv if (ret) 8103c817da7Sdv return (ret); 8113c817da7Sdv 8123c817da7Sdv /* Check we have the expected number of ranges. */ 8133c817da7Sdv if (vm->vm_nmemranges != vsp->vsp_nmemranges) 8143c817da7Sdv goto out; 8153c817da7Sdv n = vm->vm_nmemranges; 8163c817da7Sdv 8173c817da7Sdv /* Check their types, sizes, and gpa's (implying page alignment). */ 8183c817da7Sdv for (i = 0; i < n; i++) { 8193c817da7Sdv src = &vm->vm_memranges[i]; 8203c817da7Sdv dst = &vsp->vsp_memranges[i]; 8213c817da7Sdv 8223c817da7Sdv /* 8233c817da7Sdv * The vm memranges were already checked during creation, so 8243c817da7Sdv * compare to them to confirm validity of mapping request. 8253c817da7Sdv */ 8263c817da7Sdv if (src->vmr_type != dst->vmr_type) 8273c817da7Sdv goto out; 8283c817da7Sdv if (src->vmr_gpa != dst->vmr_gpa) 8293c817da7Sdv goto out; 8303c817da7Sdv if (src->vmr_size != dst->vmr_size) 8313c817da7Sdv goto out; 8323c817da7Sdv 8333c817da7Sdv /* Check our intended destination is page-aligned. */ 8343c817da7Sdv if (dst->vmr_va & PAGE_MASK) 8353c817da7Sdv goto out; 8363c817da7Sdv } 8373c817da7Sdv 8383c817da7Sdv /* 8393c817da7Sdv * Share each range individually with the calling process. We do 8403c817da7Sdv * not need PROC_EXEC as the emulated devices do not need to execute 8413c817da7Sdv * instructions from guest memory. 8423c817da7Sdv */ 8433c817da7Sdv for (i = 0; i < n; i++) { 8443c817da7Sdv src = &vm->vm_memranges[i]; 8453c817da7Sdv dst = &vsp->vsp_memranges[i]; 8463c817da7Sdv 8473c817da7Sdv /* Skip MMIO range. */ 8483c817da7Sdv if (src->vmr_type == VM_MEM_MMIO) 8493c817da7Sdv continue; 8503c817da7Sdv 8513c817da7Sdv DPRINTF("sharing gpa=0x%lx for pid %d @ va=0x%lx\n", 8523c817da7Sdv src->vmr_gpa, p->p_p->ps_pid, dst->vmr_va); 8533c817da7Sdv ret = uvm_share(&p->p_vmspace->vm_map, dst->vmr_va, 8543c817da7Sdv PROT_READ | PROT_WRITE, vm->vm_map, src->vmr_gpa, 8553c817da7Sdv src->vmr_size); 8563c817da7Sdv if (ret) { 8573c817da7Sdv printf("%s: uvm_share failed (%d)\n", __func__, ret); 8583c817da7Sdv break; 8593c817da7Sdv } 8603c817da7Sdv } 8613c817da7Sdv ret = 0; 8623c817da7Sdv out: 8633c817da7Sdv refcnt_rele_wake(&vm->vm_refcnt); 8643c817da7Sdv return (ret); 8653c817da7Sdv } 866