xref: /dflybsd-src/sys/dev/virtual/nvmm/nvmm.c (revision 24f14bf420a8b016f2534fd3c7f3b9e45ce582e3)
16d65b43dSAaron LI /*
2bfc69df0SAaron LI  * Copyright (c) 2018-2021 Maxime Villard, m00nbsd.net
36d65b43dSAaron LI  * All rights reserved.
46d65b43dSAaron LI  *
57f0e1ce2SAaron LI  * This code is part of the NVMM hypervisor.
66d65b43dSAaron LI  *
76d65b43dSAaron LI  * Redistribution and use in source and binary forms, with or without
86d65b43dSAaron LI  * modification, are permitted provided that the following conditions
96d65b43dSAaron LI  * are met:
106d65b43dSAaron LI  * 1. Redistributions of source code must retain the above copyright
116d65b43dSAaron LI  *    notice, this list of conditions and the following disclaimer.
126d65b43dSAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
136d65b43dSAaron LI  *    notice, this list of conditions and the following disclaimer in the
146d65b43dSAaron LI  *    documentation and/or other materials provided with the distribution.
156d65b43dSAaron LI  *
167f0e1ce2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177f0e1ce2SAaron LI  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
187f0e1ce2SAaron LI  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
197f0e1ce2SAaron LI  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
207f0e1ce2SAaron LI  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
217f0e1ce2SAaron LI  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
227f0e1ce2SAaron LI  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
237f0e1ce2SAaron LI  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
247f0e1ce2SAaron LI  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257f0e1ce2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267f0e1ce2SAaron LI  * SUCH DAMAGE.
276d65b43dSAaron LI  */
286d65b43dSAaron LI 
296d65b43dSAaron LI #include <sys/param.h>
306d65b43dSAaron LI #include <sys/systm.h>
316d65b43dSAaron LI 
32db2e0557SAaron LI #include <sys/kernel.h>
3342862644SAaron LI #include <sys/mman.h>
346d65b43dSAaron LI 
35bfc69df0SAaron LI #include "nvmm.h"
36bfc69df0SAaron LI #include "nvmm_internal.h"
37bfc69df0SAaron LI #include "nvmm_ioctl.h"
386d65b43dSAaron LI 
396d65b43dSAaron LI static struct nvmm_machine machines[NVMM_MAX_MACHINES];
40002185e5SAaron LI volatile unsigned int nmachines __cacheline_aligned;
416d65b43dSAaron LI 
426d65b43dSAaron LI static const struct nvmm_impl *nvmm_impl_list[] = {
436d65b43dSAaron LI #if defined(__x86_64__)
446d65b43dSAaron LI 	&nvmm_x86_svm,	/* x86 AMD SVM */
456d65b43dSAaron LI 	&nvmm_x86_vmx	/* x86 Intel VMX */
466d65b43dSAaron LI #endif
476d65b43dSAaron LI };
486d65b43dSAaron LI 
49002185e5SAaron LI const struct nvmm_impl *nvmm_impl __read_mostly = NULL;
506d65b43dSAaron LI 
51002185e5SAaron LI struct nvmm_owner nvmm_root_owner;
526d65b43dSAaron LI 
536d65b43dSAaron LI /* -------------------------------------------------------------------------- */
546d65b43dSAaron LI 
556d65b43dSAaron LI static int
nvmm_machine_alloc(struct nvmm_machine ** ret)566d65b43dSAaron LI nvmm_machine_alloc(struct nvmm_machine **ret)
576d65b43dSAaron LI {
586d65b43dSAaron LI 	struct nvmm_machine *mach;
596d65b43dSAaron LI 	size_t i;
606d65b43dSAaron LI 
616d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_MACHINES; i++) {
626d65b43dSAaron LI 		mach = &machines[i];
636d65b43dSAaron LI 
6442862644SAaron LI 		os_rwl_wlock(&mach->lock);
656d65b43dSAaron LI 		if (mach->present) {
6642862644SAaron LI 			os_rwl_unlock(&mach->lock);
676d65b43dSAaron LI 			continue;
686d65b43dSAaron LI 		}
696d65b43dSAaron LI 
706d65b43dSAaron LI 		mach->present = true;
716d65b43dSAaron LI 		mach->time = time_second;
726d65b43dSAaron LI 		*ret = mach;
7342862644SAaron LI 		os_atomic_inc_uint(&nmachines);
746d65b43dSAaron LI 		return 0;
756d65b43dSAaron LI 	}
766d65b43dSAaron LI 
776d65b43dSAaron LI 	return ENOBUFS;
786d65b43dSAaron LI }
796d65b43dSAaron LI 
806d65b43dSAaron LI static void
nvmm_machine_free(struct nvmm_machine * mach)816d65b43dSAaron LI nvmm_machine_free(struct nvmm_machine *mach)
826d65b43dSAaron LI {
8342862644SAaron LI 	OS_ASSERT(os_rwl_wheld(&mach->lock));
8442862644SAaron LI 	OS_ASSERT(mach->present);
856d65b43dSAaron LI 	mach->present = false;
8642862644SAaron LI 	os_atomic_dec_uint(&nmachines);
876d65b43dSAaron LI }
886d65b43dSAaron LI 
896d65b43dSAaron LI static int
nvmm_machine_get(struct nvmm_owner * owner,nvmm_machid_t machid,struct nvmm_machine ** ret,bool writer)906d65b43dSAaron LI nvmm_machine_get(struct nvmm_owner *owner, nvmm_machid_t machid,
916d65b43dSAaron LI     struct nvmm_machine **ret, bool writer)
926d65b43dSAaron LI {
936d65b43dSAaron LI 	struct nvmm_machine *mach;
946d65b43dSAaron LI 
956d65b43dSAaron LI 	if (__predict_false(machid >= NVMM_MAX_MACHINES)) {
966d65b43dSAaron LI 		return EINVAL;
976d65b43dSAaron LI 	}
986d65b43dSAaron LI 	mach = &machines[machid];
996d65b43dSAaron LI 
10042862644SAaron LI 	if (__predict_false(writer)) {
10142862644SAaron LI 		os_rwl_wlock(&mach->lock);
10242862644SAaron LI 	} else {
10342862644SAaron LI 		os_rwl_rlock(&mach->lock);
10442862644SAaron LI 	}
1056d65b43dSAaron LI 	if (__predict_false(!mach->present)) {
10642862644SAaron LI 		os_rwl_unlock(&mach->lock);
1076d65b43dSAaron LI 		return ENOENT;
1086d65b43dSAaron LI 	}
109002185e5SAaron LI 	if (__predict_false(mach->owner != owner &&
110002185e5SAaron LI 			    owner != &nvmm_root_owner)) {
11142862644SAaron LI 		os_rwl_unlock(&mach->lock);
1126d65b43dSAaron LI 		return EPERM;
1136d65b43dSAaron LI 	}
1146d65b43dSAaron LI 	*ret = mach;
1156d65b43dSAaron LI 
1166d65b43dSAaron LI 	return 0;
1176d65b43dSAaron LI }
1186d65b43dSAaron LI 
1196d65b43dSAaron LI static void
nvmm_machine_put(struct nvmm_machine * mach)1206d65b43dSAaron LI nvmm_machine_put(struct nvmm_machine *mach)
1216d65b43dSAaron LI {
12242862644SAaron LI 	os_rwl_unlock(&mach->lock);
1236d65b43dSAaron LI }
1246d65b43dSAaron LI 
1256d65b43dSAaron LI /* -------------------------------------------------------------------------- */
1266d65b43dSAaron LI 
1276d65b43dSAaron LI static int
nvmm_vcpu_alloc(struct nvmm_machine * mach,nvmm_cpuid_t cpuid,struct nvmm_cpu ** ret)1286d65b43dSAaron LI nvmm_vcpu_alloc(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
1296d65b43dSAaron LI     struct nvmm_cpu **ret)
1306d65b43dSAaron LI {
1316d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
1326d65b43dSAaron LI 
1336d65b43dSAaron LI 	if (cpuid >= NVMM_MAX_VCPUS) {
1346d65b43dSAaron LI 		return EINVAL;
1356d65b43dSAaron LI 	}
1366d65b43dSAaron LI 	vcpu = &mach->cpus[cpuid];
1376d65b43dSAaron LI 
13842862644SAaron LI 	os_mtx_lock(&vcpu->lock);
1396d65b43dSAaron LI 	if (vcpu->present) {
14042862644SAaron LI 		os_mtx_unlock(&vcpu->lock);
1416d65b43dSAaron LI 		return EBUSY;
1426d65b43dSAaron LI 	}
1436d65b43dSAaron LI 
1446d65b43dSAaron LI 	vcpu->present = true;
1456d65b43dSAaron LI 	vcpu->comm = NULL;
1466d65b43dSAaron LI 	vcpu->hcpu_last = -1;
1476d65b43dSAaron LI 	*ret = vcpu;
1486d65b43dSAaron LI 	return 0;
1496d65b43dSAaron LI }
1506d65b43dSAaron LI 
1516d65b43dSAaron LI static void
nvmm_vcpu_free(struct nvmm_machine * mach,struct nvmm_cpu * vcpu)1526d65b43dSAaron LI nvmm_vcpu_free(struct nvmm_machine *mach, struct nvmm_cpu *vcpu)
1536d65b43dSAaron LI {
15442862644SAaron LI 	OS_ASSERT(os_mtx_owned(&vcpu->lock));
1556d65b43dSAaron LI 	vcpu->present = false;
1566d65b43dSAaron LI 	if (vcpu->comm != NULL) {
15742862644SAaron LI 		os_vmobj_unmap(os_kernel_map, (vaddr_t)vcpu->comm,
158d8a75061SAaron LI 		    (vaddr_t)vcpu->comm + NVMM_COMM_PAGE_SIZE, true);
159*cca384e4SAaron LI 		/*
160*cca384e4SAaron LI 		 * Require userland to unmap the comm page from its address
161*cca384e4SAaron LI 		 * space, because os_curproc_map at this point (fd close)
162*cca384e4SAaron LI 		 * is not guaranteed to be the correct address space.
163*cca384e4SAaron LI 		 */
1641759ebc3SAaron LI 	}
1656d65b43dSAaron LI }
1666d65b43dSAaron LI 
1676d65b43dSAaron LI static int
nvmm_vcpu_get(struct nvmm_machine * mach,nvmm_cpuid_t cpuid,struct nvmm_cpu ** ret)1686d65b43dSAaron LI nvmm_vcpu_get(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
1696d65b43dSAaron LI     struct nvmm_cpu **ret)
1706d65b43dSAaron LI {
1716d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
1726d65b43dSAaron LI 
1736d65b43dSAaron LI 	if (__predict_false(cpuid >= NVMM_MAX_VCPUS)) {
1746d65b43dSAaron LI 		return EINVAL;
1756d65b43dSAaron LI 	}
1766d65b43dSAaron LI 	vcpu = &mach->cpus[cpuid];
1776d65b43dSAaron LI 
17842862644SAaron LI 	os_mtx_lock(&vcpu->lock);
1796d65b43dSAaron LI 	if (__predict_false(!vcpu->present)) {
18042862644SAaron LI 		os_mtx_unlock(&vcpu->lock);
1816d65b43dSAaron LI 		return ENOENT;
1826d65b43dSAaron LI 	}
1836d65b43dSAaron LI 	*ret = vcpu;
1846d65b43dSAaron LI 
1856d65b43dSAaron LI 	return 0;
1866d65b43dSAaron LI }
1876d65b43dSAaron LI 
1886d65b43dSAaron LI static void
nvmm_vcpu_put(struct nvmm_cpu * vcpu)1896d65b43dSAaron LI nvmm_vcpu_put(struct nvmm_cpu *vcpu)
1906d65b43dSAaron LI {
19142862644SAaron LI 	os_mtx_unlock(&vcpu->lock);
1926d65b43dSAaron LI }
1936d65b43dSAaron LI 
1946d65b43dSAaron LI /* -------------------------------------------------------------------------- */
1956d65b43dSAaron LI 
196002185e5SAaron LI void
nvmm_kill_machines(struct nvmm_owner * owner)1976d65b43dSAaron LI nvmm_kill_machines(struct nvmm_owner *owner)
1986d65b43dSAaron LI {
1996d65b43dSAaron LI 	struct nvmm_machine *mach;
2006d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
2016d65b43dSAaron LI 	size_t i, j;
2026d65b43dSAaron LI 	int error;
2036d65b43dSAaron LI 
2046d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_MACHINES; i++) {
2056d65b43dSAaron LI 		mach = &machines[i];
2066d65b43dSAaron LI 
20742862644SAaron LI 		os_rwl_wlock(&mach->lock);
2086d65b43dSAaron LI 		if (!mach->present || mach->owner != owner) {
20942862644SAaron LI 			os_rwl_unlock(&mach->lock);
2106d65b43dSAaron LI 			continue;
2116d65b43dSAaron LI 		}
2126d65b43dSAaron LI 
2136d65b43dSAaron LI 		/* Kill it. */
2146d65b43dSAaron LI 		for (j = 0; j < NVMM_MAX_VCPUS; j++) {
2156d65b43dSAaron LI 			error = nvmm_vcpu_get(mach, j, &vcpu);
2166d65b43dSAaron LI 			if (error)
2176d65b43dSAaron LI 				continue;
2186d65b43dSAaron LI 			(*nvmm_impl->vcpu_destroy)(mach, vcpu);
2196d65b43dSAaron LI 			nvmm_vcpu_free(mach, vcpu);
2206d65b43dSAaron LI 			nvmm_vcpu_put(vcpu);
22142862644SAaron LI 			os_atomic_dec_uint(&mach->ncpus);
2226d65b43dSAaron LI 		}
2236d65b43dSAaron LI 		(*nvmm_impl->machine_destroy)(mach);
22442862644SAaron LI 		os_vmspace_destroy(mach->vm);
2256d65b43dSAaron LI 
226412bdc0aSAaron LI 		/* Drop the kernel vmobj refs. */
2276d65b43dSAaron LI 		for (j = 0; j < NVMM_MAX_HMAPPINGS; j++) {
2286d65b43dSAaron LI 			if (!mach->hmap[j].present)
2296d65b43dSAaron LI 				continue;
23042862644SAaron LI 			os_vmobj_rel(mach->hmap[j].vmobj);
2316d65b43dSAaron LI 		}
2326d65b43dSAaron LI 
2336d65b43dSAaron LI 		nvmm_machine_free(mach);
2346d65b43dSAaron LI 
23542862644SAaron LI 		os_rwl_unlock(&mach->lock);
2366d65b43dSAaron LI 	}
2376d65b43dSAaron LI }
2386d65b43dSAaron LI 
2396d65b43dSAaron LI /* -------------------------------------------------------------------------- */
2406d65b43dSAaron LI 
2416d65b43dSAaron LI static int
nvmm_capability(struct nvmm_owner * owner,struct nvmm_ioc_capability * args)2426d65b43dSAaron LI nvmm_capability(struct nvmm_owner *owner, struct nvmm_ioc_capability *args)
2436d65b43dSAaron LI {
2446d65b43dSAaron LI 	args->cap.version = NVMM_KERN_VERSION;
2456d65b43dSAaron LI 	args->cap.state_size = nvmm_impl->state_size;
246*cca384e4SAaron LI 	args->cap.comm_size = NVMM_COMM_PAGE_SIZE;
2476d65b43dSAaron LI 	args->cap.max_machines = NVMM_MAX_MACHINES;
2486d65b43dSAaron LI 	args->cap.max_vcpus = NVMM_MAX_VCPUS;
2496d65b43dSAaron LI 	args->cap.max_ram = NVMM_MAX_RAM;
2506d65b43dSAaron LI 
2516d65b43dSAaron LI 	(*nvmm_impl->capability)(&args->cap);
2526d65b43dSAaron LI 
2536d65b43dSAaron LI 	return 0;
2546d65b43dSAaron LI }
2556d65b43dSAaron LI 
2566d65b43dSAaron LI static int
nvmm_machine_create(struct nvmm_owner * owner,struct nvmm_ioc_machine_create * args)2576d65b43dSAaron LI nvmm_machine_create(struct nvmm_owner *owner,
2586d65b43dSAaron LI     struct nvmm_ioc_machine_create *args)
2596d65b43dSAaron LI {
2606d65b43dSAaron LI 	struct nvmm_machine *mach;
2616d65b43dSAaron LI 	int error;
2626d65b43dSAaron LI 
2636d65b43dSAaron LI 	error = nvmm_machine_alloc(&mach);
2646d65b43dSAaron LI 	if (error)
2656d65b43dSAaron LI 		return error;
2666d65b43dSAaron LI 
2676d65b43dSAaron LI 	/* Curproc owns the machine. */
2686d65b43dSAaron LI 	mach->owner = owner;
2696d65b43dSAaron LI 
2706d65b43dSAaron LI 	/* Zero out the host mappings. */
2716d65b43dSAaron LI 	memset(&mach->hmap, 0, sizeof(mach->hmap));
2726d65b43dSAaron LI 
2736d65b43dSAaron LI 	/* Create the machine vmspace. */
2746d65b43dSAaron LI 	mach->gpa_begin = 0;
2756d65b43dSAaron LI 	mach->gpa_end = NVMM_MAX_RAM;
27642862644SAaron LI 	mach->vm = os_vmspace_create(mach->gpa_begin, mach->gpa_end);
2779bbbdb7eSAaron LI 
278412bdc0aSAaron LI 	/* Create the comm vmobj. */
279d8a75061SAaron LI 	mach->commvmobj = os_vmobj_create(
280d8a75061SAaron LI 	    NVMM_MAX_VCPUS * NVMM_COMM_PAGE_SIZE);
2816d65b43dSAaron LI 
2826d65b43dSAaron LI 	(*nvmm_impl->machine_create)(mach);
2836d65b43dSAaron LI 
2846d65b43dSAaron LI 	args->machid = mach->machid;
2856d65b43dSAaron LI 	nvmm_machine_put(mach);
2866d65b43dSAaron LI 
2876d65b43dSAaron LI 	return 0;
2886d65b43dSAaron LI }
2896d65b43dSAaron LI 
2906d65b43dSAaron LI static int
nvmm_machine_destroy(struct nvmm_owner * owner,struct nvmm_ioc_machine_destroy * args)2916d65b43dSAaron LI nvmm_machine_destroy(struct nvmm_owner *owner,
2926d65b43dSAaron LI     struct nvmm_ioc_machine_destroy *args)
2936d65b43dSAaron LI {
2946d65b43dSAaron LI 	struct nvmm_machine *mach;
2956d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
2966d65b43dSAaron LI 	int error;
2976d65b43dSAaron LI 	size_t i;
2986d65b43dSAaron LI 
2996d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, true);
3006d65b43dSAaron LI 	if (error)
3016d65b43dSAaron LI 		return error;
3026d65b43dSAaron LI 
3036d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_VCPUS; i++) {
3046d65b43dSAaron LI 		error = nvmm_vcpu_get(mach, i, &vcpu);
3056d65b43dSAaron LI 		if (error)
3066d65b43dSAaron LI 			continue;
3076d65b43dSAaron LI 
3086d65b43dSAaron LI 		(*nvmm_impl->vcpu_destroy)(mach, vcpu);
3096d65b43dSAaron LI 		nvmm_vcpu_free(mach, vcpu);
3106d65b43dSAaron LI 		nvmm_vcpu_put(vcpu);
31142862644SAaron LI 		os_atomic_dec_uint(&mach->ncpus);
3126d65b43dSAaron LI 	}
3136d65b43dSAaron LI 
3146d65b43dSAaron LI 	(*nvmm_impl->machine_destroy)(mach);
3156d65b43dSAaron LI 
3166d65b43dSAaron LI 	/* Free the machine vmspace. */
31742862644SAaron LI 	os_vmspace_destroy(mach->vm);
3186d65b43dSAaron LI 
319412bdc0aSAaron LI 	/* Drop the kernel vmobj refs. */
3206d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
3216d65b43dSAaron LI 		if (!mach->hmap[i].present)
3226d65b43dSAaron LI 			continue;
32342862644SAaron LI 		os_vmobj_rel(mach->hmap[i].vmobj);
3246d65b43dSAaron LI 	}
3256d65b43dSAaron LI 
3266d65b43dSAaron LI 	nvmm_machine_free(mach);
3276d65b43dSAaron LI 	nvmm_machine_put(mach);
3286d65b43dSAaron LI 
3296d65b43dSAaron LI 	return 0;
3306d65b43dSAaron LI }
3316d65b43dSAaron LI 
3326d65b43dSAaron LI static int
nvmm_machine_configure(struct nvmm_owner * owner,struct nvmm_ioc_machine_configure * args)3336d65b43dSAaron LI nvmm_machine_configure(struct nvmm_owner *owner,
3346d65b43dSAaron LI     struct nvmm_ioc_machine_configure *args)
3356d65b43dSAaron LI {
3366d65b43dSAaron LI 	struct nvmm_machine *mach;
3376d65b43dSAaron LI 	size_t allocsz;
3386d65b43dSAaron LI 	uint64_t op;
3396d65b43dSAaron LI 	void *data;
3406d65b43dSAaron LI 	int error;
3416d65b43dSAaron LI 
3426d65b43dSAaron LI 	op = NVMM_MACH_CONF_MD(args->op);
3436d65b43dSAaron LI 	if (__predict_false(op >= nvmm_impl->mach_conf_max)) {
3446d65b43dSAaron LI 		return EINVAL;
3456d65b43dSAaron LI 	}
3466d65b43dSAaron LI 
3476d65b43dSAaron LI 	allocsz = nvmm_impl->mach_conf_sizes[op];
34842862644SAaron LI 	data = os_mem_alloc(allocsz);
3496d65b43dSAaron LI 
3506d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, true);
3516d65b43dSAaron LI 	if (error) {
35242862644SAaron LI 		os_mem_free(data, allocsz);
3536d65b43dSAaron LI 		return error;
3546d65b43dSAaron LI 	}
3556d65b43dSAaron LI 
3566d65b43dSAaron LI 	error = copyin(args->conf, data, allocsz);
3576d65b43dSAaron LI 	if (error) {
3586d65b43dSAaron LI 		goto out;
3596d65b43dSAaron LI 	}
3606d65b43dSAaron LI 
3616d65b43dSAaron LI 	error = (*nvmm_impl->machine_configure)(mach, op, data);
3626d65b43dSAaron LI 
3636d65b43dSAaron LI out:
3646d65b43dSAaron LI 	nvmm_machine_put(mach);
36542862644SAaron LI 	os_mem_free(data, allocsz);
3666d65b43dSAaron LI 	return error;
3676d65b43dSAaron LI }
3686d65b43dSAaron LI 
3696d65b43dSAaron LI static int
nvmm_vcpu_create(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_create * args)3706d65b43dSAaron LI nvmm_vcpu_create(struct nvmm_owner *owner, struct nvmm_ioc_vcpu_create *args)
3716d65b43dSAaron LI {
3726d65b43dSAaron LI 	struct nvmm_machine *mach;
3736d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
3746d65b43dSAaron LI 	int error;
3756d65b43dSAaron LI 
3766d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
3776d65b43dSAaron LI 	if (error)
3786d65b43dSAaron LI 		return error;
3796d65b43dSAaron LI 
3806d65b43dSAaron LI 	error = nvmm_vcpu_alloc(mach, args->cpuid, &vcpu);
3816d65b43dSAaron LI 	if (error)
3826d65b43dSAaron LI 		goto out;
3836d65b43dSAaron LI 
384412bdc0aSAaron LI 	/* Map the comm page on the kernel side, as wired. */
385d8a75061SAaron LI 	error = os_vmobj_map(os_kernel_map, (vaddr_t *)&vcpu->comm,
386d8a75061SAaron LI 	    NVMM_COMM_PAGE_SIZE, mach->commvmobj,
387d8a75061SAaron LI 	    args->cpuid * NVMM_COMM_PAGE_SIZE, true /* wired */,
38842862644SAaron LI 	    false /* !fixed */, true /* shared */, PROT_READ | PROT_WRITE,
38942862644SAaron LI 	    PROT_READ | PROT_WRITE);
3906d65b43dSAaron LI 	if (error) {
3916d65b43dSAaron LI 		nvmm_vcpu_free(mach, vcpu);
3926d65b43dSAaron LI 		nvmm_vcpu_put(vcpu);
3936d65b43dSAaron LI 		goto out;
3946d65b43dSAaron LI 	}
39542862644SAaron LI 
396d8a75061SAaron LI 	memset(vcpu->comm, 0, NVMM_COMM_PAGE_SIZE);
3976d65b43dSAaron LI 
3989aa070efSAaron LI 	/* Map the comm page on the user side, as pageable. */
399*cca384e4SAaron LI 	error = os_vmobj_map(os_curproc_map, (vaddr_t *)&args->comm,
400d8a75061SAaron LI 	    NVMM_COMM_PAGE_SIZE, mach->commvmobj,
401d8a75061SAaron LI 	    args->cpuid * NVMM_COMM_PAGE_SIZE, false /* !wired */,
40242862644SAaron LI 	    false /* !fixed */, true /* shared */, PROT_READ | PROT_WRITE,
40342862644SAaron LI 	    PROT_READ | PROT_WRITE);
4049aa070efSAaron LI 	if (error) {
4059aa070efSAaron LI 		nvmm_vcpu_free(mach, vcpu);
4069aa070efSAaron LI 		nvmm_vcpu_put(vcpu);
4079aa070efSAaron LI 		goto out;
4089aa070efSAaron LI 	}
4099aa070efSAaron LI 
4106d65b43dSAaron LI 	error = (*nvmm_impl->vcpu_create)(mach, vcpu);
4116d65b43dSAaron LI 	if (error) {
4126d65b43dSAaron LI 		nvmm_vcpu_free(mach, vcpu);
4136d65b43dSAaron LI 		nvmm_vcpu_put(vcpu);
4146d65b43dSAaron LI 		goto out;
4156d65b43dSAaron LI 	}
4166d65b43dSAaron LI 
4176d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
41842862644SAaron LI 	os_atomic_inc_uint(&mach->ncpus);
4196d65b43dSAaron LI 
4206d65b43dSAaron LI out:
4216d65b43dSAaron LI 	nvmm_machine_put(mach);
4226d65b43dSAaron LI 	return error;
4236d65b43dSAaron LI }
4246d65b43dSAaron LI 
4256d65b43dSAaron LI static int
nvmm_vcpu_destroy(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_destroy * args)4266d65b43dSAaron LI nvmm_vcpu_destroy(struct nvmm_owner *owner, struct nvmm_ioc_vcpu_destroy *args)
4276d65b43dSAaron LI {
4286d65b43dSAaron LI 	struct nvmm_machine *mach;
4296d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
4306d65b43dSAaron LI 	int error;
4316d65b43dSAaron LI 
4326d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
4336d65b43dSAaron LI 	if (error)
4346d65b43dSAaron LI 		return error;
4356d65b43dSAaron LI 
4366d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
4376d65b43dSAaron LI 	if (error)
4386d65b43dSAaron LI 		goto out;
4396d65b43dSAaron LI 
4406d65b43dSAaron LI 	(*nvmm_impl->vcpu_destroy)(mach, vcpu);
4416d65b43dSAaron LI 	nvmm_vcpu_free(mach, vcpu);
4426d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
44342862644SAaron LI 	os_atomic_dec_uint(&mach->ncpus);
4446d65b43dSAaron LI 
4456d65b43dSAaron LI out:
4466d65b43dSAaron LI 	nvmm_machine_put(mach);
4476d65b43dSAaron LI 	return error;
4486d65b43dSAaron LI }
4496d65b43dSAaron LI 
4506d65b43dSAaron LI static int
nvmm_vcpu_configure(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_configure * args)4516d65b43dSAaron LI nvmm_vcpu_configure(struct nvmm_owner *owner,
4526d65b43dSAaron LI     struct nvmm_ioc_vcpu_configure *args)
4536d65b43dSAaron LI {
4546d65b43dSAaron LI 	struct nvmm_machine *mach;
4556d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
4566d65b43dSAaron LI 	size_t allocsz;
4576d65b43dSAaron LI 	uint64_t op;
4586d65b43dSAaron LI 	void *data;
4596d65b43dSAaron LI 	int error;
4606d65b43dSAaron LI 
4616d65b43dSAaron LI 	op = NVMM_VCPU_CONF_MD(args->op);
4626d65b43dSAaron LI 	if (__predict_false(op >= nvmm_impl->vcpu_conf_max))
4636d65b43dSAaron LI 		return EINVAL;
4646d65b43dSAaron LI 
4656d65b43dSAaron LI 	allocsz = nvmm_impl->vcpu_conf_sizes[op];
46642862644SAaron LI 	data = os_mem_alloc(allocsz);
4676d65b43dSAaron LI 
4686d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
4696d65b43dSAaron LI 	if (error) {
47042862644SAaron LI 		os_mem_free(data, allocsz);
4716d65b43dSAaron LI 		return error;
4726d65b43dSAaron LI 	}
4736d65b43dSAaron LI 
4746d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
4756d65b43dSAaron LI 	if (error) {
4766d65b43dSAaron LI 		nvmm_machine_put(mach);
47742862644SAaron LI 		os_mem_free(data, allocsz);
4786d65b43dSAaron LI 		return error;
4796d65b43dSAaron LI 	}
4806d65b43dSAaron LI 
4816d65b43dSAaron LI 	error = copyin(args->conf, data, allocsz);
4826d65b43dSAaron LI 	if (error) {
4836d65b43dSAaron LI 		goto out;
4846d65b43dSAaron LI 	}
4856d65b43dSAaron LI 
4866d65b43dSAaron LI 	error = (*nvmm_impl->vcpu_configure)(vcpu, op, data);
4876d65b43dSAaron LI 
4886d65b43dSAaron LI out:
4896d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
4906d65b43dSAaron LI 	nvmm_machine_put(mach);
49142862644SAaron LI 	os_mem_free(data, allocsz);
4926d65b43dSAaron LI 	return error;
4936d65b43dSAaron LI }
4946d65b43dSAaron LI 
4956d65b43dSAaron LI static int
nvmm_vcpu_setstate(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_setstate * args)4966d65b43dSAaron LI nvmm_vcpu_setstate(struct nvmm_owner *owner,
4976d65b43dSAaron LI     struct nvmm_ioc_vcpu_setstate *args)
4986d65b43dSAaron LI {
4996d65b43dSAaron LI 	struct nvmm_machine *mach;
5006d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
5016d65b43dSAaron LI 	int error;
5026d65b43dSAaron LI 
5036d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
5046d65b43dSAaron LI 	if (error)
5056d65b43dSAaron LI 		return error;
5066d65b43dSAaron LI 
5076d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
5086d65b43dSAaron LI 	if (error)
5096d65b43dSAaron LI 		goto out;
5106d65b43dSAaron LI 
5116d65b43dSAaron LI 	(*nvmm_impl->vcpu_setstate)(vcpu);
5126d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
5136d65b43dSAaron LI 
5146d65b43dSAaron LI out:
5156d65b43dSAaron LI 	nvmm_machine_put(mach);
5166d65b43dSAaron LI 	return error;
5176d65b43dSAaron LI }
5186d65b43dSAaron LI 
5196d65b43dSAaron LI static int
nvmm_vcpu_getstate(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_getstate * args)5206d65b43dSAaron LI nvmm_vcpu_getstate(struct nvmm_owner *owner,
5216d65b43dSAaron LI     struct nvmm_ioc_vcpu_getstate *args)
5226d65b43dSAaron LI {
5236d65b43dSAaron LI 	struct nvmm_machine *mach;
5246d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
5256d65b43dSAaron LI 	int error;
5266d65b43dSAaron LI 
5276d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
5286d65b43dSAaron LI 	if (error)
5296d65b43dSAaron LI 		return error;
5306d65b43dSAaron LI 
5316d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
5326d65b43dSAaron LI 	if (error)
5336d65b43dSAaron LI 		goto out;
5346d65b43dSAaron LI 
5356d65b43dSAaron LI 	(*nvmm_impl->vcpu_getstate)(vcpu);
5366d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
5376d65b43dSAaron LI 
5386d65b43dSAaron LI out:
5396d65b43dSAaron LI 	nvmm_machine_put(mach);
5406d65b43dSAaron LI 	return error;
5416d65b43dSAaron LI }
5426d65b43dSAaron LI 
5436d65b43dSAaron LI static int
nvmm_vcpu_inject(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_inject * args)5446d65b43dSAaron LI nvmm_vcpu_inject(struct nvmm_owner *owner, struct nvmm_ioc_vcpu_inject *args)
5456d65b43dSAaron LI {
5466d65b43dSAaron LI 	struct nvmm_machine *mach;
5476d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
5486d65b43dSAaron LI 	int error;
5496d65b43dSAaron LI 
5506d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
5516d65b43dSAaron LI 	if (error)
5526d65b43dSAaron LI 		return error;
5536d65b43dSAaron LI 
5546d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
5556d65b43dSAaron LI 	if (error)
5566d65b43dSAaron LI 		goto out;
5576d65b43dSAaron LI 
5586d65b43dSAaron LI 	error = (*nvmm_impl->vcpu_inject)(vcpu);
5596d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
5606d65b43dSAaron LI 
5616d65b43dSAaron LI out:
5626d65b43dSAaron LI 	nvmm_machine_put(mach);
5636d65b43dSAaron LI 	return error;
5646d65b43dSAaron LI }
5656d65b43dSAaron LI 
5666d65b43dSAaron LI static int
nvmm_do_vcpu_run(struct nvmm_machine * mach,struct nvmm_cpu * vcpu,struct nvmm_vcpu_exit * exit)5676d65b43dSAaron LI nvmm_do_vcpu_run(struct nvmm_machine *mach, struct nvmm_cpu *vcpu,
5686d65b43dSAaron LI     struct nvmm_vcpu_exit *exit)
5696d65b43dSAaron LI {
5706d65b43dSAaron LI 	struct vmspace *vm = mach->vm;
5716d65b43dSAaron LI 	int ret;
5726d65b43dSAaron LI 
5736d65b43dSAaron LI 	while (1) {
5746d65b43dSAaron LI 		/* Got a signal? Or pending resched? Leave. */
57542862644SAaron LI 		if (__predict_false(os_return_needed())) {
5766d65b43dSAaron LI 			exit->reason = NVMM_VCPU_EXIT_NONE;
5776d65b43dSAaron LI 			return 0;
5786d65b43dSAaron LI 		}
5796d65b43dSAaron LI 
5806d65b43dSAaron LI 		/* Run the VCPU. */
5816d65b43dSAaron LI 		ret = (*nvmm_impl->vcpu_run)(mach, vcpu, exit);
5826d65b43dSAaron LI 		if (__predict_false(ret != 0)) {
5836d65b43dSAaron LI 			return ret;
5846d65b43dSAaron LI 		}
5856d65b43dSAaron LI 
5866d65b43dSAaron LI 		/* Process nested page faults. */
5876d65b43dSAaron LI 		if (__predict_true(exit->reason != NVMM_VCPU_EXIT_MEMORY)) {
5886d65b43dSAaron LI 			break;
5896d65b43dSAaron LI 		}
5906d65b43dSAaron LI 		if (exit->u.mem.gpa >= mach->gpa_end) {
5916d65b43dSAaron LI 			break;
5926d65b43dSAaron LI 		}
59342862644SAaron LI 		if (os_vmspace_fault(vm, exit->u.mem.gpa, exit->u.mem.prot)) {
5946d65b43dSAaron LI 			break;
5956d65b43dSAaron LI 		}
5966d65b43dSAaron LI 	}
5976d65b43dSAaron LI 
5986d65b43dSAaron LI 	return 0;
5996d65b43dSAaron LI }
6006d65b43dSAaron LI 
6016d65b43dSAaron LI static int
nvmm_vcpu_run(struct nvmm_owner * owner,struct nvmm_ioc_vcpu_run * args)6026d65b43dSAaron LI nvmm_vcpu_run(struct nvmm_owner *owner, struct nvmm_ioc_vcpu_run *args)
6036d65b43dSAaron LI {
6046d65b43dSAaron LI 	struct nvmm_machine *mach;
6056d65b43dSAaron LI 	struct nvmm_cpu *vcpu;
6066d65b43dSAaron LI 	int error;
6076d65b43dSAaron LI 
6086d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
6096d65b43dSAaron LI 	if (error)
6106d65b43dSAaron LI 		return error;
6116d65b43dSAaron LI 
6126d65b43dSAaron LI 	error = nvmm_vcpu_get(mach, args->cpuid, &vcpu);
6136d65b43dSAaron LI 	if (error)
6146d65b43dSAaron LI 		goto out;
6156d65b43dSAaron LI 
6166d65b43dSAaron LI 	error = nvmm_do_vcpu_run(mach, vcpu, &args->exit);
6176d65b43dSAaron LI 	nvmm_vcpu_put(vcpu);
6186d65b43dSAaron LI 
6196d65b43dSAaron LI out:
6206d65b43dSAaron LI 	nvmm_machine_put(mach);
6216d65b43dSAaron LI 	return error;
6226d65b43dSAaron LI }
6236d65b43dSAaron LI 
6246d65b43dSAaron LI /* -------------------------------------------------------------------------- */
6256d65b43dSAaron LI 
62642862644SAaron LI static os_vmobj_t *
nvmm_hmapping_getvmobj(struct nvmm_machine * mach,uintptr_t hva,size_t size,size_t * off)627412bdc0aSAaron LI nvmm_hmapping_getvmobj(struct nvmm_machine *mach, uintptr_t hva, size_t size,
6286d65b43dSAaron LI    size_t *off)
6296d65b43dSAaron LI {
6306d65b43dSAaron LI 	struct nvmm_hmapping *hmapping;
6316d65b43dSAaron LI 	size_t i;
6326d65b43dSAaron LI 
6336d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
6346d65b43dSAaron LI 		hmapping = &mach->hmap[i];
6356d65b43dSAaron LI 		if (!hmapping->present) {
6366d65b43dSAaron LI 			continue;
6376d65b43dSAaron LI 		}
6386d65b43dSAaron LI 		if (hva >= hmapping->hva &&
6396d65b43dSAaron LI 		    hva + size <= hmapping->hva + hmapping->size) {
6406d65b43dSAaron LI 			*off = hva - hmapping->hva;
641412bdc0aSAaron LI 			return hmapping->vmobj;
6426d65b43dSAaron LI 		}
6436d65b43dSAaron LI 	}
6446d65b43dSAaron LI 
6456d65b43dSAaron LI 	return NULL;
6466d65b43dSAaron LI }
6476d65b43dSAaron LI 
6486d65b43dSAaron LI static int
nvmm_hmapping_validate(struct nvmm_machine * mach,uintptr_t hva,size_t size)6496d65b43dSAaron LI nvmm_hmapping_validate(struct nvmm_machine *mach, uintptr_t hva, size_t size)
6506d65b43dSAaron LI {
6516d65b43dSAaron LI 	struct nvmm_hmapping *hmapping;
6526d65b43dSAaron LI 	size_t i;
65360ec4ed4SMatthew Dillon 	uintptr_t hva_end;
65460ec4ed4SMatthew Dillon 	uintptr_t hmap_end;
6556d65b43dSAaron LI 
6566d65b43dSAaron LI 	if ((hva % PAGE_SIZE) != 0 || (size % PAGE_SIZE) != 0) {
6576d65b43dSAaron LI 		return EINVAL;
6586d65b43dSAaron LI 	}
6596d65b43dSAaron LI 	if (hva == 0) {
6606d65b43dSAaron LI 		return EINVAL;
6616d65b43dSAaron LI 	}
6626d65b43dSAaron LI 
66360ec4ed4SMatthew Dillon 	/*
66460ec4ed4SMatthew Dillon 	 * Overflow tests MUST be done very carefully to avoid compiler
66560ec4ed4SMatthew Dillon 	 * optimizations from effectively deleting the test.
66660ec4ed4SMatthew Dillon 	 */
66760ec4ed4SMatthew Dillon 	hva_end = hva + size;
66860ec4ed4SMatthew Dillon 	if (hva_end <= hva)
66960ec4ed4SMatthew Dillon 		return EINVAL;
67060ec4ed4SMatthew Dillon 
67160ec4ed4SMatthew Dillon 	/*
67260ec4ed4SMatthew Dillon 	 * Overlap tests
67360ec4ed4SMatthew Dillon 	 */
6746d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
6756d65b43dSAaron LI 		hmapping = &mach->hmap[i];
67660ec4ed4SMatthew Dillon 
6776d65b43dSAaron LI 		if (!hmapping->present) {
6786d65b43dSAaron LI 			continue;
6796d65b43dSAaron LI 		}
68060ec4ed4SMatthew Dillon 		hmap_end = hmapping->hva + hmapping->size;
6816d65b43dSAaron LI 
68260ec4ed4SMatthew Dillon 		if (hva >= hmapping->hva && hva_end <= hmap_end)
6836d65b43dSAaron LI 			break;
68460ec4ed4SMatthew Dillon 		if (hva >= hmapping->hva && hva < hmap_end)
6856d65b43dSAaron LI 			return EEXIST;
68660ec4ed4SMatthew Dillon 		if (hva_end > hmapping->hva && hva_end <= hmap_end)
6876d65b43dSAaron LI 			return EEXIST;
68860ec4ed4SMatthew Dillon 		if (hva <= hmapping->hva && hva_end >= hmap_end)
6896d65b43dSAaron LI 			return EEXIST;
6906d65b43dSAaron LI 	}
6916d65b43dSAaron LI 
6926d65b43dSAaron LI 	return 0;
6936d65b43dSAaron LI }
6946d65b43dSAaron LI 
6956d65b43dSAaron LI static struct nvmm_hmapping *
nvmm_hmapping_alloc(struct nvmm_machine * mach)6966d65b43dSAaron LI nvmm_hmapping_alloc(struct nvmm_machine *mach)
6976d65b43dSAaron LI {
6986d65b43dSAaron LI 	struct nvmm_hmapping *hmapping;
6996d65b43dSAaron LI 	size_t i;
7006d65b43dSAaron LI 
7016d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
7026d65b43dSAaron LI 		hmapping = &mach->hmap[i];
7036d65b43dSAaron LI 		if (!hmapping->present) {
7046d65b43dSAaron LI 			hmapping->present = true;
7056d65b43dSAaron LI 			return hmapping;
7066d65b43dSAaron LI 		}
7076d65b43dSAaron LI 	}
7086d65b43dSAaron LI 
7096d65b43dSAaron LI 	return NULL;
7106d65b43dSAaron LI }
7116d65b43dSAaron LI 
7126d65b43dSAaron LI static int
nvmm_hmapping_free(struct nvmm_machine * mach,uintptr_t hva,size_t size)7136d65b43dSAaron LI nvmm_hmapping_free(struct nvmm_machine *mach, uintptr_t hva, size_t size)
7146d65b43dSAaron LI {
7156d65b43dSAaron LI 	struct nvmm_hmapping *hmapping;
7166d65b43dSAaron LI 	size_t i;
7176d65b43dSAaron LI 
7186d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
7196d65b43dSAaron LI 		hmapping = &mach->hmap[i];
7206d65b43dSAaron LI 		if (!hmapping->present || hmapping->hva != hva ||
7216d65b43dSAaron LI 		    hmapping->size != size) {
7226d65b43dSAaron LI 			continue;
7236d65b43dSAaron LI 		}
7246d65b43dSAaron LI 
72542862644SAaron LI 		os_vmobj_unmap(os_curproc_map, hmapping->hva,
72642862644SAaron LI 		    hmapping->hva + hmapping->size, false);
72742862644SAaron LI 		os_vmobj_rel(hmapping->vmobj);
7286d65b43dSAaron LI 
729412bdc0aSAaron LI 		hmapping->vmobj = NULL;
7306d65b43dSAaron LI 		hmapping->present = false;
7316d65b43dSAaron LI 
7326d65b43dSAaron LI 		return 0;
7336d65b43dSAaron LI 	}
7346d65b43dSAaron LI 
7356d65b43dSAaron LI 	return ENOENT;
7366d65b43dSAaron LI }
7376d65b43dSAaron LI 
7386d65b43dSAaron LI static int
nvmm_hva_map(struct nvmm_owner * owner,struct nvmm_ioc_hva_map * args)7396d65b43dSAaron LI nvmm_hva_map(struct nvmm_owner *owner, struct nvmm_ioc_hva_map *args)
7406d65b43dSAaron LI {
7416d65b43dSAaron LI 	struct nvmm_machine *mach;
7426d65b43dSAaron LI 	struct nvmm_hmapping *hmapping;
7436d65b43dSAaron LI 	vaddr_t uva;
7446d65b43dSAaron LI 	int error;
7456d65b43dSAaron LI 
7466d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, true);
7476d65b43dSAaron LI 	if (error)
7486d65b43dSAaron LI 		return error;
7496d65b43dSAaron LI 
7506d65b43dSAaron LI 	error = nvmm_hmapping_validate(mach, args->hva, args->size);
7516d65b43dSAaron LI 	if (error)
7526d65b43dSAaron LI 		goto out;
7536d65b43dSAaron LI 
7546d65b43dSAaron LI 	hmapping = nvmm_hmapping_alloc(mach);
7556d65b43dSAaron LI 	if (hmapping == NULL) {
7566d65b43dSAaron LI 		error = ENOBUFS;
7576d65b43dSAaron LI 		goto out;
7586d65b43dSAaron LI 	}
7596d65b43dSAaron LI 
7606d65b43dSAaron LI 	hmapping->hva = args->hva;
7616d65b43dSAaron LI 	hmapping->size = args->size;
76242862644SAaron LI 	hmapping->vmobj = os_vmobj_create(hmapping->size);
7636d65b43dSAaron LI 	uva = hmapping->hva;
7646d65b43dSAaron LI 
765412bdc0aSAaron LI 	/* Map the vmobj into the user address space, as pageable. */
76642862644SAaron LI 	error = os_vmobj_map(os_curproc_map, &uva, hmapping->size,
76742862644SAaron LI 	    hmapping->vmobj, 0, false /* !wired */, true /* fixed */,
76842862644SAaron LI 	    true /* shared */, PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE);
7696d65b43dSAaron LI 
7706d65b43dSAaron LI out:
7716d65b43dSAaron LI 	nvmm_machine_put(mach);
7726d65b43dSAaron LI 	return error;
7736d65b43dSAaron LI }
7746d65b43dSAaron LI 
7756d65b43dSAaron LI static int
nvmm_hva_unmap(struct nvmm_owner * owner,struct nvmm_ioc_hva_unmap * args)7766d65b43dSAaron LI nvmm_hva_unmap(struct nvmm_owner *owner, struct nvmm_ioc_hva_unmap *args)
7776d65b43dSAaron LI {
7786d65b43dSAaron LI 	struct nvmm_machine *mach;
7796d65b43dSAaron LI 	int error;
7806d65b43dSAaron LI 
7816d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, true);
7826d65b43dSAaron LI 	if (error)
7836d65b43dSAaron LI 		return error;
7846d65b43dSAaron LI 
7856d65b43dSAaron LI 	error = nvmm_hmapping_free(mach, args->hva, args->size);
7866d65b43dSAaron LI 
7876d65b43dSAaron LI 	nvmm_machine_put(mach);
7886d65b43dSAaron LI 	return error;
7896d65b43dSAaron LI }
7906d65b43dSAaron LI 
7916d65b43dSAaron LI /* -------------------------------------------------------------------------- */
7926d65b43dSAaron LI 
7936d65b43dSAaron LI static int
nvmm_gpa_map(struct nvmm_owner * owner,struct nvmm_ioc_gpa_map * args)7946d65b43dSAaron LI nvmm_gpa_map(struct nvmm_owner *owner, struct nvmm_ioc_gpa_map *args)
7956d65b43dSAaron LI {
7966d65b43dSAaron LI 	struct nvmm_machine *mach;
79742862644SAaron LI 	os_vmobj_t *vmobj;
7986d65b43dSAaron LI 	gpaddr_t gpa;
79960ec4ed4SMatthew Dillon 	gpaddr_t gpa_end;
8006d65b43dSAaron LI 	size_t off;
8016d65b43dSAaron LI 	int error;
8026d65b43dSAaron LI 
8036d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
8046d65b43dSAaron LI 	if (error)
8056d65b43dSAaron LI 		return error;
8066d65b43dSAaron LI 
8076d65b43dSAaron LI 	if ((args->prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC)) != 0) {
8086d65b43dSAaron LI 		error = EINVAL;
8096d65b43dSAaron LI 		goto out;
8106d65b43dSAaron LI 	}
8116d65b43dSAaron LI 
81260ec4ed4SMatthew Dillon 	/*
81360ec4ed4SMatthew Dillon 	 * Overflow tests MUST be done very carefully to avoid compiler
81460ec4ed4SMatthew Dillon 	 * optimizations from effectively deleting the test.
81560ec4ed4SMatthew Dillon 	 */
81660ec4ed4SMatthew Dillon 	gpa = args->gpa;
81760ec4ed4SMatthew Dillon 	gpa_end = gpa + args->size;
81860ec4ed4SMatthew Dillon 	if (gpa_end <= gpa) {
81960ec4ed4SMatthew Dillon 		error = EINVAL;
82060ec4ed4SMatthew Dillon 		goto out;
82160ec4ed4SMatthew Dillon 	}
82260ec4ed4SMatthew Dillon 
82360ec4ed4SMatthew Dillon 	if ((gpa % PAGE_SIZE) != 0 || (args->size % PAGE_SIZE) != 0 ||
8246d65b43dSAaron LI 	    (args->hva % PAGE_SIZE) != 0) {
8256d65b43dSAaron LI 		error = EINVAL;
8266d65b43dSAaron LI 		goto out;
8276d65b43dSAaron LI 	}
8286d65b43dSAaron LI 	if (args->hva == 0) {
8296d65b43dSAaron LI 		error = EINVAL;
8306d65b43dSAaron LI 		goto out;
8316d65b43dSAaron LI 	}
83260ec4ed4SMatthew Dillon 
83360ec4ed4SMatthew Dillon 	if (gpa < mach->gpa_begin || gpa >= mach->gpa_end) {
8346d65b43dSAaron LI 		error = EINVAL;
8356d65b43dSAaron LI 		goto out;
8366d65b43dSAaron LI 	}
83760ec4ed4SMatthew Dillon 	if (gpa_end  > mach->gpa_end) {
8386d65b43dSAaron LI 		error = EINVAL;
8396d65b43dSAaron LI 		goto out;
8406d65b43dSAaron LI 	}
8416d65b43dSAaron LI 
842412bdc0aSAaron LI 	vmobj = nvmm_hmapping_getvmobj(mach, args->hva, args->size, &off);
843412bdc0aSAaron LI 	if (vmobj == NULL) {
8446d65b43dSAaron LI 		error = EINVAL;
8456d65b43dSAaron LI 		goto out;
8466d65b43dSAaron LI 	}
8476d65b43dSAaron LI 
848412bdc0aSAaron LI 	/* Map the vmobj into the machine address space, as pageable. */
84942862644SAaron LI 	error = os_vmobj_map(&mach->vm->vm_map, &gpa, args->size, vmobj, off,
85042862644SAaron LI 	    false /* !wired */, true /* fixed */, false /* !shared */,
85142862644SAaron LI 	    args->prot, PROT_READ | PROT_WRITE | PROT_EXEC);
8526d65b43dSAaron LI 
8536d65b43dSAaron LI out:
8546d65b43dSAaron LI 	nvmm_machine_put(mach);
8556d65b43dSAaron LI 	return error;
8566d65b43dSAaron LI }
8576d65b43dSAaron LI 
8586d65b43dSAaron LI static int
nvmm_gpa_unmap(struct nvmm_owner * owner,struct nvmm_ioc_gpa_unmap * args)8596d65b43dSAaron LI nvmm_gpa_unmap(struct nvmm_owner *owner, struct nvmm_ioc_gpa_unmap *args)
8606d65b43dSAaron LI {
8616d65b43dSAaron LI 	struct nvmm_machine *mach;
8626d65b43dSAaron LI 	gpaddr_t gpa;
86360ec4ed4SMatthew Dillon 	gpaddr_t gpa_end;
8646d65b43dSAaron LI 	int error;
8656d65b43dSAaron LI 
8666d65b43dSAaron LI 	error = nvmm_machine_get(owner, args->machid, &mach, false);
8676d65b43dSAaron LI 	if (error)
8686d65b43dSAaron LI 		return error;
8696d65b43dSAaron LI 
87060ec4ed4SMatthew Dillon 	/*
87160ec4ed4SMatthew Dillon 	 * Overflow tests MUST be done very carefully to avoid compiler
87260ec4ed4SMatthew Dillon 	 * optimizations from effectively deleting the test.
87360ec4ed4SMatthew Dillon 	 */
8746d65b43dSAaron LI 	gpa = args->gpa;
87560ec4ed4SMatthew Dillon 	gpa_end = gpa + args->size;
87660ec4ed4SMatthew Dillon 	if (gpa_end <= gpa) {
87760ec4ed4SMatthew Dillon 		error = EINVAL;
87860ec4ed4SMatthew Dillon 		goto out;
87960ec4ed4SMatthew Dillon 	}
88060ec4ed4SMatthew Dillon 
88160ec4ed4SMatthew Dillon 	if ((gpa % PAGE_SIZE) != 0 || (args->size % PAGE_SIZE) != 0) {
88260ec4ed4SMatthew Dillon 		error = EINVAL;
88360ec4ed4SMatthew Dillon 		goto out;
88460ec4ed4SMatthew Dillon 	}
88560ec4ed4SMatthew Dillon 	if (gpa < mach->gpa_begin || gpa >= mach->gpa_end) {
88660ec4ed4SMatthew Dillon 		error = EINVAL;
88760ec4ed4SMatthew Dillon 		goto out;
88860ec4ed4SMatthew Dillon 	}
88960ec4ed4SMatthew Dillon 	if (gpa_end >= mach->gpa_end) {
89060ec4ed4SMatthew Dillon 		error = EINVAL;
89160ec4ed4SMatthew Dillon 		goto out;
89260ec4ed4SMatthew Dillon 	}
8936d65b43dSAaron LI 
8946d65b43dSAaron LI 	/* Unmap the memory from the machine. */
89542862644SAaron LI 	os_vmobj_unmap(&mach->vm->vm_map, gpa, gpa + args->size, false);
8966d65b43dSAaron LI 
8976d65b43dSAaron LI out:
8986d65b43dSAaron LI 	nvmm_machine_put(mach);
8996d65b43dSAaron LI 	return error;
9006d65b43dSAaron LI }
9016d65b43dSAaron LI 
9026d65b43dSAaron LI /* -------------------------------------------------------------------------- */
9036d65b43dSAaron LI 
9046d65b43dSAaron LI static int
nvmm_ctl_mach_info(struct nvmm_owner * owner,struct nvmm_ioc_ctl * args)9056d65b43dSAaron LI nvmm_ctl_mach_info(struct nvmm_owner *owner, struct nvmm_ioc_ctl *args)
9066d65b43dSAaron LI {
9076d65b43dSAaron LI 	struct nvmm_ctl_mach_info ctl;
9086d65b43dSAaron LI 	struct nvmm_machine *mach;
9096d65b43dSAaron LI 	int error;
9106d65b43dSAaron LI 	size_t i;
9116d65b43dSAaron LI 
9126d65b43dSAaron LI 	if (args->size != sizeof(ctl))
9136d65b43dSAaron LI 		return EINVAL;
9146d65b43dSAaron LI 	error = copyin(args->data, &ctl, sizeof(ctl));
9156d65b43dSAaron LI 	if (error)
9166d65b43dSAaron LI 		return error;
9176d65b43dSAaron LI 
9186d65b43dSAaron LI 	error = nvmm_machine_get(owner, ctl.machid, &mach, true);
9196d65b43dSAaron LI 	if (error)
9206d65b43dSAaron LI 		return error;
9216d65b43dSAaron LI 
9226d65b43dSAaron LI 	ctl.nvcpus = mach->ncpus;
9236d65b43dSAaron LI 
9246d65b43dSAaron LI 	ctl.nram = 0;
9256d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_HMAPPINGS; i++) {
9266d65b43dSAaron LI 		if (!mach->hmap[i].present)
9276d65b43dSAaron LI 			continue;
9286d65b43dSAaron LI 		ctl.nram += mach->hmap[i].size;
9296d65b43dSAaron LI 	}
9306d65b43dSAaron LI 
9316d65b43dSAaron LI 	ctl.pid = mach->owner->pid;
9326d65b43dSAaron LI 	ctl.time = mach->time;
9336d65b43dSAaron LI 
9346d65b43dSAaron LI 	nvmm_machine_put(mach);
9356d65b43dSAaron LI 
9366d65b43dSAaron LI 	error = copyout(&ctl, args->data, sizeof(ctl));
9376d65b43dSAaron LI 	if (error)
9386d65b43dSAaron LI 		return error;
9396d65b43dSAaron LI 
9406d65b43dSAaron LI 	return 0;
9416d65b43dSAaron LI }
9426d65b43dSAaron LI 
9436d65b43dSAaron LI static int
nvmm_ctl(struct nvmm_owner * owner,struct nvmm_ioc_ctl * args)9446d65b43dSAaron LI nvmm_ctl(struct nvmm_owner *owner, struct nvmm_ioc_ctl *args)
9456d65b43dSAaron LI {
9466d65b43dSAaron LI 	switch (args->op) {
9476d65b43dSAaron LI 	case NVMM_CTL_MACH_INFO:
9486d65b43dSAaron LI 		return nvmm_ctl_mach_info(owner, args);
9496d65b43dSAaron LI 	default:
9506d65b43dSAaron LI 		return EINVAL;
9516d65b43dSAaron LI 	}
9526d65b43dSAaron LI }
9536d65b43dSAaron LI 
9546d65b43dSAaron LI /* -------------------------------------------------------------------------- */
9556d65b43dSAaron LI 
956002185e5SAaron LI const struct nvmm_impl *
nvmm_ident(void)9576d65b43dSAaron LI nvmm_ident(void)
9586d65b43dSAaron LI {
9596d65b43dSAaron LI 	size_t i;
9606d65b43dSAaron LI 
9616d65b43dSAaron LI 	for (i = 0; i < __arraycount(nvmm_impl_list); i++) {
9626d65b43dSAaron LI 		if ((*nvmm_impl_list[i]->ident)())
9636d65b43dSAaron LI 			return nvmm_impl_list[i];
9646d65b43dSAaron LI 	}
9656d65b43dSAaron LI 
9666d65b43dSAaron LI 	return NULL;
9676d65b43dSAaron LI }
9686d65b43dSAaron LI 
969002185e5SAaron LI int
nvmm_init(void)9706d65b43dSAaron LI nvmm_init(void)
9716d65b43dSAaron LI {
9726d65b43dSAaron LI 	size_t i, n;
9736d65b43dSAaron LI 
9746d65b43dSAaron LI 	nvmm_impl = nvmm_ident();
9756d65b43dSAaron LI 	if (nvmm_impl == NULL)
9766d65b43dSAaron LI 		return ENOTSUP;
9776d65b43dSAaron LI 
9786d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_MACHINES; i++) {
9796d65b43dSAaron LI 		machines[i].machid = i;
98042862644SAaron LI 		os_rwl_init(&machines[i].lock);
9816d65b43dSAaron LI 		for (n = 0; n < NVMM_MAX_VCPUS; n++) {
9826d65b43dSAaron LI 			machines[i].cpus[n].present = false;
9836d65b43dSAaron LI 			machines[i].cpus[n].cpuid = n;
98442862644SAaron LI 			os_mtx_init(&machines[i].cpus[n].lock);
9856d65b43dSAaron LI 		}
9866d65b43dSAaron LI 	}
9876d65b43dSAaron LI 
9886d65b43dSAaron LI 	(*nvmm_impl->init)();
9896d65b43dSAaron LI 
9906d65b43dSAaron LI 	return 0;
9916d65b43dSAaron LI }
9926d65b43dSAaron LI 
993002185e5SAaron LI void
nvmm_fini(void)9946d65b43dSAaron LI nvmm_fini(void)
9956d65b43dSAaron LI {
9966d65b43dSAaron LI 	size_t i, n;
9976d65b43dSAaron LI 
9986d65b43dSAaron LI 	for (i = 0; i < NVMM_MAX_MACHINES; i++) {
99942862644SAaron LI 		os_rwl_destroy(&machines[i].lock);
10006d65b43dSAaron LI 		for (n = 0; n < NVMM_MAX_VCPUS; n++) {
100142862644SAaron LI 			os_mtx_destroy(&machines[i].cpus[n].lock);
10026d65b43dSAaron LI 		}
10036d65b43dSAaron LI 	}
10046d65b43dSAaron LI 
10056d65b43dSAaron LI 	(*nvmm_impl->fini)();
10066d65b43dSAaron LI 	nvmm_impl = NULL;
10076d65b43dSAaron LI }
10086d65b43dSAaron LI 
10096d65b43dSAaron LI /* -------------------------------------------------------------------------- */
10106d65b43dSAaron LI 
1011002185e5SAaron LI int
nvmm_ioctl(struct nvmm_owner * owner,unsigned long cmd,void * data)101242862644SAaron LI nvmm_ioctl(struct nvmm_owner *owner, unsigned long cmd, void *data)
10136d65b43dSAaron LI {
10146d65b43dSAaron LI 	switch (cmd) {
10156d65b43dSAaron LI 	case NVMM_IOC_CAPABILITY:
10166d65b43dSAaron LI 		return nvmm_capability(owner, data);
10176d65b43dSAaron LI 	case NVMM_IOC_MACHINE_CREATE:
10186d65b43dSAaron LI 		return nvmm_machine_create(owner, data);
10196d65b43dSAaron LI 	case NVMM_IOC_MACHINE_DESTROY:
10206d65b43dSAaron LI 		return nvmm_machine_destroy(owner, data);
10216d65b43dSAaron LI 	case NVMM_IOC_MACHINE_CONFIGURE:
10226d65b43dSAaron LI 		return nvmm_machine_configure(owner, data);
10236d65b43dSAaron LI 	case NVMM_IOC_VCPU_CREATE:
10246d65b43dSAaron LI 		return nvmm_vcpu_create(owner, data);
10256d65b43dSAaron LI 	case NVMM_IOC_VCPU_DESTROY:
10266d65b43dSAaron LI 		return nvmm_vcpu_destroy(owner, data);
10276d65b43dSAaron LI 	case NVMM_IOC_VCPU_CONFIGURE:
10286d65b43dSAaron LI 		return nvmm_vcpu_configure(owner, data);
10296d65b43dSAaron LI 	case NVMM_IOC_VCPU_SETSTATE:
10306d65b43dSAaron LI 		return nvmm_vcpu_setstate(owner, data);
10316d65b43dSAaron LI 	case NVMM_IOC_VCPU_GETSTATE:
10326d65b43dSAaron LI 		return nvmm_vcpu_getstate(owner, data);
10336d65b43dSAaron LI 	case NVMM_IOC_VCPU_INJECT:
10346d65b43dSAaron LI 		return nvmm_vcpu_inject(owner, data);
10356d65b43dSAaron LI 	case NVMM_IOC_VCPU_RUN:
10366d65b43dSAaron LI 		return nvmm_vcpu_run(owner, data);
10376d65b43dSAaron LI 	case NVMM_IOC_GPA_MAP:
10386d65b43dSAaron LI 		return nvmm_gpa_map(owner, data);
10396d65b43dSAaron LI 	case NVMM_IOC_GPA_UNMAP:
10406d65b43dSAaron LI 		return nvmm_gpa_unmap(owner, data);
10416d65b43dSAaron LI 	case NVMM_IOC_HVA_MAP:
10426d65b43dSAaron LI 		return nvmm_hva_map(owner, data);
10436d65b43dSAaron LI 	case NVMM_IOC_HVA_UNMAP:
10446d65b43dSAaron LI 		return nvmm_hva_unmap(owner, data);
10456d65b43dSAaron LI 	case NVMM_IOC_CTL:
10466d65b43dSAaron LI 		return nvmm_ctl(owner, data);
10476d65b43dSAaron LI 	default:
10486d65b43dSAaron LI 		return EINVAL;
10496d65b43dSAaron LI 	}
10506d65b43dSAaron LI }
1051