vmm.c revision 246188
1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD: head/sys/amd64/vmm/vmm.c 246188 2013-02-01 01:16:26Z neel $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/sys/amd64/vmm/vmm.c 246188 2013-02-01 01:16:26Z neel $");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33234695Sgrehan#include <sys/systm.h>
34221828Sgrehan#include <sys/kernel.h>
35221828Sgrehan#include <sys/module.h>
36221828Sgrehan#include <sys/sysctl.h>
37221828Sgrehan#include <sys/malloc.h>
38221828Sgrehan#include <sys/pcpu.h>
39221828Sgrehan#include <sys/lock.h>
40221828Sgrehan#include <sys/mutex.h>
41221828Sgrehan#include <sys/proc.h>
42221828Sgrehan#include <sys/sched.h>
43221828Sgrehan#include <sys/smp.h>
44221828Sgrehan#include <sys/systm.h>
45221828Sgrehan
46221828Sgrehan#include <vm/vm.h>
47221828Sgrehan
48221828Sgrehan#include <machine/vm.h>
49221828Sgrehan#include <machine/pcb.h>
50241489Sneel#include <machine/smp.h>
51221914Sjhb#include <x86/apicreg.h>
52221828Sgrehan
53221828Sgrehan#include <machine/vmm.h>
54242275Sneel#include "vmm_host.h"
55221828Sgrehan#include "vmm_mem.h"
56221828Sgrehan#include "vmm_util.h"
57221828Sgrehan#include <machine/vmm_dev.h>
58221828Sgrehan#include "vlapic.h"
59221828Sgrehan#include "vmm_msr.h"
60221828Sgrehan#include "vmm_ipi.h"
61221828Sgrehan#include "vmm_stat.h"
62242065Sneel#include "vmm_lapic.h"
63221828Sgrehan
64221828Sgrehan#include "io/ppt.h"
65221828Sgrehan#include "io/iommu.h"
66221828Sgrehan
67221828Sgrehanstruct vlapic;
68221828Sgrehan
69221828Sgrehanstruct vcpu {
70221828Sgrehan	int		flags;
71241489Sneel	enum vcpu_state	state;
72241489Sneel	struct mtx	mtx;
73221828Sgrehan	int		pincpu;		/* host cpuid this vcpu is bound to */
74221828Sgrehan	int		hostcpu;	/* host cpuid this vcpu last ran on */
75221828Sgrehan	uint64_t	guest_msrs[VMM_MSR_NUM];
76221828Sgrehan	struct vlapic	*vlapic;
77221828Sgrehan	int		 vcpuid;
78234695Sgrehan	struct savefpu	*guestfpu;	/* guest fpu state */
79221828Sgrehan	void		*stats;
80240894Sneel	struct vm_exit	exitinfo;
81240922Sneel	enum x2apic_state x2apic_state;
82241982Sneel	int		nmi_pending;
83221828Sgrehan};
84221828Sgrehan#define	VCPU_F_PINNED	0x0001
85221828Sgrehan
86221828Sgrehan#define	VCPU_PINCPU(vm, vcpuid)	\
87221828Sgrehan    ((vm->vcpu[vcpuid].flags & VCPU_F_PINNED) ? vm->vcpu[vcpuid].pincpu : -1)
88221828Sgrehan
89221828Sgrehan#define	VCPU_UNPIN(vm, vcpuid)	(vm->vcpu[vcpuid].flags &= ~VCPU_F_PINNED)
90221828Sgrehan
91221828Sgrehan#define	VCPU_PIN(vm, vcpuid, host_cpuid)				\
92221828Sgrehando {									\
93221828Sgrehan	vm->vcpu[vcpuid].flags |= VCPU_F_PINNED;			\
94221828Sgrehan	vm->vcpu[vcpuid].pincpu = host_cpuid;				\
95221828Sgrehan} while(0)
96221828Sgrehan
97242065Sneel#define	vcpu_lock_init(v)	mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN)
98242065Sneel#define	vcpu_lock(v)		mtx_lock_spin(&((v)->mtx))
99242065Sneel#define	vcpu_unlock(v)		mtx_unlock_spin(&((v)->mtx))
100241489Sneel
101221828Sgrehan#define	VM_MAX_MEMORY_SEGMENTS	2
102221828Sgrehan
103221828Sgrehanstruct vm {
104221828Sgrehan	void		*cookie;	/* processor-specific data */
105221828Sgrehan	void		*iommu;		/* iommu-specific data */
106221828Sgrehan	struct vcpu	vcpu[VM_MAXCPU];
107221828Sgrehan	int		num_mem_segs;
108221828Sgrehan	struct vm_memory_segment mem_segs[VM_MAX_MEMORY_SEGMENTS];
109221828Sgrehan	char		name[VM_MAX_NAMELEN];
110221828Sgrehan
111221828Sgrehan	/*
112223621Sgrehan	 * Set of active vcpus.
113221828Sgrehan	 * An active vcpu is one that has been started implicitly (BSP) or
114221828Sgrehan	 * explicitly (AP) by sending it a startup ipi.
115221828Sgrehan	 */
116223621Sgrehan	cpuset_t	active_cpus;
117221828Sgrehan};
118221828Sgrehan
119221828Sgrehanstatic struct vmm_ops *ops;
120221828Sgrehan#define	VMM_INIT()	(ops != NULL ? (*ops->init)() : 0)
121221828Sgrehan#define	VMM_CLEANUP()	(ops != NULL ? (*ops->cleanup)() : 0)
122221828Sgrehan
123221828Sgrehan#define	VMINIT(vm)	(ops != NULL ? (*ops->vminit)(vm): NULL)
124240894Sneel#define	VMRUN(vmi, vcpu, rip) \
125240894Sneel	(ops != NULL ? (*ops->vmrun)(vmi, vcpu, rip) : ENXIO)
126221828Sgrehan#define	VMCLEANUP(vmi)	(ops != NULL ? (*ops->vmcleanup)(vmi) : NULL)
127241147Sneel#define	VMMMAP_SET(vmi, gpa, hpa, len, attr, prot, spm)			\
128241147Sneel    	(ops != NULL ? 							\
129241147Sneel    	(*ops->vmmmap_set)(vmi, gpa, hpa, len, attr, prot, spm) :	\
130241147Sneel	ENXIO)
131241147Sneel#define	VMMMAP_GET(vmi, gpa) \
132241147Sneel	(ops != NULL ? (*ops->vmmmap_get)(vmi, gpa) : ENXIO)
133221828Sgrehan#define	VMGETREG(vmi, vcpu, num, retval)		\
134221828Sgrehan	(ops != NULL ? (*ops->vmgetreg)(vmi, vcpu, num, retval) : ENXIO)
135221828Sgrehan#define	VMSETREG(vmi, vcpu, num, val)		\
136221828Sgrehan	(ops != NULL ? (*ops->vmsetreg)(vmi, vcpu, num, val) : ENXIO)
137221828Sgrehan#define	VMGETDESC(vmi, vcpu, num, desc)		\
138221828Sgrehan	(ops != NULL ? (*ops->vmgetdesc)(vmi, vcpu, num, desc) : ENXIO)
139221828Sgrehan#define	VMSETDESC(vmi, vcpu, num, desc)		\
140221828Sgrehan	(ops != NULL ? (*ops->vmsetdesc)(vmi, vcpu, num, desc) : ENXIO)
141221828Sgrehan#define	VMINJECT(vmi, vcpu, type, vec, ec, ecv)	\
142221828Sgrehan	(ops != NULL ? (*ops->vminject)(vmi, vcpu, type, vec, ec, ecv) : ENXIO)
143221828Sgrehan#define	VMGETCAP(vmi, vcpu, num, retval)	\
144221828Sgrehan	(ops != NULL ? (*ops->vmgetcap)(vmi, vcpu, num, retval) : ENXIO)
145221828Sgrehan#define	VMSETCAP(vmi, vcpu, num, val)		\
146221828Sgrehan	(ops != NULL ? (*ops->vmsetcap)(vmi, vcpu, num, val) : ENXIO)
147221828Sgrehan
148245021Sneel#define	fpu_start_emulating()	load_cr0(rcr0() | CR0_TS)
149245021Sneel#define	fpu_stop_emulating()	clts()
150221828Sgrehan
151221828Sgrehanstatic MALLOC_DEFINE(M_VM, "vm", "vm");
152221828SgrehanCTASSERT(VMM_MSR_NUM <= 64);	/* msr_mask can keep track of up to 64 msrs */
153221828Sgrehan
154221828Sgrehan/* statistics */
155221828Sgrehanstatic VMM_STAT_DEFINE(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
156221828Sgrehan
157221828Sgrehanstatic void
158221828Sgrehanvcpu_cleanup(struct vcpu *vcpu)
159221828Sgrehan{
160221828Sgrehan	vlapic_cleanup(vcpu->vlapic);
161234695Sgrehan	vmm_stat_free(vcpu->stats);
162234695Sgrehan	fpu_save_area_free(vcpu->guestfpu);
163221828Sgrehan}
164221828Sgrehan
165221828Sgrehanstatic void
166221828Sgrehanvcpu_init(struct vm *vm, uint32_t vcpu_id)
167221828Sgrehan{
168221828Sgrehan	struct vcpu *vcpu;
169221828Sgrehan
170221828Sgrehan	vcpu = &vm->vcpu[vcpu_id];
171221828Sgrehan
172241489Sneel	vcpu_lock_init(vcpu);
173241489Sneel	vcpu->hostcpu = NOCPU;
174221828Sgrehan	vcpu->vcpuid = vcpu_id;
175221828Sgrehan	vcpu->vlapic = vlapic_init(vm, vcpu_id);
176240943Sneel	vm_set_x2apic_state(vm, vcpu_id, X2APIC_ENABLED);
177234695Sgrehan	vcpu->guestfpu = fpu_save_area_alloc();
178234695Sgrehan	fpu_save_area_reset(vcpu->guestfpu);
179221828Sgrehan	vcpu->stats = vmm_stat_alloc();
180221828Sgrehan}
181221828Sgrehan
182240894Sneelstruct vm_exit *
183240894Sneelvm_exitinfo(struct vm *vm, int cpuid)
184240894Sneel{
185240894Sneel	struct vcpu *vcpu;
186240894Sneel
187240894Sneel	if (cpuid < 0 || cpuid >= VM_MAXCPU)
188240894Sneel		panic("vm_exitinfo: invalid cpuid %d", cpuid);
189240894Sneel
190240894Sneel	vcpu = &vm->vcpu[cpuid];
191240894Sneel
192240894Sneel	return (&vcpu->exitinfo);
193240894Sneel}
194240894Sneel
195221828Sgrehanstatic int
196221828Sgrehanvmm_init(void)
197221828Sgrehan{
198221828Sgrehan	int error;
199221828Sgrehan
200242275Sneel	vmm_host_state_init();
201221828Sgrehan	vmm_ipi_init();
202221828Sgrehan
203221828Sgrehan	error = vmm_mem_init();
204221828Sgrehan	if (error)
205221828Sgrehan		return (error);
206221828Sgrehan
207221828Sgrehan	if (vmm_is_intel())
208221828Sgrehan		ops = &vmm_ops_intel;
209221828Sgrehan	else if (vmm_is_amd())
210221828Sgrehan		ops = &vmm_ops_amd;
211221828Sgrehan	else
212221828Sgrehan		return (ENXIO);
213221828Sgrehan
214221828Sgrehan	vmm_msr_init();
215221828Sgrehan
216221828Sgrehan	return (VMM_INIT());
217221828Sgrehan}
218221828Sgrehan
219221828Sgrehanstatic int
220221828Sgrehanvmm_handler(module_t mod, int what, void *arg)
221221828Sgrehan{
222221828Sgrehan	int error;
223221828Sgrehan
224221828Sgrehan	switch (what) {
225221828Sgrehan	case MOD_LOAD:
226221828Sgrehan		vmmdev_init();
227221828Sgrehan		iommu_init();
228221828Sgrehan		error = vmm_init();
229221828Sgrehan		break;
230221828Sgrehan	case MOD_UNLOAD:
231241454Sneel		error = vmmdev_cleanup();
232241454Sneel		if (error == 0) {
233241454Sneel			iommu_cleanup();
234241454Sneel			vmm_ipi_cleanup();
235241454Sneel			error = VMM_CLEANUP();
236241454Sneel		}
237221828Sgrehan		break;
238221828Sgrehan	default:
239221828Sgrehan		error = 0;
240221828Sgrehan		break;
241221828Sgrehan	}
242221828Sgrehan	return (error);
243221828Sgrehan}
244221828Sgrehan
245221828Sgrehanstatic moduledata_t vmm_kmod = {
246221828Sgrehan	"vmm",
247221828Sgrehan	vmm_handler,
248221828Sgrehan	NULL
249221828Sgrehan};
250221828Sgrehan
251221828Sgrehan/*
252245704Sneel * vmm initialization has the following dependencies:
253245704Sneel *
254245704Sneel * - iommu initialization must happen after the pci passthru driver has had
255245704Sneel *   a chance to attach to any passthru devices (after SI_SUB_CONFIGURE).
256245704Sneel *
257245704Sneel * - VT-x initialization requires smp_rendezvous() and therefore must happen
258245704Sneel *   after SMP is fully functional (after SI_SUB_SMP).
259221828Sgrehan */
260245704SneelDECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY);
261221828SgrehanMODULE_VERSION(vmm, 1);
262221828Sgrehan
263221828SgrehanSYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
264221828Sgrehan
265221828Sgrehanstruct vm *
266221828Sgrehanvm_create(const char *name)
267221828Sgrehan{
268221828Sgrehan	int i;
269221828Sgrehan	struct vm *vm;
270221828Sgrehan	vm_paddr_t maxaddr;
271221828Sgrehan
272221828Sgrehan	const int BSP = 0;
273221828Sgrehan
274221828Sgrehan	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
275221828Sgrehan		return (NULL);
276221828Sgrehan
277221828Sgrehan	vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO);
278221828Sgrehan	strcpy(vm->name, name);
279221828Sgrehan	vm->cookie = VMINIT(vm);
280221828Sgrehan
281221828Sgrehan	for (i = 0; i < VM_MAXCPU; i++) {
282221828Sgrehan		vcpu_init(vm, i);
283221828Sgrehan		guest_msrs_init(vm, i);
284221828Sgrehan	}
285221828Sgrehan
286221828Sgrehan	maxaddr = vmm_mem_maxaddr();
287221828Sgrehan	vm->iommu = iommu_create_domain(maxaddr);
288221828Sgrehan	vm_activate_cpu(vm, BSP);
289221828Sgrehan
290221828Sgrehan	return (vm);
291221828Sgrehan}
292221828Sgrehan
293241178Sneelstatic void
294241178Sneelvm_free_mem_seg(struct vm *vm, struct vm_memory_segment *seg)
295241178Sneel{
296241178Sneel	size_t len;
297241178Sneel	vm_paddr_t hpa;
298241362Sneel	void *host_domain;
299241178Sneel
300241362Sneel	host_domain = iommu_host_domain();
301241362Sneel
302241178Sneel	len = 0;
303241178Sneel	while (len < seg->len) {
304241178Sneel		hpa = vm_gpa2hpa(vm, seg->gpa + len, PAGE_SIZE);
305241178Sneel		if (hpa == (vm_paddr_t)-1) {
306241178Sneel			panic("vm_free_mem_segs: cannot free hpa "
307241178Sneel			      "associated with gpa 0x%016lx", seg->gpa + len);
308241178Sneel		}
309241178Sneel
310241362Sneel		/*
311241362Sneel		 * Remove the 'gpa' to 'hpa' mapping in VMs domain.
312241362Sneel		 * And resurrect the 1:1 mapping for 'hpa' in 'host_domain'.
313241362Sneel		 */
314241362Sneel		iommu_remove_mapping(vm->iommu, seg->gpa + len, PAGE_SIZE);
315241362Sneel		iommu_create_mapping(host_domain, hpa, hpa, PAGE_SIZE);
316241362Sneel
317241178Sneel		vmm_mem_free(hpa, PAGE_SIZE);
318241178Sneel
319241178Sneel		len += PAGE_SIZE;
320241178Sneel	}
321241178Sneel
322241362Sneel	/*
323241362Sneel	 * Invalidate cached translations associated with 'vm->iommu' since
324241362Sneel	 * we have now moved some pages from it.
325241362Sneel	 */
326241362Sneel	iommu_invalidate_tlb(vm->iommu);
327241362Sneel
328241178Sneel	bzero(seg, sizeof(struct vm_memory_segment));
329241178Sneel}
330241178Sneel
331221828Sgrehanvoid
332221828Sgrehanvm_destroy(struct vm *vm)
333221828Sgrehan{
334221828Sgrehan	int i;
335221828Sgrehan
336221828Sgrehan	ppt_unassign_all(vm);
337221828Sgrehan
338221828Sgrehan	for (i = 0; i < vm->num_mem_segs; i++)
339241178Sneel		vm_free_mem_seg(vm, &vm->mem_segs[i]);
340221828Sgrehan
341241178Sneel	vm->num_mem_segs = 0;
342241178Sneel
343221828Sgrehan	for (i = 0; i < VM_MAXCPU; i++)
344221828Sgrehan		vcpu_cleanup(&vm->vcpu[i]);
345221828Sgrehan
346221828Sgrehan	iommu_destroy_domain(vm->iommu);
347221828Sgrehan
348221828Sgrehan	VMCLEANUP(vm->cookie);
349221828Sgrehan
350221828Sgrehan	free(vm, M_VM);
351221828Sgrehan}
352221828Sgrehan
353221828Sgrehanconst char *
354221828Sgrehanvm_name(struct vm *vm)
355221828Sgrehan{
356221828Sgrehan	return (vm->name);
357221828Sgrehan}
358221828Sgrehan
359221828Sgrehanint
360221828Sgrehanvm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa)
361221828Sgrehan{
362221828Sgrehan	const boolean_t spok = TRUE;	/* superpage mappings are ok */
363221828Sgrehan
364241147Sneel	return (VMMMAP_SET(vm->cookie, gpa, hpa, len, VM_MEMATTR_UNCACHEABLE,
365241147Sneel			   VM_PROT_RW, spok));
366221828Sgrehan}
367221828Sgrehan
368221828Sgrehanint
369221828Sgrehanvm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len)
370221828Sgrehan{
371221828Sgrehan	const boolean_t spok = TRUE;	/* superpage mappings are ok */
372221828Sgrehan
373241147Sneel	return (VMMMAP_SET(vm->cookie, gpa, 0, len, 0,
374241147Sneel			   VM_PROT_NONE, spok));
375221828Sgrehan}
376221828Sgrehan
377241041Sneel/*
378241041Sneel * Returns TRUE if 'gpa' is available for allocation and FALSE otherwise
379241041Sneel */
380241041Sneelstatic boolean_t
381241041Sneelvm_gpa_available(struct vm *vm, vm_paddr_t gpa)
382241041Sneel{
383241041Sneel	int i;
384241041Sneel	vm_paddr_t gpabase, gpalimit;
385241041Sneel
386241041Sneel	if (gpa & PAGE_MASK)
387241041Sneel		panic("vm_gpa_available: gpa (0x%016lx) not page aligned", gpa);
388241041Sneel
389241041Sneel	for (i = 0; i < vm->num_mem_segs; i++) {
390241041Sneel		gpabase = vm->mem_segs[i].gpa;
391241041Sneel		gpalimit = gpabase + vm->mem_segs[i].len;
392241041Sneel		if (gpa >= gpabase && gpa < gpalimit)
393241041Sneel			return (FALSE);
394241041Sneel	}
395241041Sneel
396241041Sneel	return (TRUE);
397241041Sneel}
398241041Sneel
399221828Sgrehanint
400241041Sneelvm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len)
401221828Sgrehan{
402241041Sneel	int error, available, allocated;
403241178Sneel	struct vm_memory_segment *seg;
404241041Sneel	vm_paddr_t g, hpa;
405241362Sneel	void *host_domain;
406221828Sgrehan
407221828Sgrehan	const boolean_t spok = TRUE;	/* superpage mappings are ok */
408241041Sneel
409241041Sneel	if ((gpa & PAGE_MASK) || (len & PAGE_MASK) || len == 0)
410241041Sneel		return (EINVAL);
411221828Sgrehan
412241041Sneel	available = allocated = 0;
413241041Sneel	g = gpa;
414241041Sneel	while (g < gpa + len) {
415241041Sneel		if (vm_gpa_available(vm, g))
416241041Sneel			available++;
417241041Sneel		else
418241041Sneel			allocated++;
419241041Sneel
420241041Sneel		g += PAGE_SIZE;
421241041Sneel	}
422241041Sneel
423221828Sgrehan	/*
424241041Sneel	 * If there are some allocated and some available pages in the address
425241041Sneel	 * range then it is an error.
426221828Sgrehan	 */
427241041Sneel	if (allocated && available)
428241041Sneel		return (EINVAL);
429221828Sgrehan
430241041Sneel	/*
431241041Sneel	 * If the entire address range being requested has already been
432241041Sneel	 * allocated then there isn't anything more to do.
433241041Sneel	 */
434241041Sneel	if (allocated && available == 0)
435241041Sneel		return (0);
436241041Sneel
437221828Sgrehan	if (vm->num_mem_segs >= VM_MAX_MEMORY_SEGMENTS)
438221828Sgrehan		return (E2BIG);
439221828Sgrehan
440241362Sneel	host_domain = iommu_host_domain();
441241362Sneel
442241178Sneel	seg = &vm->mem_segs[vm->num_mem_segs];
443221828Sgrehan
444241362Sneel	error = 0;
445241178Sneel	seg->gpa = gpa;
446241178Sneel	seg->len = 0;
447241178Sneel	while (seg->len < len) {
448241178Sneel		hpa = vmm_mem_alloc(PAGE_SIZE);
449241178Sneel		if (hpa == 0) {
450241178Sneel			error = ENOMEM;
451241178Sneel			break;
452241178Sneel		}
453241178Sneel
454241178Sneel		error = VMMMAP_SET(vm->cookie, gpa + seg->len, hpa, PAGE_SIZE,
455241178Sneel				   VM_MEMATTR_WRITE_BACK, VM_PROT_ALL, spok);
456241178Sneel		if (error)
457241178Sneel			break;
458241178Sneel
459241362Sneel		/*
460241362Sneel		 * Remove the 1:1 mapping for 'hpa' from the 'host_domain'.
461241362Sneel		 * Add mapping for 'gpa + seg->len' to 'hpa' in the VMs domain.
462241362Sneel		 */
463241362Sneel		iommu_remove_mapping(host_domain, hpa, PAGE_SIZE);
464241178Sneel		iommu_create_mapping(vm->iommu, gpa + seg->len, hpa, PAGE_SIZE);
465241178Sneel
466241178Sneel		seg->len += PAGE_SIZE;
467241178Sneel	}
468241178Sneel
469241362Sneel	if (error) {
470241178Sneel		vm_free_mem_seg(vm, seg);
471221828Sgrehan		return (error);
472221828Sgrehan	}
473221828Sgrehan
474241362Sneel	/*
475241362Sneel	 * Invalidate cached translations associated with 'host_domain' since
476241362Sneel	 * we have now moved some pages from it.
477241362Sneel	 */
478241362Sneel	iommu_invalidate_tlb(host_domain);
479241362Sneel
480221828Sgrehan	vm->num_mem_segs++;
481241041Sneel
482221828Sgrehan	return (0);
483221828Sgrehan}
484221828Sgrehan
485221828Sgrehanvm_paddr_t
486221828Sgrehanvm_gpa2hpa(struct vm *vm, vm_paddr_t gpa, size_t len)
487221828Sgrehan{
488241148Sneel	vm_paddr_t nextpage;
489221828Sgrehan
490241148Sneel	nextpage = rounddown(gpa + PAGE_SIZE, PAGE_SIZE);
491241148Sneel	if (len > nextpage - gpa)
492241148Sneel		panic("vm_gpa2hpa: invalid gpa/len: 0x%016lx/%lu", gpa, len);
493241148Sneel
494241147Sneel	return (VMMMAP_GET(vm->cookie, gpa));
495221828Sgrehan}
496221828Sgrehan
497221828Sgrehanint
498221828Sgrehanvm_gpabase2memseg(struct vm *vm, vm_paddr_t gpabase,
499221828Sgrehan		  struct vm_memory_segment *seg)
500221828Sgrehan{
501221828Sgrehan	int i;
502221828Sgrehan
503221828Sgrehan	for (i = 0; i < vm->num_mem_segs; i++) {
504221828Sgrehan		if (gpabase == vm->mem_segs[i].gpa) {
505221828Sgrehan			*seg = vm->mem_segs[i];
506221828Sgrehan			return (0);
507221828Sgrehan		}
508221828Sgrehan	}
509221828Sgrehan	return (-1);
510221828Sgrehan}
511221828Sgrehan
512221828Sgrehanint
513221828Sgrehanvm_get_register(struct vm *vm, int vcpu, int reg, uint64_t *retval)
514221828Sgrehan{
515221828Sgrehan
516221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
517221828Sgrehan		return (EINVAL);
518221828Sgrehan
519221828Sgrehan	if (reg >= VM_REG_LAST)
520221828Sgrehan		return (EINVAL);
521221828Sgrehan
522221828Sgrehan	return (VMGETREG(vm->cookie, vcpu, reg, retval));
523221828Sgrehan}
524221828Sgrehan
525221828Sgrehanint
526221828Sgrehanvm_set_register(struct vm *vm, int vcpu, int reg, uint64_t val)
527221828Sgrehan{
528221828Sgrehan
529221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
530221828Sgrehan		return (EINVAL);
531221828Sgrehan
532221828Sgrehan	if (reg >= VM_REG_LAST)
533221828Sgrehan		return (EINVAL);
534221828Sgrehan
535221828Sgrehan	return (VMSETREG(vm->cookie, vcpu, reg, val));
536221828Sgrehan}
537221828Sgrehan
538221828Sgrehanstatic boolean_t
539221828Sgrehanis_descriptor_table(int reg)
540221828Sgrehan{
541221828Sgrehan
542221828Sgrehan	switch (reg) {
543221828Sgrehan	case VM_REG_GUEST_IDTR:
544221828Sgrehan	case VM_REG_GUEST_GDTR:
545221828Sgrehan		return (TRUE);
546221828Sgrehan	default:
547221828Sgrehan		return (FALSE);
548221828Sgrehan	}
549221828Sgrehan}
550221828Sgrehan
551221828Sgrehanstatic boolean_t
552221828Sgrehanis_segment_register(int reg)
553221828Sgrehan{
554221828Sgrehan
555221828Sgrehan	switch (reg) {
556221828Sgrehan	case VM_REG_GUEST_ES:
557221828Sgrehan	case VM_REG_GUEST_CS:
558221828Sgrehan	case VM_REG_GUEST_SS:
559221828Sgrehan	case VM_REG_GUEST_DS:
560221828Sgrehan	case VM_REG_GUEST_FS:
561221828Sgrehan	case VM_REG_GUEST_GS:
562221828Sgrehan	case VM_REG_GUEST_TR:
563221828Sgrehan	case VM_REG_GUEST_LDTR:
564221828Sgrehan		return (TRUE);
565221828Sgrehan	default:
566221828Sgrehan		return (FALSE);
567221828Sgrehan	}
568221828Sgrehan}
569221828Sgrehan
570221828Sgrehanint
571221828Sgrehanvm_get_seg_desc(struct vm *vm, int vcpu, int reg,
572221828Sgrehan		struct seg_desc *desc)
573221828Sgrehan{
574221828Sgrehan
575221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
576221828Sgrehan		return (EINVAL);
577221828Sgrehan
578221828Sgrehan	if (!is_segment_register(reg) && !is_descriptor_table(reg))
579221828Sgrehan		return (EINVAL);
580221828Sgrehan
581221828Sgrehan	return (VMGETDESC(vm->cookie, vcpu, reg, desc));
582221828Sgrehan}
583221828Sgrehan
584221828Sgrehanint
585221828Sgrehanvm_set_seg_desc(struct vm *vm, int vcpu, int reg,
586221828Sgrehan		struct seg_desc *desc)
587221828Sgrehan{
588221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
589221828Sgrehan		return (EINVAL);
590221828Sgrehan
591221828Sgrehan	if (!is_segment_register(reg) && !is_descriptor_table(reg))
592221828Sgrehan		return (EINVAL);
593221828Sgrehan
594221828Sgrehan	return (VMSETDESC(vm->cookie, vcpu, reg, desc));
595221828Sgrehan}
596221828Sgrehan
597221828Sgrehanint
598221828Sgrehanvm_get_pinning(struct vm *vm, int vcpuid, int *cpuid)
599221828Sgrehan{
600221828Sgrehan
601221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
602221828Sgrehan		return (EINVAL);
603221828Sgrehan
604221828Sgrehan	*cpuid = VCPU_PINCPU(vm, vcpuid);
605221828Sgrehan
606221828Sgrehan	return (0);
607221828Sgrehan}
608221828Sgrehan
609221828Sgrehanint
610221828Sgrehanvm_set_pinning(struct vm *vm, int vcpuid, int host_cpuid)
611221828Sgrehan{
612221828Sgrehan	struct thread *td;
613221828Sgrehan
614221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
615221828Sgrehan		return (EINVAL);
616221828Sgrehan
617221828Sgrehan	td = curthread;		/* XXXSMP only safe when muxing vcpus */
618221828Sgrehan
619221828Sgrehan	/* unpin */
620221828Sgrehan	if (host_cpuid < 0) {
621221828Sgrehan		VCPU_UNPIN(vm, vcpuid);
622221828Sgrehan		thread_lock(td);
623221828Sgrehan		sched_unbind(td);
624221828Sgrehan		thread_unlock(td);
625221828Sgrehan		return (0);
626221828Sgrehan	}
627221828Sgrehan
628221828Sgrehan	if (CPU_ABSENT(host_cpuid))
629221828Sgrehan		return (EINVAL);
630221828Sgrehan
631221828Sgrehan	/*
632221828Sgrehan	 * XXX we should check that 'host_cpuid' has not already been pinned
633221828Sgrehan	 * by another vm.
634221828Sgrehan	 */
635221828Sgrehan	thread_lock(td);
636221828Sgrehan	sched_bind(td, host_cpuid);
637221828Sgrehan	thread_unlock(td);
638221828Sgrehan	VCPU_PIN(vm, vcpuid, host_cpuid);
639221828Sgrehan
640221828Sgrehan	return (0);
641221828Sgrehan}
642221828Sgrehan
643221828Sgrehanstatic void
644221828Sgrehanrestore_guest_fpustate(struct vcpu *vcpu)
645221828Sgrehan{
646221828Sgrehan
647234695Sgrehan	/* flush host state to the pcb */
648234695Sgrehan	fpuexit(curthread);
649242122Sneel
650242122Sneel	/* restore guest FPU state */
651221828Sgrehan	fpu_stop_emulating();
652234695Sgrehan	fpurestore(vcpu->guestfpu);
653242122Sneel
654242122Sneel	/*
655242122Sneel	 * The FPU is now "dirty" with the guest's state so turn on emulation
656242122Sneel	 * to trap any access to the FPU by the host.
657242122Sneel	 */
658242122Sneel	fpu_start_emulating();
659221828Sgrehan}
660221828Sgrehan
661221828Sgrehanstatic void
662221828Sgrehansave_guest_fpustate(struct vcpu *vcpu)
663221828Sgrehan{
664221828Sgrehan
665242122Sneel	if ((rcr0() & CR0_TS) == 0)
666242122Sneel		panic("fpu emulation not enabled in host!");
667242122Sneel
668242122Sneel	/* save guest FPU state */
669242122Sneel	fpu_stop_emulating();
670234695Sgrehan	fpusave(vcpu->guestfpu);
671221828Sgrehan	fpu_start_emulating();
672221828Sgrehan}
673221828Sgrehan
674242065Sneelstatic VMM_STAT_DEFINE(VCPU_IDLE_TICKS, "number of ticks vcpu was idle");
675242065Sneel
676221828Sgrehanint
677221828Sgrehanvm_run(struct vm *vm, struct vm_run *vmrun)
678221828Sgrehan{
679242065Sneel	int error, vcpuid, sleepticks, t;
680221828Sgrehan	struct vcpu *vcpu;
681221828Sgrehan	struct pcb *pcb;
682242065Sneel	uint64_t tscval, rip;
683242065Sneel	struct vm_exit *vme;
684221828Sgrehan
685221828Sgrehan	vcpuid = vmrun->cpuid;
686221828Sgrehan
687221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
688221828Sgrehan		return (EINVAL);
689221828Sgrehan
690221828Sgrehan	vcpu = &vm->vcpu[vcpuid];
691242065Sneel	vme = &vmrun->vm_exit;
692242065Sneel	rip = vmrun->rip;
693242065Sneelrestart:
694221828Sgrehan	critical_enter();
695221828Sgrehan
696221828Sgrehan	tscval = rdtsc();
697221828Sgrehan
698221828Sgrehan	pcb = PCPU_GET(curpcb);
699221914Sjhb	set_pcb_flags(pcb, PCB_FULL_IRET);
700221828Sgrehan
701234695Sgrehan	restore_guest_msrs(vm, vcpuid);
702221828Sgrehan	restore_guest_fpustate(vcpu);
703241489Sneel
704241489Sneel	vcpu->hostcpu = curcpu;
705242065Sneel	error = VMRUN(vm->cookie, vcpuid, rip);
706241489Sneel	vcpu->hostcpu = NOCPU;
707241489Sneel
708221828Sgrehan	save_guest_fpustate(vcpu);
709221828Sgrehan	restore_host_msrs(vm, vcpuid);
710221828Sgrehan
711221828Sgrehan	vmm_stat_incr(vm, vcpuid, VCPU_TOTAL_RUNTIME, rdtsc() - tscval);
712221828Sgrehan
713240894Sneel	/* copy the exit information */
714242065Sneel	bcopy(&vcpu->exitinfo, vme, sizeof(struct vm_exit));
715240894Sneel
716221828Sgrehan	critical_exit();
717221828Sgrehan
718242065Sneel	/*
719242065Sneel	 * Oblige the guest's desire to 'hlt' by sleeping until the vcpu
720242065Sneel	 * is ready to run.
721242065Sneel	 */
722242065Sneel	if (error == 0 && vme->exitcode == VM_EXITCODE_HLT) {
723242065Sneel		vcpu_lock(vcpu);
724242065Sneel
725242065Sneel		/*
726242065Sneel		 * Figure out the number of host ticks until the next apic
727242065Sneel		 * timer interrupt in the guest.
728242065Sneel		 */
729242065Sneel		sleepticks = lapic_timer_tick(vm, vcpuid);
730242065Sneel
731242065Sneel		/*
732242065Sneel		 * If the guest local apic timer is disabled then sleep for
733242065Sneel		 * a long time but not forever.
734242065Sneel		 */
735242065Sneel		if (sleepticks < 0)
736242065Sneel			sleepticks = hz;
737242065Sneel
738242065Sneel		/*
739242065Sneel		 * Do a final check for pending NMI or interrupts before
740242065Sneel		 * really putting this thread to sleep.
741242065Sneel		 *
742242065Sneel		 * These interrupts could have happened any time after we
743242065Sneel		 * returned from VMRUN() and before we grabbed the vcpu lock.
744242065Sneel		 */
745242065Sneel		if (!vm_nmi_pending(vm, vcpuid) &&
746242065Sneel		    lapic_pending_intr(vm, vcpuid) < 0) {
747242065Sneel			if (sleepticks <= 0)
748242065Sneel				panic("invalid sleepticks %d", sleepticks);
749242065Sneel			t = ticks;
750242065Sneel			msleep_spin(vcpu, &vcpu->mtx, "vmidle", sleepticks);
751242065Sneel			vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t);
752242065Sneel		}
753242065Sneel
754242065Sneel		vcpu_unlock(vcpu);
755242065Sneel
756242065Sneel		rip = vme->rip + vme->inst_length;
757242065Sneel		goto restart;
758242065Sneel	}
759242065Sneel
760221828Sgrehan	return (error);
761221828Sgrehan}
762221828Sgrehan
763221828Sgrehanint
764221828Sgrehanvm_inject_event(struct vm *vm, int vcpuid, int type,
765221828Sgrehan		int vector, uint32_t code, int code_valid)
766221828Sgrehan{
767221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
768221828Sgrehan		return (EINVAL);
769221828Sgrehan
770221828Sgrehan	if ((type > VM_EVENT_NONE && type < VM_EVENT_MAX) == 0)
771221828Sgrehan		return (EINVAL);
772221828Sgrehan
773221828Sgrehan	if (vector < 0 || vector > 255)
774221828Sgrehan		return (EINVAL);
775221828Sgrehan
776221828Sgrehan	return (VMINJECT(vm->cookie, vcpuid, type, vector, code, code_valid));
777221828Sgrehan}
778221828Sgrehan
779242065Sneelstatic VMM_STAT_DEFINE(VCPU_NMI_COUNT, "number of NMIs delivered to vcpu");
780241982Sneel
781221828Sgrehanint
782241982Sneelvm_inject_nmi(struct vm *vm, int vcpuid)
783221828Sgrehan{
784241982Sneel	struct vcpu *vcpu;
785221828Sgrehan
786241982Sneel	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
787221828Sgrehan		return (EINVAL);
788221828Sgrehan
789241982Sneel	vcpu = &vm->vcpu[vcpuid];
790241982Sneel
791241982Sneel	vcpu->nmi_pending = 1;
792241982Sneel	vm_interrupt_hostcpu(vm, vcpuid);
793241982Sneel	return (0);
794221828Sgrehan}
795221828Sgrehan
796221828Sgrehanint
797241982Sneelvm_nmi_pending(struct vm *vm, int vcpuid)
798241982Sneel{
799241982Sneel	struct vcpu *vcpu;
800241982Sneel
801241982Sneel	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
802241982Sneel		panic("vm_nmi_pending: invalid vcpuid %d", vcpuid);
803241982Sneel
804241982Sneel	vcpu = &vm->vcpu[vcpuid];
805241982Sneel
806241982Sneel	return (vcpu->nmi_pending);
807241982Sneel}
808241982Sneel
809241982Sneelvoid
810241982Sneelvm_nmi_clear(struct vm *vm, int vcpuid)
811241982Sneel{
812241982Sneel	struct vcpu *vcpu;
813241982Sneel
814241982Sneel	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
815241982Sneel		panic("vm_nmi_pending: invalid vcpuid %d", vcpuid);
816241982Sneel
817241982Sneel	vcpu = &vm->vcpu[vcpuid];
818241982Sneel
819241982Sneel	if (vcpu->nmi_pending == 0)
820241982Sneel		panic("vm_nmi_clear: inconsistent nmi_pending state");
821241982Sneel
822241982Sneel	vcpu->nmi_pending = 0;
823241982Sneel	vmm_stat_incr(vm, vcpuid, VCPU_NMI_COUNT, 1);
824241982Sneel}
825241982Sneel
826241982Sneelint
827221828Sgrehanvm_get_capability(struct vm *vm, int vcpu, int type, int *retval)
828221828Sgrehan{
829221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
830221828Sgrehan		return (EINVAL);
831221828Sgrehan
832221828Sgrehan	if (type < 0 || type >= VM_CAP_MAX)
833221828Sgrehan		return (EINVAL);
834221828Sgrehan
835221828Sgrehan	return (VMGETCAP(vm->cookie, vcpu, type, retval));
836221828Sgrehan}
837221828Sgrehan
838221828Sgrehanint
839221828Sgrehanvm_set_capability(struct vm *vm, int vcpu, int type, int val)
840221828Sgrehan{
841221828Sgrehan	if (vcpu < 0 || vcpu >= VM_MAXCPU)
842221828Sgrehan		return (EINVAL);
843221828Sgrehan
844221828Sgrehan	if (type < 0 || type >= VM_CAP_MAX)
845221828Sgrehan		return (EINVAL);
846221828Sgrehan
847221828Sgrehan	return (VMSETCAP(vm->cookie, vcpu, type, val));
848221828Sgrehan}
849221828Sgrehan
850221828Sgrehanuint64_t *
851221828Sgrehanvm_guest_msrs(struct vm *vm, int cpu)
852221828Sgrehan{
853221828Sgrehan	return (vm->vcpu[cpu].guest_msrs);
854221828Sgrehan}
855221828Sgrehan
856221828Sgrehanstruct vlapic *
857221828Sgrehanvm_lapic(struct vm *vm, int cpu)
858221828Sgrehan{
859221828Sgrehan	return (vm->vcpu[cpu].vlapic);
860221828Sgrehan}
861221828Sgrehan
862221828Sgrehanboolean_t
863221828Sgrehanvmm_is_pptdev(int bus, int slot, int func)
864221828Sgrehan{
865246188Sneel	int found, i, n;
866246188Sneel	int b, s, f;
867221828Sgrehan	char *val, *cp, *cp2;
868221828Sgrehan
869221828Sgrehan	/*
870246188Sneel	 * XXX
871246188Sneel	 * The length of an environment variable is limited to 128 bytes which
872246188Sneel	 * puts an upper limit on the number of passthru devices that may be
873246188Sneel	 * specified using a single environment variable.
874246188Sneel	 *
875246188Sneel	 * Work around this by scanning multiple environment variable
876246188Sneel	 * names instead of a single one - yuck!
877221828Sgrehan	 */
878246188Sneel	const char *names[] = { "pptdevs", "pptdevs2", "pptdevs3", NULL };
879246188Sneel
880246188Sneel	/* set pptdevs="1/2/3 4/5/6 7/8/9 10/11/12" */
881221828Sgrehan	found = 0;
882246188Sneel	for (i = 0; names[i] != NULL && !found; i++) {
883246188Sneel		cp = val = getenv(names[i]);
884246188Sneel		while (cp != NULL && *cp != '\0') {
885246188Sneel			if ((cp2 = strchr(cp, ' ')) != NULL)
886246188Sneel				*cp2 = '\0';
887221828Sgrehan
888246188Sneel			n = sscanf(cp, "%d/%d/%d", &b, &s, &f);
889246188Sneel			if (n == 3 && bus == b && slot == s && func == f) {
890246188Sneel				found = 1;
891246188Sneel				break;
892246188Sneel			}
893221828Sgrehan
894246188Sneel			if (cp2 != NULL)
895246188Sneel				*cp2++ = ' ';
896221828Sgrehan
897246188Sneel			cp = cp2;
898246188Sneel		}
899246188Sneel		freeenv(val);
900221828Sgrehan	}
901221828Sgrehan	return (found);
902221828Sgrehan}
903221828Sgrehan
904221828Sgrehanvoid *
905221828Sgrehanvm_iommu_domain(struct vm *vm)
906221828Sgrehan{
907221828Sgrehan
908221828Sgrehan	return (vm->iommu);
909221828Sgrehan}
910221828Sgrehan
911241489Sneelint
912241489Sneelvcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state state)
913221828Sgrehan{
914241489Sneel	int error;
915221828Sgrehan	struct vcpu *vcpu;
916221828Sgrehan
917221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
918221828Sgrehan		panic("vm_set_run_state: invalid vcpuid %d", vcpuid);
919221828Sgrehan
920221828Sgrehan	vcpu = &vm->vcpu[vcpuid];
921221828Sgrehan
922241489Sneel	vcpu_lock(vcpu);
923241489Sneel
924241489Sneel	/*
925241489Sneel	 * The following state transitions are allowed:
926241489Sneel	 * IDLE -> RUNNING -> IDLE
927241489Sneel	 * IDLE -> CANNOT_RUN -> IDLE
928241489Sneel	 */
929241489Sneel	if ((vcpu->state == VCPU_IDLE && state != VCPU_IDLE) ||
930241489Sneel	    (vcpu->state != VCPU_IDLE && state == VCPU_IDLE)) {
931241489Sneel		error = 0;
932241489Sneel		vcpu->state = state;
933221828Sgrehan	} else {
934241489Sneel		error = EBUSY;
935221828Sgrehan	}
936241489Sneel
937241489Sneel	vcpu_unlock(vcpu);
938241489Sneel
939241489Sneel	return (error);
940221828Sgrehan}
941221828Sgrehan
942241489Sneelenum vcpu_state
943241489Sneelvcpu_get_state(struct vm *vm, int vcpuid)
944221828Sgrehan{
945221828Sgrehan	struct vcpu *vcpu;
946241489Sneel	enum vcpu_state state;
947221828Sgrehan
948221828Sgrehan	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
949221828Sgrehan		panic("vm_get_run_state: invalid vcpuid %d", vcpuid);
950221828Sgrehan
951221828Sgrehan	vcpu = &vm->vcpu[vcpuid];
952221828Sgrehan
953241489Sneel	vcpu_lock(vcpu);
954241489Sneel	state = vcpu->state;
955241489Sneel	vcpu_unlock(vcpu);
956221828Sgrehan
957241489Sneel	return (state);
958221828Sgrehan}
959221828Sgrehan
960221828Sgrehanvoid
961221828Sgrehanvm_activate_cpu(struct vm *vm, int vcpuid)
962221828Sgrehan{
963221828Sgrehan
964221828Sgrehan	if (vcpuid >= 0 && vcpuid < VM_MAXCPU)
965223621Sgrehan		CPU_SET(vcpuid, &vm->active_cpus);
966221828Sgrehan}
967221828Sgrehan
968223621Sgrehancpuset_t
969221828Sgrehanvm_active_cpus(struct vm *vm)
970221828Sgrehan{
971221828Sgrehan
972221828Sgrehan	return (vm->active_cpus);
973221828Sgrehan}
974221828Sgrehan
975221828Sgrehanvoid *
976221828Sgrehanvcpu_stats(struct vm *vm, int vcpuid)
977221828Sgrehan{
978221828Sgrehan
979221828Sgrehan	return (vm->vcpu[vcpuid].stats);
980221828Sgrehan}
981240922Sneel
982240922Sneelint
983240922Sneelvm_get_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state *state)
984240922Sneel{
985240922Sneel	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
986240922Sneel		return (EINVAL);
987240922Sneel
988240922Sneel	*state = vm->vcpu[vcpuid].x2apic_state;
989240922Sneel
990240922Sneel	return (0);
991240922Sneel}
992240922Sneel
993240922Sneelint
994240922Sneelvm_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state)
995240922Sneel{
996240922Sneel	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
997240922Sneel		return (EINVAL);
998240922Sneel
999240922Sneel	if (state < 0 || state >= X2APIC_STATE_LAST)
1000240922Sneel		return (EINVAL);
1001240922Sneel
1002240922Sneel	vm->vcpu[vcpuid].x2apic_state = state;
1003240922Sneel
1004240943Sneel	vlapic_set_x2apic_state(vm, vcpuid, state);
1005240943Sneel
1006240922Sneel	return (0);
1007240922Sneel}
1008241489Sneel
1009241489Sneelvoid
1010241489Sneelvm_interrupt_hostcpu(struct vm *vm, int vcpuid)
1011241489Sneel{
1012241489Sneel	int hostcpu;
1013241489Sneel	struct vcpu *vcpu;
1014241489Sneel
1015241489Sneel	vcpu = &vm->vcpu[vcpuid];
1016241489Sneel
1017242065Sneel	vcpu_lock(vcpu);
1018241489Sneel	hostcpu = vcpu->hostcpu;
1019242065Sneel	if (hostcpu == NOCPU) {
1020242065Sneel		/*
1021242065Sneel		 * If the vcpu is 'RUNNING' but without a valid 'hostcpu' then
1022242065Sneel		 * the host thread must be sleeping waiting for an event to
1023242065Sneel		 * kick the vcpu out of 'hlt'.
1024242065Sneel		 *
1025242065Sneel		 * XXX this is racy because the condition exists right before
1026242065Sneel		 * and after calling VMRUN() in vm_run(). The wakeup() is
1027242065Sneel		 * benign in this case.
1028242065Sneel		 */
1029242065Sneel		if (vcpu->state == VCPU_RUNNING)
1030242065Sneel			wakeup_one(vcpu);
1031242065Sneel	} else {
1032242065Sneel		if (vcpu->state != VCPU_RUNNING)
1033242065Sneel			panic("invalid vcpu state %d", vcpu->state);
1034242065Sneel		if (hostcpu != curcpu)
1035242065Sneel			ipi_cpu(hostcpu, vmm_ipinum);
1036242065Sneel	}
1037242065Sneel	vcpu_unlock(vcpu);
1038241489Sneel}
1039