| File: | dev/vmm/vmm.c |
| Warning: | line 813, column 6 Access to field 'vm_nmemranges' results in a dereference of a null pointer (loaded from variable 'vm') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: vmm.c,v 1.2 2023/05/13 23:15:28 dv Exp $ */ | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2014-2023 Mike Larkin <mlarkin@openbsd.org> | |||
| 4 | * | |||
| 5 | * Permission to use, copy, modify, and distribute this software for any | |||
| 6 | * purpose with or without fee is hereby granted, provided that the above | |||
| 7 | * copyright notice and this permission notice appear in all copies. | |||
| 8 | * | |||
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 16 | */ | |||
| 17 | ||||
| 18 | #include <sys/param.h> | |||
| 19 | #include <sys/systm.h> | |||
| 20 | #include <sys/device.h> | |||
| 21 | #include <sys/pool.h> | |||
| 22 | #include <sys/pledge.h> | |||
| 23 | #include <sys/proc.h> | |||
| 24 | #include <sys/ioctl.h> | |||
| 25 | #include <sys/malloc.h> | |||
| 26 | #include <sys/signalvar.h> | |||
| 27 | ||||
| 28 | #include <machine/vmmvar.h> | |||
| 29 | ||||
| 30 | #include <dev/vmm/vmm.h> | |||
| 31 | ||||
| 32 | struct vmm_softc *vmm_softc; | |||
| 33 | struct pool vm_pool; | |||
| 34 | struct pool vcpu_pool; | |||
| 35 | ||||
| 36 | struct cfdriver vmm_cd = { | |||
| 37 | NULL((void *)0), "vmm", DV_DULL, CD_SKIPHIBERNATE2 | |||
| 38 | }; | |||
| 39 | ||||
| 40 | const struct cfattach vmm_ca = { | |||
| 41 | sizeof(struct vmm_softc), vmm_probe, vmm_attach, NULL((void *)0), vmm_activate | |||
| 42 | }; | |||
| 43 | ||||
| 44 | int | |||
| 45 | vmm_probe(struct device *parent, void *match, void *aux) | |||
| 46 | { | |||
| 47 | const char **busname = (const char **)aux; | |||
| 48 | ||||
| 49 | if (strcmp(*busname, vmm_cd.cd_name) != 0) | |||
| 50 | return (0); | |||
| 51 | return (1); | |||
| 52 | } | |||
| 53 | ||||
| 54 | void | |||
| 55 | vmm_attach(struct device *parent, struct device *self, void *aux) | |||
| 56 | { | |||
| 57 | struct vmm_softc *sc = (struct vmm_softc *)self; | |||
| 58 | ||||
| 59 | rw_init(&sc->sc_slock, "vmmslk")_rw_init_flags(&sc->sc_slock, "vmmslk", 0, ((void *)0) ); | |||
| 60 | sc->sc_status = VMM_ACTIVE(unsigned int) 1; | |||
| 61 | refcnt_init(&sc->sc_refcnt); | |||
| 62 | ||||
| 63 | sc->vcpu_ct = 0; | |||
| 64 | sc->vcpu_max = VMM_MAX_VCPUS512; | |||
| 65 | sc->vm_ct = 0; | |||
| 66 | sc->vm_idx = 0; | |||
| 67 | ||||
| 68 | SLIST_INIT(&sc->vm_list){ ((&sc->vm_list)->slh_first) = ((void *)0); }; | |||
| 69 | rw_init(&sc->vm_lock, "vm_list")_rw_init_flags(&sc->vm_lock, "vm_list", 0, ((void *)0) ); | |||
| 70 | ||||
| 71 | pool_init(&vm_pool, sizeof(struct vm), 0, IPL_MPFLOOR0x9, PR_WAITOK0x0001, | |||
| 72 | "vmpool", NULL((void *)0)); | |||
| 73 | pool_init(&vcpu_pool, sizeof(struct vcpu), 64, IPL_MPFLOOR0x9, PR_WAITOK0x0001, | |||
| 74 | "vcpupl", NULL((void *)0)); | |||
| 75 | ||||
| 76 | vmm_attach_machdep(parent, self, aux); | |||
| 77 | ||||
| 78 | vmm_softc = sc; | |||
| 79 | printf("\n"); | |||
| 80 | } | |||
| 81 | ||||
| 82 | int | |||
| 83 | vmm_activate(struct device *self, int act) | |||
| 84 | { | |||
| 85 | switch (act) { | |||
| 86 | case DVACT_QUIESCE2: | |||
| 87 | /* Block device users as we're suspending operation. */ | |||
| 88 | rw_enter_write(&vmm_softc->sc_slock); | |||
| 89 | KASSERT(vmm_softc->sc_status == VMM_ACTIVE)((vmm_softc->sc_status == (unsigned int) 1) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/dev/vmm/vmm.c", 89, "vmm_softc->sc_status == VMM_ACTIVE" )); | |||
| 90 | vmm_softc->sc_status = VMM_SUSPENDED(unsigned int) 0; | |||
| 91 | rw_exit_write(&vmm_softc->sc_slock); | |||
| 92 | ||||
| 93 | /* Wait for any device users to finish. */ | |||
| 94 | refcnt_finalize(&vmm_softc->sc_refcnt, "vmmsusp"); | |||
| 95 | ||||
| 96 | vmm_activate_machdep(self, act); | |||
| 97 | break; | |||
| 98 | case DVACT_WAKEUP5: | |||
| 99 | vmm_activate_machdep(self, act); | |||
| 100 | ||||
| 101 | /* Set the device back to active. */ | |||
| 102 | rw_enter_write(&vmm_softc->sc_slock); | |||
| 103 | KASSERT(vmm_softc->sc_status == VMM_SUSPENDED)((vmm_softc->sc_status == (unsigned int) 0) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/dev/vmm/vmm.c", 103, "vmm_softc->sc_status == VMM_SUSPENDED" )); | |||
| 104 | refcnt_init(&vmm_softc->sc_refcnt); | |||
| 105 | vmm_softc->sc_status = VMM_ACTIVE(unsigned int) 1; | |||
| 106 | rw_exit_write(&vmm_softc->sc_slock); | |||
| 107 | ||||
| 108 | /* Notify any waiting device users. */ | |||
| 109 | wakeup(&vmm_softc->sc_status); | |||
| 110 | break; | |||
| 111 | } | |||
| 112 | ||||
| 113 | return (0); | |||
| 114 | } | |||
| 115 | ||||
| 116 | /* | |||
| 117 | * vmmopen | |||
| 118 | * | |||
| 119 | * Called during open of /dev/vmm. | |||
| 120 | * | |||
| 121 | * Parameters: | |||
| 122 | * dev, flag, mode, p: These come from the character device and are | |||
| 123 | * all unused for this function | |||
| 124 | * | |||
| 125 | * Return values: | |||
| 126 | * ENODEV: if vmm(4) didn't attach or no supported CPUs detected | |||
| 127 | * 0: successful open | |||
| 128 | */ | |||
| 129 | int | |||
| 130 | vmmopen(dev_t dev, int flag, int mode, struct proc *p) | |||
| 131 | { | |||
| 132 | /* Don't allow open if we didn't attach */ | |||
| 133 | if (vmm_softc == NULL((void *)0)) | |||
| 134 | return (ENODEV19); | |||
| 135 | ||||
| 136 | /* Don't allow open if we didn't detect any supported CPUs */ | |||
| 137 | if (vmm_softc->mode == VMM_MODE_UNKNOWN) | |||
| 138 | return (ENODEV19); | |||
| 139 | ||||
| 140 | return 0; | |||
| 141 | } | |||
| 142 | ||||
| 143 | /* | |||
| 144 | * vmmclose | |||
| 145 | * | |||
| 146 | * Called when /dev/vmm is closed. Presently unused. | |||
| 147 | */ | |||
| 148 | int | |||
| 149 | vmmclose(dev_t dev, int flag, int mode, struct proc *p) | |||
| 150 | { | |||
| 151 | return 0; | |||
| 152 | } | |||
| 153 | ||||
| 154 | /* | |||
| 155 | * vm_find | |||
| 156 | * | |||
| 157 | * Function to find an existing VM by its identifier. | |||
| 158 | * Must be called under the global vm_lock. | |||
| 159 | * | |||
| 160 | * Parameters: | |||
| 161 | * id: The VM identifier. | |||
| 162 | * *res: A pointer to the VM or NULL if not found | |||
| 163 | * | |||
| 164 | * Return values: | |||
| 165 | * 0: if successful | |||
| 166 | * ENOENT: if the VM defined by 'id' cannot be found | |||
| 167 | * EPERM: if the VM cannot be accessed by the current process | |||
| 168 | */ | |||
| 169 | int | |||
| 170 | vm_find(uint32_t id, struct vm **res) | |||
| 171 | { | |||
| 172 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; | |||
| 173 | struct vm *vm; | |||
| 174 | int ret = ENOENT2; | |||
| 175 | ||||
| 176 | *res = NULL((void *)0); | |||
| 177 | ||||
| 178 | rw_enter_read(&vmm_softc->vm_lock); | |||
| 179 | SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link)for((vm) = ((&vmm_softc->vm_list)->slh_first); (vm) != ((void *)0); (vm) = ((vm)->vm_link.sle_next)) { | |||
| 180 | if (vm->vm_id == id) { | |||
| 181 | /* | |||
| 182 | * In the pledged VM process, only allow to find | |||
| 183 | * the VM that is running in the current process. | |||
| 184 | * The managing vmm parent process can lookup all | |||
| 185 | * all VMs and is indicated by PLEDGE_PROC. | |||
| 186 | */ | |||
| 187 | if (((p->p_p->ps_pledge & | |||
| 188 | (PLEDGE_VMM0x0000000040000000ULL | PLEDGE_PROC0x0000000000001000ULL)) == PLEDGE_VMM0x0000000040000000ULL) && | |||
| 189 | (vm->vm_creator_pid != p->p_p->ps_pid)) | |||
| 190 | ret = EPERM1; | |||
| 191 | else { | |||
| 192 | refcnt_take(&vm->vm_refcnt); | |||
| 193 | *res = vm; | |||
| 194 | ret = 0; | |||
| 195 | } | |||
| 196 | break; | |||
| 197 | } | |||
| 198 | } | |||
| 199 | rw_exit_read(&vmm_softc->vm_lock); | |||
| 200 | ||||
| 201 | if (ret
| |||
| 202 | return (pledge_fail(p, EPERM1, PLEDGE_VMM0x0000000040000000ULL)); | |||
| 203 | return (ret); | |||
| 204 | } | |||
| 205 | ||||
| 206 | /* | |||
| 207 | * vmmioctl | |||
| 208 | * | |||
| 209 | * Main ioctl dispatch routine for /dev/vmm. Parses ioctl type and calls | |||
| 210 | * appropriate lower level handler routine. Returns result to ioctl caller. | |||
| 211 | */ | |||
| 212 | int | |||
| 213 | vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) | |||
| 214 | { | |||
| 215 | int ret; | |||
| 216 | ||||
| 217 | KERNEL_UNLOCK()_kernel_unlock(); | |||
| 218 | ||||
| 219 | ret = rw_enter(&vmm_softc->sc_slock, RW_READ0x0002UL | RW_INTR0x0010UL); | |||
| 220 | if (ret != 0) | |||
| ||||
| 221 | goto out; | |||
| 222 | while (vmm_softc->sc_status != VMM_ACTIVE(unsigned int) 1) { | |||
| 223 | ret = rwsleep_nsec(&vmm_softc->sc_status, &vmm_softc->sc_slock, | |||
| 224 | PWAIT32 | PCATCH0x100, "vmmresume", INFSLP0xffffffffffffffffULL); | |||
| 225 | if (ret != 0) { | |||
| 226 | rw_exit(&vmm_softc->sc_slock); | |||
| 227 | goto out; | |||
| 228 | } | |||
| 229 | } | |||
| 230 | refcnt_take(&vmm_softc->sc_refcnt); | |||
| 231 | rw_exit(&vmm_softc->sc_slock); | |||
| 232 | ||||
| 233 | switch (cmd) { | |||
| 234 | case VMM_IOC_CREATE(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_create_params) & 0x1fff) << 16) | ((('V' )) << 8) | ((1))): | |||
| 235 | if ((ret = vmm_start()) != 0) { | |||
| 236 | vmm_stop(); | |||
| 237 | break; | |||
| 238 | } | |||
| 239 | ret = vm_create((struct vm_create_params *)data, p); | |||
| 240 | break; | |||
| 241 | case VMM_IOC_RUN(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_run_params) & 0x1fff) << 16) | ((('V')) << 8) | ((2))): | |||
| 242 | ret = vm_run((struct vm_run_params *)data); | |||
| 243 | break; | |||
| 244 | case VMM_IOC_INFO(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_info_params) & 0x1fff) << 16) | ((('V')) << 8) | ((3))): | |||
| 245 | ret = vm_get_info((struct vm_info_params *)data); | |||
| 246 | break; | |||
| 247 | case VMM_IOC_TERM((unsigned long)0x80000000 | ((sizeof(struct vm_terminate_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((4))): | |||
| 248 | ret = vm_terminate((struct vm_terminate_params *)data); | |||
| 249 | break; | |||
| 250 | case VMM_IOC_RESETCPU((unsigned long)0x80000000 | ((sizeof(struct vm_resetcpu_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((5))): | |||
| 251 | ret = vm_resetcpu((struct vm_resetcpu_params *)data); | |||
| 252 | break; | |||
| 253 | case VMM_IOC_READREGS(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_rwregs_params) & 0x1fff) << 16) | ((('V' )) << 8) | ((7))): | |||
| 254 | ret = vm_rwregs((struct vm_rwregs_params *)data, 0); | |||
| 255 | break; | |||
| 256 | case VMM_IOC_WRITEREGS((unsigned long)0x80000000 | ((sizeof(struct vm_rwregs_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((8))): | |||
| 257 | ret = vm_rwregs((struct vm_rwregs_params *)data, 1); | |||
| 258 | break; | |||
| 259 | case VMM_IOC_READVMPARAMS(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_rwvmparams_params) & 0x1fff) << 16) | (( ('V')) << 8) | ((9))): | |||
| 260 | ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 0); | |||
| 261 | break; | |||
| 262 | case VMM_IOC_WRITEVMPARAMS((unsigned long)0x80000000 | ((sizeof(struct vm_rwvmparams_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((10))): | |||
| 263 | ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 1); | |||
| 264 | break; | |||
| 265 | case VMM_IOC_SHAREMEM((unsigned long)0x80000000 | ((sizeof(struct vm_sharemem_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((11))): | |||
| 266 | ret = vm_share_mem((struct vm_sharemem_params *)data, p); | |||
| 267 | break; | |||
| 268 | default: | |||
| 269 | ret = vmmioctl_machdep(dev, cmd, data, flag, p); | |||
| 270 | break; | |||
| 271 | } | |||
| 272 | ||||
| 273 | refcnt_rele_wake(&vmm_softc->sc_refcnt); | |||
| 274 | out: | |||
| 275 | KERNEL_LOCK()_kernel_lock(); | |||
| 276 | ||||
| 277 | return (ret); | |||
| 278 | } | |||
| 279 | ||||
| 280 | /* | |||
| 281 | * pledge_ioctl_vmm | |||
| 282 | * | |||
| 283 | * Restrict the allowed ioctls in a pledged process context. | |||
| 284 | * Is called from pledge_ioctl(). | |||
| 285 | */ | |||
| 286 | int | |||
| 287 | pledge_ioctl_vmm(struct proc *p, long com) | |||
| 288 | { | |||
| 289 | switch (com) { | |||
| 290 | case VMM_IOC_CREATE(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_create_params) & 0x1fff) << 16) | ((('V' )) << 8) | ((1))): | |||
| 291 | case VMM_IOC_INFO(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_info_params) & 0x1fff) << 16) | ((('V')) << 8) | ((3))): | |||
| 292 | case VMM_IOC_SHAREMEM((unsigned long)0x80000000 | ((sizeof(struct vm_sharemem_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((11))): | |||
| 293 | /* The "parent" process in vmd forks and manages VMs */ | |||
| 294 | if (p->p_p->ps_pledge & PLEDGE_PROC0x0000000000001000ULL) | |||
| 295 | return (0); | |||
| 296 | break; | |||
| 297 | case VMM_IOC_TERM((unsigned long)0x80000000 | ((sizeof(struct vm_terminate_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((4))): | |||
| 298 | /* XXX VM processes should only terminate themselves */ | |||
| 299 | case VMM_IOC_RUN(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_run_params) & 0x1fff) << 16) | ((('V')) << 8) | ((2))): | |||
| 300 | case VMM_IOC_RESETCPU((unsigned long)0x80000000 | ((sizeof(struct vm_resetcpu_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((5))): | |||
| 301 | case VMM_IOC_READREGS(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_rwregs_params) & 0x1fff) << 16) | ((('V' )) << 8) | ((7))): | |||
| 302 | case VMM_IOC_WRITEREGS((unsigned long)0x80000000 | ((sizeof(struct vm_rwregs_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((8))): | |||
| 303 | case VMM_IOC_READVMPARAMS(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct vm_rwvmparams_params) & 0x1fff) << 16) | (( ('V')) << 8) | ((9))): | |||
| 304 | case VMM_IOC_WRITEVMPARAMS((unsigned long)0x80000000 | ((sizeof(struct vm_rwvmparams_params ) & 0x1fff) << 16) | ((('V')) << 8) | ((10))): | |||
| 305 | return (0); | |||
| 306 | default: | |||
| 307 | return pledge_ioctl_vmm_machdep(p, com); | |||
| 308 | } | |||
| 309 | ||||
| 310 | return (EPERM1); | |||
| 311 | } | |||
| 312 | ||||
| 313 | /* | |||
| 314 | * vm_find_vcpu | |||
| 315 | * | |||
| 316 | * Lookup VMM VCPU by ID number | |||
| 317 | * | |||
| 318 | * Parameters: | |||
| 319 | * vm: vm structure | |||
| 320 | * id: index id of vcpu | |||
| 321 | * | |||
| 322 | * Returns pointer to vcpu structure if successful, NULL otherwise | |||
| 323 | */ | |||
| 324 | struct vcpu * | |||
| 325 | vm_find_vcpu(struct vm *vm, uint32_t id) | |||
| 326 | { | |||
| 327 | struct vcpu *vcpu; | |||
| 328 | ||||
| 329 | if (vm == NULL((void *)0)) | |||
| 330 | return (NULL((void *)0)); | |||
| 331 | ||||
| 332 | SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link)for((vcpu) = ((&vm->vm_vcpu_list)->slh_first); (vcpu ) != ((void *)0); (vcpu) = ((vcpu)->vc_vcpu_link.sle_next) ) { | |||
| 333 | if (vcpu->vc_id == id) | |||
| 334 | return (vcpu); | |||
| 335 | } | |||
| 336 | ||||
| 337 | return (NULL((void *)0)); | |||
| 338 | } | |||
| 339 | ||||
| 340 | /* | |||
| 341 | * vm_create | |||
| 342 | * | |||
| 343 | * Creates the in-memory VMM structures for the VM defined by 'vcp'. The | |||
| 344 | * parent of this VM shall be the process defined by 'p'. | |||
| 345 | * This function does not start the VCPU(s) - see vm_start. | |||
| 346 | * | |||
| 347 | * Return Values: | |||
| 348 | * 0: the create operation was successful | |||
| 349 | * ENOMEM: out of memory | |||
| 350 | * various other errors from vcpu_init/vm_impl_init | |||
| 351 | */ | |||
| 352 | int | |||
| 353 | vm_create(struct vm_create_params *vcp, struct proc *p) | |||
| 354 | { | |||
| 355 | int i, ret; | |||
| 356 | size_t memsize; | |||
| 357 | struct vm *vm; | |||
| 358 | struct vcpu *vcpu; | |||
| 359 | ||||
| 360 | memsize = vm_create_check_mem_ranges(vcp); | |||
| 361 | if (memsize == 0) | |||
| 362 | return (EINVAL22); | |||
| 363 | ||||
| 364 | /* XXX - support UP only (for now) */ | |||
| 365 | if (vcp->vcp_ncpus != 1) | |||
| 366 | return (EINVAL22); | |||
| 367 | ||||
| 368 | /* Bail early if we're already at vcpu capacity. */ | |||
| 369 | rw_enter_read(&vmm_softc->vm_lock); | |||
| 370 | if (vmm_softc->vcpu_ct + vcp->vcp_ncpus > vmm_softc->vcpu_max) { | |||
| 371 | DPRINTF("%s: maximum vcpus (%lu) reached\n", __func__, | |||
| 372 | vmm_softc->vcpu_max); | |||
| 373 | rw_exit_read(&vmm_softc->vm_lock); | |||
| 374 | return (ENOMEM12); | |||
| 375 | } | |||
| 376 | rw_exit_read(&vmm_softc->vm_lock); | |||
| 377 | ||||
| 378 | /* Instantiate and configure the new vm. */ | |||
| 379 | vm = pool_get(&vm_pool, PR_WAITOK0x0001 | PR_ZERO0x0008); | |||
| 380 | ||||
| 381 | vm->vm_creator_pid = p->p_p->ps_pid; | |||
| 382 | vm->vm_nmemranges = vcp->vcp_nmemranges; | |||
| 383 | memcpy(vm->vm_memranges, vcp->vcp_memranges,__builtin_memcpy((vm->vm_memranges), (vcp->vcp_memranges ), (vm->vm_nmemranges * sizeof(vm->vm_memranges[0]))) | |||
| 384 | vm->vm_nmemranges * sizeof(vm->vm_memranges[0]))__builtin_memcpy((vm->vm_memranges), (vcp->vcp_memranges ), (vm->vm_nmemranges * sizeof(vm->vm_memranges[0]))); | |||
| 385 | vm->vm_memory_size = memsize; | |||
| 386 | strncpy(vm->vm_name, vcp->vcp_name, VMM_MAX_NAME_LEN64 - 1); | |||
| 387 | ||||
| 388 | if (vm_impl_init(vm, p)) { | |||
| 389 | printf("failed to init arch-specific features for vm %p\n", vm); | |||
| 390 | vm_teardown(&vm); | |||
| 391 | return (ENOMEM12); | |||
| 392 | } | |||
| 393 | ||||
| 394 | vm->vm_vcpu_ct = 0; | |||
| 395 | ||||
| 396 | /* Initialize each VCPU defined in 'vcp' */ | |||
| 397 | SLIST_INIT(&vm->vm_vcpu_list){ ((&vm->vm_vcpu_list)->slh_first) = ((void *)0); }; | |||
| 398 | for (i = 0; i < vcp->vcp_ncpus; i++) { | |||
| 399 | vcpu = pool_get(&vcpu_pool, PR_WAITOK0x0001 | PR_ZERO0x0008); | |||
| 400 | ||||
| 401 | vcpu->vc_parent = vm; | |||
| 402 | if ((ret = vcpu_init(vcpu)) != 0) { | |||
| 403 | printf("failed to init vcpu %d for vm %p\n", i, vm); | |||
| 404 | vm_teardown(&vm); | |||
| 405 | return (ret); | |||
| 406 | } | |||
| 407 | vcpu->vc_id = vm->vm_vcpu_ct; | |||
| 408 | vm->vm_vcpu_ct++; | |||
| 409 | /* Publish vcpu to list, inheriting the reference. */ | |||
| 410 | SLIST_INSERT_HEAD(&vm->vm_vcpu_list, vcpu, vc_vcpu_link)do { (vcpu)->vc_vcpu_link.sle_next = (&vm->vm_vcpu_list )->slh_first; (&vm->vm_vcpu_list)->slh_first = ( vcpu); } while (0); | |||
| 411 | } | |||
| 412 | ||||
| 413 | /* Attempt to register the vm now that it's configured. */ | |||
| 414 | rw_enter_write(&vmm_softc->vm_lock); | |||
| 415 | ||||
| 416 | if (vmm_softc->vcpu_ct + vm->vm_vcpu_ct > vmm_softc->vcpu_max) { | |||
| 417 | /* Someone already took our capacity. */ | |||
| 418 | printf("%s: maximum vcpus (%lu) reached\n", __func__, | |||
| 419 | vmm_softc->vcpu_max); | |||
| 420 | rw_exit_write(&vmm_softc->vm_lock); | |||
| 421 | vm_teardown(&vm); | |||
| 422 | return (ENOMEM12); | |||
| 423 | } | |||
| 424 | ||||
| 425 | /* Update the global index and identify the vm. */ | |||
| 426 | vmm_softc->vm_idx++; | |||
| 427 | vm->vm_id = vmm_softc->vm_idx; | |||
| 428 | vcp->vcp_id = vm->vm_id; | |||
| 429 | ||||
| 430 | /* Publish the vm into the list and update counts. */ | |||
| 431 | refcnt_init(&vm->vm_refcnt); | |||
| 432 | SLIST_INSERT_HEAD(&vmm_softc->vm_list, vm, vm_link)do { (vm)->vm_link.sle_next = (&vmm_softc->vm_list) ->slh_first; (&vmm_softc->vm_list)->slh_first = ( vm); } while (0); | |||
| 433 | vmm_softc->vm_ct++; | |||
| 434 | vmm_softc->vcpu_ct += vm->vm_vcpu_ct; | |||
| 435 | ||||
| 436 | rw_exit_write(&vmm_softc->vm_lock); | |||
| 437 | ||||
| 438 | return (0); | |||
| 439 | } | |||
| 440 | ||||
| 441 | /* | |||
| 442 | * vm_create_check_mem_ranges | |||
| 443 | * | |||
| 444 | * Make sure that the guest physical memory ranges given by the user process | |||
| 445 | * do not overlap and are in ascending order. | |||
| 446 | * | |||
| 447 | * The last physical address may not exceed VMM_MAX_VM_MEM_SIZE. | |||
| 448 | * | |||
| 449 | * Return Values: | |||
| 450 | * The total memory size in bytes if the checks were successful | |||
| 451 | * 0: One of the memory ranges was invalid or VMM_MAX_VM_MEM_SIZE was | |||
| 452 | * exceeded | |||
| 453 | */ | |||
| 454 | size_t | |||
| 455 | vm_create_check_mem_ranges(struct vm_create_params *vcp) | |||
| 456 | { | |||
| 457 | size_t i, memsize = 0; | |||
| 458 | struct vm_mem_range *vmr, *pvmr; | |||
| 459 | const paddr_t maxgpa = VMM_MAX_VM_MEM_SIZE32L * 1024 * 1024 * 1024; | |||
| 460 | ||||
| 461 | if (vcp->vcp_nmemranges == 0 || | |||
| 462 | vcp->vcp_nmemranges > VMM_MAX_MEM_RANGES16) { | |||
| 463 | DPRINTF("invalid number of guest memory ranges\n"); | |||
| 464 | return (0); | |||
| 465 | } | |||
| 466 | ||||
| 467 | for (i = 0; i < vcp->vcp_nmemranges; i++) { | |||
| 468 | vmr = &vcp->vcp_memranges[i]; | |||
| 469 | ||||
| 470 | /* Only page-aligned addresses and sizes are permitted */ | |||
| 471 | if ((vmr->vmr_gpa & PAGE_MASK((1 << 12) - 1)) || (vmr->vmr_va & PAGE_MASK((1 << 12) - 1)) || | |||
| 472 | (vmr->vmr_size & PAGE_MASK((1 << 12) - 1)) || vmr->vmr_size == 0) { | |||
| 473 | DPRINTF("memory range %zu is not page aligned\n", i); | |||
| 474 | return (0); | |||
| 475 | } | |||
| 476 | ||||
| 477 | /* Make sure that VMM_MAX_VM_MEM_SIZE is not exceeded */ | |||
| 478 | if (vmr->vmr_gpa >= maxgpa || | |||
| 479 | vmr->vmr_size > maxgpa - vmr->vmr_gpa) { | |||
| 480 | DPRINTF("exceeded max memory size\n"); | |||
| 481 | return (0); | |||
| 482 | } | |||
| 483 | ||||
| 484 | /* | |||
| 485 | * Make sure that all virtual addresses are within the address | |||
| 486 | * space of the process and that they do not wrap around. | |||
| 487 | * Calling uvm_share() when creating the VM will take care of | |||
| 488 | * further checks. | |||
| 489 | */ | |||
| 490 | if (vmr->vmr_va < VM_MIN_ADDRESS(1 << 12) || | |||
| 491 | vmr->vmr_va >= VM_MAXUSER_ADDRESS0x00007f7fffffc000 || | |||
| 492 | vmr->vmr_size >= VM_MAXUSER_ADDRESS0x00007f7fffffc000 - vmr->vmr_va) { | |||
| 493 | DPRINTF("guest va not within range or wraps\n"); | |||
| 494 | return (0); | |||
| 495 | } | |||
| 496 | ||||
| 497 | /* | |||
| 498 | * Make sure that guest physical memory ranges do not overlap | |||
| 499 | * and that they are ascending. | |||
| 500 | */ | |||
| 501 | if (i > 0 && pvmr->vmr_gpa + pvmr->vmr_size > vmr->vmr_gpa) { | |||
| 502 | DPRINTF("guest range %zu overlaps or !ascending\n", i); | |||
| 503 | return (0); | |||
| 504 | } | |||
| 505 | ||||
| 506 | /* | |||
| 507 | * No memory is mappable in MMIO ranges, so don't count towards | |||
| 508 | * the total guest memory size. | |||
| 509 | */ | |||
| 510 | if (vmr->vmr_type != VM_MEM_MMIO2) | |||
| 511 | memsize += vmr->vmr_size; | |||
| 512 | pvmr = vmr; | |||
| 513 | } | |||
| 514 | ||||
| 515 | return (memsize); | |||
| 516 | } | |||
| 517 | ||||
| 518 | /* | |||
| 519 | * vm_teardown | |||
| 520 | * | |||
| 521 | * Tears down (destroys) the vm indicated by 'vm'. | |||
| 522 | * | |||
| 523 | * Assumes the vm is already removed from the global vm list (or was never | |||
| 524 | * added). | |||
| 525 | * | |||
| 526 | * Parameters: | |||
| 527 | * vm: vm to be torn down | |||
| 528 | */ | |||
| 529 | void | |||
| 530 | vm_teardown(struct vm **target) | |||
| 531 | { | |||
| 532 | size_t nvcpu = 0; | |||
| 533 | struct vcpu *vcpu, *tmp; | |||
| 534 | struct vm *vm = *target; | |||
| 535 | struct vmspace *vm_vmspace; | |||
| 536 | ||||
| 537 | KERNEL_ASSERT_UNLOCKED()((panicstr || db_active || !_kernel_lock_held()) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/dev/vmm/vmm.c", 537, "panicstr || db_active || !_kernel_lock_held()" )); | |||
| 538 | ||||
| 539 | /* Free VCPUs */ | |||
| 540 | SLIST_FOREACH_SAFE(vcpu, &vm->vm_vcpu_list, vc_vcpu_link, tmp)for ((vcpu) = ((&vm->vm_vcpu_list)->slh_first); (vcpu ) && ((tmp) = ((vcpu)->vc_vcpu_link.sle_next), 1); (vcpu) = (tmp)) { | |||
| 541 | SLIST_REMOVE(&vm->vm_vcpu_list, vcpu, vcpu, vc_vcpu_link)do { if ((&vm->vm_vcpu_list)->slh_first == (vcpu)) { do { ((&vm->vm_vcpu_list))->slh_first = ((&vm-> vm_vcpu_list))->slh_first->vc_vcpu_link.sle_next; } while (0); } else { struct vcpu *curelm = (&vm->vm_vcpu_list )->slh_first; while (curelm->vc_vcpu_link.sle_next != ( vcpu)) curelm = curelm->vc_vcpu_link.sle_next; curelm-> vc_vcpu_link.sle_next = curelm->vc_vcpu_link.sle_next-> vc_vcpu_link.sle_next; } ((vcpu)->vc_vcpu_link.sle_next) = ((void *)-1); } while (0); | |||
| 542 | vcpu_deinit(vcpu); | |||
| 543 | ||||
| 544 | pool_put(&vcpu_pool, vcpu); | |||
| 545 | nvcpu++; | |||
| 546 | } | |||
| 547 | ||||
| 548 | vm_impl_deinit(vm); | |||
| 549 | ||||
| 550 | /* teardown guest vmspace */ | |||
| 551 | KERNEL_LOCK()_kernel_lock(); | |||
| 552 | vm_vmspace = vm->vm_vmspace; | |||
| 553 | if (vm_vmspace != NULL((void *)0)) { | |||
| 554 | vm->vm_vmspace = NULL((void *)0); | |||
| 555 | uvmspace_free(vm_vmspace); | |||
| 556 | } | |||
| 557 | KERNEL_UNLOCK()_kernel_unlock(); | |||
| 558 | ||||
| 559 | pool_put(&vm_pool, vm); | |||
| 560 | *target = NULL((void *)0); | |||
| 561 | } | |||
| 562 | ||||
| 563 | /* | |||
| 564 | * vm_get_info | |||
| 565 | * | |||
| 566 | * Returns information about the VM indicated by 'vip'. The 'vip_size' field | |||
| 567 | * in the 'vip' parameter is used to indicate the size of the caller's buffer. | |||
| 568 | * If insufficient space exists in that buffer, the required size needed is | |||
| 569 | * returned in vip_size and the number of VM information structures returned | |||
| 570 | * in vip_info_count is set to 0. The caller should then try the ioctl again | |||
| 571 | * after allocating a sufficiently large buffer. | |||
| 572 | * | |||
| 573 | * Parameters: | |||
| 574 | * vip: information structure identifying the VM to query | |||
| 575 | * | |||
| 576 | * Return values: | |||
| 577 | * 0: the operation succeeded | |||
| 578 | * ENOMEM: memory allocation error during processing | |||
| 579 | * EFAULT: error copying data to user process | |||
| 580 | */ | |||
| 581 | int | |||
| 582 | vm_get_info(struct vm_info_params *vip) | |||
| 583 | { | |||
| 584 | struct vm_info_result *out; | |||
| 585 | struct vm *vm; | |||
| 586 | struct vcpu *vcpu; | |||
| 587 | int i = 0, j; | |||
| 588 | size_t need, vm_ct; | |||
| 589 | ||||
| 590 | rw_enter_read(&vmm_softc->vm_lock); | |||
| 591 | vm_ct = vmm_softc->vm_ct; | |||
| 592 | rw_exit_read(&vmm_softc->vm_lock); | |||
| 593 | ||||
| 594 | need = vm_ct * sizeof(struct vm_info_result); | |||
| 595 | if (vip->vip_size < need) { | |||
| 596 | vip->vip_info_ct = 0; | |||
| 597 | vip->vip_size = need; | |||
| 598 | return (0); | |||
| 599 | } | |||
| 600 | ||||
| 601 | out = malloc(need, M_DEVBUF2, M_NOWAIT0x0002|M_ZERO0x0008); | |||
| 602 | if (out == NULL((void *)0)) { | |||
| 603 | vip->vip_info_ct = 0; | |||
| 604 | return (ENOMEM12); | |||
| 605 | } | |||
| 606 | ||||
| 607 | vip->vip_info_ct = vm_ct; | |||
| 608 | ||||
| 609 | rw_enter_read(&vmm_softc->vm_lock); | |||
| 610 | SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link)for((vm) = ((&vmm_softc->vm_list)->slh_first); (vm) != ((void *)0); (vm) = ((vm)->vm_link.sle_next)) { | |||
| 611 | refcnt_take(&vm->vm_refcnt); | |||
| 612 | ||||
| 613 | out[i].vir_memory_size = vm->vm_memory_size; | |||
| 614 | out[i].vir_used_size = | |||
| 615 | pmap_resident_count(vm->vm_map->pmap)((vm->vm_map->pmap)->pm_stats.resident_count) * PAGE_SIZE(1 << 12); | |||
| 616 | out[i].vir_ncpus = vm->vm_vcpu_ct; | |||
| 617 | out[i].vir_id = vm->vm_id; | |||
| 618 | out[i].vir_creator_pid = vm->vm_creator_pid; | |||
| 619 | strlcpy(out[i].vir_name, vm->vm_name, VMM_MAX_NAME_LEN64); | |||
| 620 | ||||
| 621 | for (j = 0; j < vm->vm_vcpu_ct; j++) { | |||
| 622 | out[i].vir_vcpu_state[j] = VCPU_STATE_UNKNOWN; | |||
| 623 | SLIST_FOREACH(vcpu, &vm->vm_vcpu_list,for((vcpu) = ((&vm->vm_vcpu_list)->slh_first); (vcpu ) != ((void *)0); (vcpu) = ((vcpu)->vc_vcpu_link.sle_next) ) | |||
| 624 | vc_vcpu_link)for((vcpu) = ((&vm->vm_vcpu_list)->slh_first); (vcpu ) != ((void *)0); (vcpu) = ((vcpu)->vc_vcpu_link.sle_next) ) { | |||
| 625 | if (vcpu->vc_id == j) | |||
| 626 | out[i].vir_vcpu_state[j] = | |||
| 627 | vcpu->vc_state; | |||
| 628 | } | |||
| 629 | } | |||
| 630 | ||||
| 631 | refcnt_rele_wake(&vm->vm_refcnt); | |||
| 632 | i++; | |||
| 633 | if (i == vm_ct) | |||
| 634 | break; /* Truncate to keep within bounds of 'out'. */ | |||
| 635 | } | |||
| 636 | rw_exit_read(&vmm_softc->vm_lock); | |||
| 637 | ||||
| 638 | if (copyout(out, vip->vip_info, need) == EFAULT14) { | |||
| 639 | free(out, M_DEVBUF2, need); | |||
| 640 | return (EFAULT14); | |||
| 641 | } | |||
| 642 | ||||
| 643 | free(out, M_DEVBUF2, need); | |||
| 644 | return (0); | |||
| 645 | } | |||
| 646 | ||||
| 647 | /* | |||
| 648 | * vm_terminate | |||
| 649 | * | |||
| 650 | * Terminates the VM indicated by 'vtp'. | |||
| 651 | * | |||
| 652 | * Parameters: | |||
| 653 | * vtp: structure defining the VM to terminate | |||
| 654 | * | |||
| 655 | * Return values: | |||
| 656 | * 0: the VM was terminated | |||
| 657 | * !0: the VM could not be located | |||
| 658 | */ | |||
| 659 | int | |||
| 660 | vm_terminate(struct vm_terminate_params *vtp) | |||
| 661 | { | |||
| 662 | struct vm *vm; | |||
| 663 | int error, nvcpu, vm_id; | |||
| 664 | ||||
| 665 | /* | |||
| 666 | * Find desired VM | |||
| 667 | */ | |||
| 668 | error = vm_find(vtp->vtp_vm_id, &vm); | |||
| 669 | if (error) | |||
| 670 | return (error); | |||
| 671 | ||||
| 672 | /* Pop the vm out of the global vm list. */ | |||
| 673 | rw_enter_write(&vmm_softc->vm_lock); | |||
| 674 | SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link)do { if ((&vmm_softc->vm_list)->slh_first == (vm)) { do { ((&vmm_softc->vm_list))->slh_first = ((&vmm_softc ->vm_list))->slh_first->vm_link.sle_next; } while (0 ); } else { struct vm *curelm = (&vmm_softc->vm_list)-> slh_first; while (curelm->vm_link.sle_next != (vm)) curelm = curelm->vm_link.sle_next; curelm->vm_link.sle_next = curelm->vm_link.sle_next->vm_link.sle_next; } ((vm)-> vm_link.sle_next) = ((void *)-1); } while (0); | |||
| 675 | rw_exit_write(&vmm_softc->vm_lock); | |||
| 676 | ||||
| 677 | /* Drop the vm_list's reference to the vm. */ | |||
| 678 | if (refcnt_rele(&vm->vm_refcnt)) | |||
| 679 | panic("%s: vm %d(%p) vm_list refcnt drop was the last", | |||
| 680 | __func__, vm->vm_id, vm); | |||
| 681 | ||||
| 682 | /* Wait for our reference (taken from vm_find) is the last active. */ | |||
| 683 | refcnt_finalize(&vm->vm_refcnt, __func__); | |||
| 684 | ||||
| 685 | vm_id = vm->vm_id; | |||
| 686 | nvcpu = vm->vm_vcpu_ct; | |||
| 687 | ||||
| 688 | vm_teardown(&vm); | |||
| 689 | ||||
| 690 | if (vm_id > 0) { | |||
| 691 | rw_enter_write(&vmm_softc->vm_lock); | |||
| 692 | vmm_softc->vm_ct--; | |||
| 693 | vmm_softc->vcpu_ct -= nvcpu; | |||
| 694 | if (vmm_softc->vm_ct < 1) | |||
| 695 | vmm_stop(); | |||
| 696 | rw_exit_write(&vmm_softc->vm_lock); | |||
| 697 | } | |||
| 698 | ||||
| 699 | return (0); | |||
| 700 | } | |||
| 701 | ||||
| 702 | /* | |||
| 703 | * vm_resetcpu | |||
| 704 | * | |||
| 705 | * Resets the vcpu defined in 'vrp' to power-on-init register state | |||
| 706 | * | |||
| 707 | * Parameters: | |||
| 708 | * vrp: ioctl structure defining the vcpu to reset (see vmmvar.h) | |||
| 709 | * | |||
| 710 | * Returns 0 if successful, or various error codes on failure: | |||
| 711 | * ENOENT if the VM id contained in 'vrp' refers to an unknown VM or | |||
| 712 | * if vrp describes an unknown vcpu for this VM | |||
| 713 | * EBUSY if the indicated VCPU is not stopped | |||
| 714 | * EIO if the indicated VCPU failed to reset | |||
| 715 | */ | |||
| 716 | int | |||
| 717 | vm_resetcpu(struct vm_resetcpu_params *vrp) | |||
| 718 | { | |||
| 719 | struct vm *vm; | |||
| 720 | struct vcpu *vcpu; | |||
| 721 | int error, ret = 0; | |||
| 722 | ||||
| 723 | /* Find the desired VM */ | |||
| 724 | error = vm_find(vrp->vrp_vm_id, &vm); | |||
| 725 | ||||
| 726 | /* Not found? exit. */ | |||
| 727 | if (error != 0) { | |||
| 728 | DPRINTF("%s: vm id %u not found\n", __func__, | |||
| 729 | vrp->vrp_vm_id); | |||
| 730 | return (error); | |||
| 731 | } | |||
| 732 | ||||
| 733 | vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id); | |||
| 734 | ||||
| 735 | if (vcpu == NULL((void *)0)) { | |||
| 736 | DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__, | |||
| 737 | vrp->vrp_vcpu_id, vrp->vrp_vm_id); | |||
| 738 | ret = ENOENT2; | |||
| 739 | goto out; | |||
| 740 | } | |||
| 741 | ||||
| 742 | rw_enter_write(&vcpu->vc_lock); | |||
| 743 | if (vcpu->vc_state != VCPU_STATE_STOPPED) | |||
| 744 | ret = EBUSY16; | |||
| 745 | else { | |||
| 746 | if (vcpu_reset_regs(vcpu, &vrp->vrp_init_state)) { | |||
| 747 | printf("%s: failed\n", __func__); | |||
| 748 | #ifdef VMM_DEBUG | |||
| 749 | dump_vcpu(vcpu); | |||
| 750 | #endif /* VMM_DEBUG */ | |||
| 751 | ret = EIO5; | |||
| 752 | } | |||
| 753 | } | |||
| 754 | rw_exit_write(&vcpu->vc_lock); | |||
| 755 | out: | |||
| 756 | refcnt_rele_wake(&vm->vm_refcnt); | |||
| 757 | ||||
| 758 | return (ret); | |||
| 759 | } | |||
| 760 | ||||
| 761 | /* | |||
| 762 | * vcpu_must_stop | |||
| 763 | * | |||
| 764 | * Check if we need to (temporarily) stop running the VCPU for some reason, | |||
| 765 | * such as: | |||
| 766 | * - the VM was requested to terminate | |||
| 767 | * - the proc running this VCPU has pending signals | |||
| 768 | * | |||
| 769 | * Parameters: | |||
| 770 | * vcpu: the VCPU to check | |||
| 771 | * | |||
| 772 | * Return values: | |||
| 773 | * 1: the VM owning this VCPU should stop | |||
| 774 | * 0: no stop is needed | |||
| 775 | */ | |||
| 776 | int | |||
| 777 | vcpu_must_stop(struct vcpu *vcpu) | |||
| 778 | { | |||
| 779 | struct proc *p = curproc({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc; | |||
| 780 | ||||
| 781 | if (vcpu->vc_state == VCPU_STATE_REQTERM) | |||
| 782 | return (1); | |||
| 783 | if (SIGPENDING(p)(((p)->p_siglist | (p)->p_p->ps_siglist) & ~(p)-> p_sigmask) != 0) | |||
| 784 | return (1); | |||
| 785 | return (0); | |||
| 786 | } | |||
| 787 | ||||
| 788 | /* | |||
| 789 | * vm_share_mem | |||
| 790 | * | |||
| 791 | * Share a uvm mapping for the vm guest memory ranges into the calling process. | |||
| 792 | * | |||
| 793 | * Return values: | |||
| 794 | * 0: if successful | |||
| 795 | * ENOENT: if the vm cannot be found by vm_find | |||
| 796 | * EPERM: if the vm cannot be accessed by the current process | |||
| 797 | * EINVAL: if the provide memory ranges fail checks | |||
| 798 | * ENOMEM: if uvm_share fails to find available memory in the destination map | |||
| 799 | */ | |||
| 800 | int | |||
| 801 | vm_share_mem(struct vm_sharemem_params *vsp, struct proc *p) | |||
| 802 | { | |||
| 803 | int ret = EINVAL22; | |||
| 804 | size_t i, n; | |||
| 805 | struct vm *vm; | |||
| 806 | struct vm_mem_range *src, *dst; | |||
| 807 | ||||
| 808 | ret = vm_find(vsp->vsp_vm_id, &vm); | |||
| 809 | if (ret) | |||
| 810 | return (ret); | |||
| 811 | ||||
| 812 | /* Check we have the expected number of ranges. */ | |||
| 813 | if (vm->vm_nmemranges != vsp->vsp_nmemranges) | |||
| ||||
| 814 | goto out; | |||
| 815 | n = vm->vm_nmemranges; | |||
| 816 | ||||
| 817 | /* Check their types, sizes, and gpa's (implying page alignment). */ | |||
| 818 | for (i = 0; i < n; i++) { | |||
| 819 | src = &vm->vm_memranges[i]; | |||
| 820 | dst = &vsp->vsp_memranges[i]; | |||
| 821 | ||||
| 822 | /* | |||
| 823 | * The vm memranges were already checked during creation, so | |||
| 824 | * compare to them to confirm validity of mapping request. | |||
| 825 | */ | |||
| 826 | if (src->vmr_type != dst->vmr_type) | |||
| 827 | goto out; | |||
| 828 | if (src->vmr_gpa != dst->vmr_gpa) | |||
| 829 | goto out; | |||
| 830 | if (src->vmr_size != dst->vmr_size) | |||
| 831 | goto out; | |||
| 832 | ||||
| 833 | /* Check our intended destination is page-aligned. */ | |||
| 834 | if (dst->vmr_va & PAGE_MASK((1 << 12) - 1)) | |||
| 835 | goto out; | |||
| 836 | } | |||
| 837 | ||||
| 838 | /* | |||
| 839 | * Share each range individually with the calling process. We do | |||
| 840 | * not need PROC_EXEC as the emulated devices do not need to execute | |||
| 841 | * instructions from guest memory. | |||
| 842 | */ | |||
| 843 | for (i = 0; i < n; i++) { | |||
| 844 | src = &vm->vm_memranges[i]; | |||
| 845 | dst = &vsp->vsp_memranges[i]; | |||
| 846 | ||||
| 847 | /* Skip MMIO range. */ | |||
| 848 | if (src->vmr_type == VM_MEM_MMIO2) | |||
| 849 | continue; | |||
| 850 | ||||
| 851 | DPRINTF("sharing gpa=0x%lx for pid %d @ va=0x%lx\n", | |||
| 852 | src->vmr_gpa, p->p_p->ps_pid, dst->vmr_va); | |||
| 853 | ret = uvm_share(&p->p_vmspace->vm_map, dst->vmr_va, | |||
| 854 | PROT_READ0x01 | PROT_WRITE0x02, vm->vm_map, src->vmr_gpa, | |||
| 855 | src->vmr_size); | |||
| 856 | if (ret) { | |||
| 857 | printf("%s: uvm_share failed (%d)\n", __func__, ret); | |||
| 858 | break; | |||
| 859 | } | |||
| 860 | } | |||
| 861 | ret = 0; | |||
| 862 | out: | |||
| 863 | refcnt_rele_wake(&vm->vm_refcnt); | |||
| 864 | return (ret); | |||
| 865 | } |