xref: /freebsd/sys/amd64/vmm/vmm.c (revision 98ed632c63bec8b8cfe7f8849f74ba15657bd06b)
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/sysctl.h>
37 #include <sys/malloc.h>
38 #include <sys/pcpu.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/proc.h>
42 #include <sys/sched.h>
43 #include <sys/smp.h>
44 #include <sys/systm.h>
45 
46 #include <vm/vm.h>
47 
48 #include <machine/vm.h>
49 #include <machine/pcb.h>
50 #include <x86/apicreg.h>
51 
52 #include <machine/vmm.h>
53 #include "vmm_mem.h"
54 #include "vmm_util.h"
55 #include <machine/vmm_dev.h>
56 #include "vlapic.h"
57 #include "vmm_msr.h"
58 #include "vmm_ipi.h"
59 #include "vmm_stat.h"
60 
61 #include "io/ppt.h"
62 #include "io/iommu.h"
63 
64 struct vlapic;
65 
66 struct vcpu {
67 	int		flags;
68 	int		pincpu;		/* host cpuid this vcpu is bound to */
69 	int		hostcpu;	/* host cpuid this vcpu last ran on */
70 	uint64_t	guest_msrs[VMM_MSR_NUM];
71 	struct vlapic	*vlapic;
72 	int		 vcpuid;
73 	struct savefpu	*guestfpu;	/* guest fpu state */
74 	void		*stats;
75 	struct vm_exit	exitinfo;
76 };
77 #define	VCPU_F_PINNED	0x0001
78 #define	VCPU_F_RUNNING	0x0002
79 
80 #define	VCPU_PINCPU(vm, vcpuid)	\
81     ((vm->vcpu[vcpuid].flags & VCPU_F_PINNED) ? vm->vcpu[vcpuid].pincpu : -1)
82 
83 #define	VCPU_UNPIN(vm, vcpuid)	(vm->vcpu[vcpuid].flags &= ~VCPU_F_PINNED)
84 
85 #define	VCPU_PIN(vm, vcpuid, host_cpuid)				\
86 do {									\
87 	vm->vcpu[vcpuid].flags |= VCPU_F_PINNED;			\
88 	vm->vcpu[vcpuid].pincpu = host_cpuid;				\
89 } while(0)
90 
91 #define	VM_MAX_MEMORY_SEGMENTS	2
92 
93 struct vm {
94 	void		*cookie;	/* processor-specific data */
95 	void		*iommu;		/* iommu-specific data */
96 	struct vcpu	vcpu[VM_MAXCPU];
97 	int		num_mem_segs;
98 	struct vm_memory_segment mem_segs[VM_MAX_MEMORY_SEGMENTS];
99 	char		name[VM_MAX_NAMELEN];
100 
101 	/*
102 	 * Set of active vcpus.
103 	 * An active vcpu is one that has been started implicitly (BSP) or
104 	 * explicitly (AP) by sending it a startup ipi.
105 	 */
106 	cpuset_t	active_cpus;
107 };
108 
109 static struct vmm_ops *ops;
110 #define	VMM_INIT()	(ops != NULL ? (*ops->init)() : 0)
111 #define	VMM_CLEANUP()	(ops != NULL ? (*ops->cleanup)() : 0)
112 
113 #define	VMINIT(vm)	(ops != NULL ? (*ops->vminit)(vm): NULL)
114 #define	VMRUN(vmi, vcpu, rip) \
115 	(ops != NULL ? (*ops->vmrun)(vmi, vcpu, rip) : ENXIO)
116 #define	VMCLEANUP(vmi)	(ops != NULL ? (*ops->vmcleanup)(vmi) : NULL)
117 #define	VMMMAP(vmi, gpa, hpa, len, attr, prot, spm)	\
118     (ops != NULL ? (*ops->vmmmap)(vmi, gpa, hpa, len, attr, prot, spm) : ENXIO)
119 #define	VMGETREG(vmi, vcpu, num, retval)		\
120 	(ops != NULL ? (*ops->vmgetreg)(vmi, vcpu, num, retval) : ENXIO)
121 #define	VMSETREG(vmi, vcpu, num, val)		\
122 	(ops != NULL ? (*ops->vmsetreg)(vmi, vcpu, num, val) : ENXIO)
123 #define	VMGETDESC(vmi, vcpu, num, desc)		\
124 	(ops != NULL ? (*ops->vmgetdesc)(vmi, vcpu, num, desc) : ENXIO)
125 #define	VMSETDESC(vmi, vcpu, num, desc)		\
126 	(ops != NULL ? (*ops->vmsetdesc)(vmi, vcpu, num, desc) : ENXIO)
127 #define	VMINJECT(vmi, vcpu, type, vec, ec, ecv)	\
128 	(ops != NULL ? (*ops->vminject)(vmi, vcpu, type, vec, ec, ecv) : ENXIO)
129 #define	VMNMI(vmi, vcpu)	\
130 	(ops != NULL ? (*ops->vmnmi)(vmi, vcpu) : ENXIO)
131 #define	VMGETCAP(vmi, vcpu, num, retval)	\
132 	(ops != NULL ? (*ops->vmgetcap)(vmi, vcpu, num, retval) : ENXIO)
133 #define	VMSETCAP(vmi, vcpu, num, val)		\
134 	(ops != NULL ? (*ops->vmsetcap)(vmi, vcpu, num, val) : ENXIO)
135 
136 #define	fpu_start_emulating()	start_emulating()
137 #define	fpu_stop_emulating()	stop_emulating()
138 
139 static MALLOC_DEFINE(M_VM, "vm", "vm");
140 CTASSERT(VMM_MSR_NUM <= 64);	/* msr_mask can keep track of up to 64 msrs */
141 
142 /* statistics */
143 static VMM_STAT_DEFINE(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
144 
145 static void
146 vcpu_cleanup(struct vcpu *vcpu)
147 {
148 	vlapic_cleanup(vcpu->vlapic);
149 	vmm_stat_free(vcpu->stats);
150 	fpu_save_area_free(vcpu->guestfpu);
151 }
152 
153 static void
154 vcpu_init(struct vm *vm, uint32_t vcpu_id)
155 {
156 	struct vcpu *vcpu;
157 
158 	vcpu = &vm->vcpu[vcpu_id];
159 
160 	vcpu->hostcpu = -1;
161 	vcpu->vcpuid = vcpu_id;
162 	vcpu->vlapic = vlapic_init(vm, vcpu_id);
163 	vcpu->guestfpu = fpu_save_area_alloc();
164 	fpu_save_area_reset(vcpu->guestfpu);
165 	vcpu->stats = vmm_stat_alloc();
166 }
167 
168 struct vm_exit *
169 vm_exitinfo(struct vm *vm, int cpuid)
170 {
171 	struct vcpu *vcpu;
172 
173 	if (cpuid < 0 || cpuid >= VM_MAXCPU)
174 		panic("vm_exitinfo: invalid cpuid %d", cpuid);
175 
176 	vcpu = &vm->vcpu[cpuid];
177 
178 	return (&vcpu->exitinfo);
179 }
180 
181 static int
182 vmm_init(void)
183 {
184 	int error;
185 
186 	vmm_ipi_init();
187 
188 	error = vmm_mem_init();
189 	if (error)
190 		return (error);
191 
192 	if (vmm_is_intel())
193 		ops = &vmm_ops_intel;
194 	else if (vmm_is_amd())
195 		ops = &vmm_ops_amd;
196 	else
197 		return (ENXIO);
198 
199 	vmm_msr_init();
200 
201 	return (VMM_INIT());
202 }
203 
204 static int
205 vmm_handler(module_t mod, int what, void *arg)
206 {
207 	int error;
208 
209 	switch (what) {
210 	case MOD_LOAD:
211 		vmmdev_init();
212 		iommu_init();
213 		error = vmm_init();
214 		break;
215 	case MOD_UNLOAD:
216 		vmmdev_cleanup();
217 		iommu_cleanup();
218 		vmm_ipi_cleanup();
219 		error = VMM_CLEANUP();
220 		break;
221 	default:
222 		error = 0;
223 		break;
224 	}
225 	return (error);
226 }
227 
228 static moduledata_t vmm_kmod = {
229 	"vmm",
230 	vmm_handler,
231 	NULL
232 };
233 
234 /*
235  * Execute the module load handler after the pci passthru driver has had
236  * a chance to claim devices. We need this information at the time we do
237  * iommu initialization.
238  */
239 DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_CONFIGURE + 1, SI_ORDER_ANY);
240 MODULE_VERSION(vmm, 1);
241 
242 SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
243 
244 struct vm *
245 vm_create(const char *name)
246 {
247 	int i;
248 	struct vm *vm;
249 	vm_paddr_t maxaddr;
250 
251 	const int BSP = 0;
252 
253 	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
254 		return (NULL);
255 
256 	vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO);
257 	strcpy(vm->name, name);
258 	vm->cookie = VMINIT(vm);
259 
260 	for (i = 0; i < VM_MAXCPU; i++) {
261 		vcpu_init(vm, i);
262 		guest_msrs_init(vm, i);
263 	}
264 
265 	maxaddr = vmm_mem_maxaddr();
266 	vm->iommu = iommu_create_domain(maxaddr);
267 	vm_activate_cpu(vm, BSP);
268 
269 	return (vm);
270 }
271 
272 void
273 vm_destroy(struct vm *vm)
274 {
275 	int i;
276 
277 	ppt_unassign_all(vm);
278 
279 	for (i = 0; i < vm->num_mem_segs; i++)
280 		vmm_mem_free(vm->mem_segs[i].hpa, vm->mem_segs[i].len);
281 
282 	for (i = 0; i < VM_MAXCPU; i++)
283 		vcpu_cleanup(&vm->vcpu[i]);
284 
285 	iommu_destroy_domain(vm->iommu);
286 
287 	VMCLEANUP(vm->cookie);
288 
289 	free(vm, M_VM);
290 }
291 
292 const char *
293 vm_name(struct vm *vm)
294 {
295 	return (vm->name);
296 }
297 
298 int
299 vm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa)
300 {
301 	const boolean_t spok = TRUE;	/* superpage mappings are ok */
302 
303 	return (VMMMAP(vm->cookie, gpa, hpa, len, VM_MEMATTR_UNCACHEABLE,
304 		       VM_PROT_RW, spok));
305 }
306 
307 int
308 vm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len)
309 {
310 	const boolean_t spok = TRUE;	/* superpage mappings are ok */
311 
312 	return (VMMMAP(vm->cookie, gpa, 0, len, VM_MEMATTR_UNCACHEABLE,
313 		       VM_PROT_NONE, spok));
314 }
315 
316 int
317 vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t *ret_hpa)
318 {
319 	int error;
320 	vm_paddr_t hpa;
321 
322 	const boolean_t spok = TRUE;	/* superpage mappings are ok */
323 
324 	/*
325 	 * find the hpa if already it was already vm_malloc'd.
326 	 */
327 	hpa = vm_gpa2hpa(vm, gpa, len);
328 	if (hpa != ((vm_paddr_t)-1))
329 		goto out;
330 
331 	if (vm->num_mem_segs >= VM_MAX_MEMORY_SEGMENTS)
332 		return (E2BIG);
333 
334 	hpa = vmm_mem_alloc(len);
335 	if (hpa == 0)
336 		return (ENOMEM);
337 
338 	error = VMMMAP(vm->cookie, gpa, hpa, len, VM_MEMATTR_WRITE_BACK,
339 		       VM_PROT_ALL, spok);
340 	if (error) {
341 		vmm_mem_free(hpa, len);
342 		return (error);
343 	}
344 
345 	iommu_create_mapping(vm->iommu, gpa, hpa, len);
346 
347 	vm->mem_segs[vm->num_mem_segs].gpa = gpa;
348 	vm->mem_segs[vm->num_mem_segs].hpa = hpa;
349 	vm->mem_segs[vm->num_mem_segs].len = len;
350 	vm->num_mem_segs++;
351 out:
352 	*ret_hpa = hpa;
353 	return (0);
354 }
355 
356 vm_paddr_t
357 vm_gpa2hpa(struct vm *vm, vm_paddr_t gpa, size_t len)
358 {
359 	int i;
360 	vm_paddr_t gpabase, gpalimit, hpabase;
361 
362 	for (i = 0; i < vm->num_mem_segs; i++) {
363 		hpabase = vm->mem_segs[i].hpa;
364 		gpabase = vm->mem_segs[i].gpa;
365 		gpalimit = gpabase + vm->mem_segs[i].len;
366 		if (gpa >= gpabase && gpa + len <= gpalimit)
367 			return ((gpa - gpabase) + hpabase);
368 	}
369 	return ((vm_paddr_t)-1);
370 }
371 
372 int
373 vm_gpabase2memseg(struct vm *vm, vm_paddr_t gpabase,
374 		  struct vm_memory_segment *seg)
375 {
376 	int i;
377 
378 	for (i = 0; i < vm->num_mem_segs; i++) {
379 		if (gpabase == vm->mem_segs[i].gpa) {
380 			*seg = vm->mem_segs[i];
381 			return (0);
382 		}
383 	}
384 	return (-1);
385 }
386 
387 int
388 vm_get_register(struct vm *vm, int vcpu, int reg, uint64_t *retval)
389 {
390 
391 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
392 		return (EINVAL);
393 
394 	if (reg >= VM_REG_LAST)
395 		return (EINVAL);
396 
397 	return (VMGETREG(vm->cookie, vcpu, reg, retval));
398 }
399 
400 int
401 vm_set_register(struct vm *vm, int vcpu, int reg, uint64_t val)
402 {
403 
404 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
405 		return (EINVAL);
406 
407 	if (reg >= VM_REG_LAST)
408 		return (EINVAL);
409 
410 	return (VMSETREG(vm->cookie, vcpu, reg, val));
411 }
412 
413 static boolean_t
414 is_descriptor_table(int reg)
415 {
416 
417 	switch (reg) {
418 	case VM_REG_GUEST_IDTR:
419 	case VM_REG_GUEST_GDTR:
420 		return (TRUE);
421 	default:
422 		return (FALSE);
423 	}
424 }
425 
426 static boolean_t
427 is_segment_register(int reg)
428 {
429 
430 	switch (reg) {
431 	case VM_REG_GUEST_ES:
432 	case VM_REG_GUEST_CS:
433 	case VM_REG_GUEST_SS:
434 	case VM_REG_GUEST_DS:
435 	case VM_REG_GUEST_FS:
436 	case VM_REG_GUEST_GS:
437 	case VM_REG_GUEST_TR:
438 	case VM_REG_GUEST_LDTR:
439 		return (TRUE);
440 	default:
441 		return (FALSE);
442 	}
443 }
444 
445 int
446 vm_get_seg_desc(struct vm *vm, int vcpu, int reg,
447 		struct seg_desc *desc)
448 {
449 
450 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
451 		return (EINVAL);
452 
453 	if (!is_segment_register(reg) && !is_descriptor_table(reg))
454 		return (EINVAL);
455 
456 	return (VMGETDESC(vm->cookie, vcpu, reg, desc));
457 }
458 
459 int
460 vm_set_seg_desc(struct vm *vm, int vcpu, int reg,
461 		struct seg_desc *desc)
462 {
463 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
464 		return (EINVAL);
465 
466 	if (!is_segment_register(reg) && !is_descriptor_table(reg))
467 		return (EINVAL);
468 
469 	return (VMSETDESC(vm->cookie, vcpu, reg, desc));
470 }
471 
472 int
473 vm_get_pinning(struct vm *vm, int vcpuid, int *cpuid)
474 {
475 
476 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
477 		return (EINVAL);
478 
479 	*cpuid = VCPU_PINCPU(vm, vcpuid);
480 
481 	return (0);
482 }
483 
484 int
485 vm_set_pinning(struct vm *vm, int vcpuid, int host_cpuid)
486 {
487 	struct thread *td;
488 
489 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
490 		return (EINVAL);
491 
492 	td = curthread;		/* XXXSMP only safe when muxing vcpus */
493 
494 	/* unpin */
495 	if (host_cpuid < 0) {
496 		VCPU_UNPIN(vm, vcpuid);
497 		thread_lock(td);
498 		sched_unbind(td);
499 		thread_unlock(td);
500 		return (0);
501 	}
502 
503 	if (CPU_ABSENT(host_cpuid))
504 		return (EINVAL);
505 
506 	/*
507 	 * XXX we should check that 'host_cpuid' has not already been pinned
508 	 * by another vm.
509 	 */
510 	thread_lock(td);
511 	sched_bind(td, host_cpuid);
512 	thread_unlock(td);
513 	VCPU_PIN(vm, vcpuid, host_cpuid);
514 
515 	return (0);
516 }
517 
518 static void
519 restore_guest_fpustate(struct vcpu *vcpu)
520 {
521 
522 	/* flush host state to the pcb */
523 	fpuexit(curthread);
524 	fpu_stop_emulating();
525 	fpurestore(vcpu->guestfpu);
526 }
527 
528 static void
529 save_guest_fpustate(struct vcpu *vcpu)
530 {
531 
532 	fpusave(vcpu->guestfpu);
533 	fpu_start_emulating();
534 }
535 
536 int
537 vm_run(struct vm *vm, struct vm_run *vmrun)
538 {
539 	int error, vcpuid;
540 	struct vcpu *vcpu;
541 	struct pcb *pcb;
542 	uint64_t tscval;
543 
544 	vcpuid = vmrun->cpuid;
545 
546 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
547 		return (EINVAL);
548 
549 	vcpu = &vm->vcpu[vcpuid];
550 
551 	critical_enter();
552 
553 	tscval = rdtsc();
554 
555 	pcb = PCPU_GET(curpcb);
556 	set_pcb_flags(pcb, PCB_FULL_IRET);
557 
558 	vcpu->hostcpu = curcpu;
559 
560 	restore_guest_msrs(vm, vcpuid);
561 	restore_guest_fpustate(vcpu);
562 	error = VMRUN(vm->cookie, vcpuid, vmrun->rip);
563 	save_guest_fpustate(vcpu);
564 	restore_host_msrs(vm, vcpuid);
565 
566 	vmm_stat_incr(vm, vcpuid, VCPU_TOTAL_RUNTIME, rdtsc() - tscval);
567 
568 	/* copy the exit information */
569 	bcopy(&vcpu->exitinfo, &vmrun->vm_exit, sizeof(struct vm_exit));
570 
571 	critical_exit();
572 
573 	return (error);
574 }
575 
576 int
577 vm_inject_event(struct vm *vm, int vcpuid, int type,
578 		int vector, uint32_t code, int code_valid)
579 {
580 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
581 		return (EINVAL);
582 
583 	if ((type > VM_EVENT_NONE && type < VM_EVENT_MAX) == 0)
584 		return (EINVAL);
585 
586 	if (vector < 0 || vector > 255)
587 		return (EINVAL);
588 
589 	return (VMINJECT(vm->cookie, vcpuid, type, vector, code, code_valid));
590 }
591 
592 int
593 vm_inject_nmi(struct vm *vm, int vcpu)
594 {
595 	int error;
596 
597 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
598 		return (EINVAL);
599 
600 	error = VMNMI(vm->cookie, vcpu);
601 	vm_interrupt_hostcpu(vm, vcpu);
602 	return (error);
603 }
604 
605 int
606 vm_get_capability(struct vm *vm, int vcpu, int type, int *retval)
607 {
608 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
609 		return (EINVAL);
610 
611 	if (type < 0 || type >= VM_CAP_MAX)
612 		return (EINVAL);
613 
614 	return (VMGETCAP(vm->cookie, vcpu, type, retval));
615 }
616 
617 int
618 vm_set_capability(struct vm *vm, int vcpu, int type, int val)
619 {
620 	if (vcpu < 0 || vcpu >= VM_MAXCPU)
621 		return (EINVAL);
622 
623 	if (type < 0 || type >= VM_CAP_MAX)
624 		return (EINVAL);
625 
626 	return (VMSETCAP(vm->cookie, vcpu, type, val));
627 }
628 
629 uint64_t *
630 vm_guest_msrs(struct vm *vm, int cpu)
631 {
632 	return (vm->vcpu[cpu].guest_msrs);
633 }
634 
635 struct vlapic *
636 vm_lapic(struct vm *vm, int cpu)
637 {
638 	return (vm->vcpu[cpu].vlapic);
639 }
640 
641 boolean_t
642 vmm_is_pptdev(int bus, int slot, int func)
643 {
644 	int found, b, s, f, n;
645 	char *val, *cp, *cp2;
646 
647 	/*
648 	 * setenv pptdevs "1/2/3 4/5/6 7/8/9 10/11/12"
649 	 */
650 	found = 0;
651 	cp = val = getenv("pptdevs");
652 	while (cp != NULL && *cp != '\0') {
653 		if ((cp2 = strchr(cp, ' ')) != NULL)
654 			*cp2 = '\0';
655 
656 		n = sscanf(cp, "%d/%d/%d", &b, &s, &f);
657 		if (n == 3 && bus == b && slot == s && func == f) {
658 			found = 1;
659 			break;
660 		}
661 
662 		if (cp2 != NULL)
663 			*cp2++ = ' ';
664 
665 		cp = cp2;
666 	}
667 	freeenv(val);
668 	return (found);
669 }
670 
671 void *
672 vm_iommu_domain(struct vm *vm)
673 {
674 
675 	return (vm->iommu);
676 }
677 
678 void
679 vm_set_run_state(struct vm *vm, int vcpuid, int state)
680 {
681 	struct vcpu *vcpu;
682 
683 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
684 		panic("vm_set_run_state: invalid vcpuid %d", vcpuid);
685 
686 	vcpu = &vm->vcpu[vcpuid];
687 
688 	if (state == VCPU_RUNNING) {
689 		if (vcpu->flags & VCPU_F_RUNNING) {
690 			panic("vm_set_run_state: %s[%d] is already running",
691 			      vm_name(vm), vcpuid);
692 		}
693 		vcpu->flags |= VCPU_F_RUNNING;
694 	} else {
695 		if ((vcpu->flags & VCPU_F_RUNNING) == 0) {
696 			panic("vm_set_run_state: %s[%d] is already stopped",
697 			      vm_name(vm), vcpuid);
698 		}
699 		vcpu->flags &= ~VCPU_F_RUNNING;
700 	}
701 }
702 
703 int
704 vm_get_run_state(struct vm *vm, int vcpuid, int *cpuptr)
705 {
706 	int retval, hostcpu;
707 	struct vcpu *vcpu;
708 
709 	if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
710 		panic("vm_get_run_state: invalid vcpuid %d", vcpuid);
711 
712 	vcpu = &vm->vcpu[vcpuid];
713 	if (vcpu->flags & VCPU_F_RUNNING) {
714 		retval = VCPU_RUNNING;
715 		hostcpu = vcpu->hostcpu;
716 	} else {
717 		retval = VCPU_STOPPED;
718 		hostcpu = -1;
719 	}
720 
721 	if (cpuptr)
722 		*cpuptr = hostcpu;
723 
724 	return (retval);
725 }
726 
727 void
728 vm_activate_cpu(struct vm *vm, int vcpuid)
729 {
730 
731 	if (vcpuid >= 0 && vcpuid < VM_MAXCPU)
732 		CPU_SET(vcpuid, &vm->active_cpus);
733 }
734 
735 cpuset_t
736 vm_active_cpus(struct vm *vm)
737 {
738 
739 	return (vm->active_cpus);
740 }
741 
742 void *
743 vcpu_stats(struct vm *vm, int vcpuid)
744 {
745 
746 	return (vm->vcpu[vcpuid].stats);
747 }
748