1d3916eacSRuslan Bukin /*-
2d3916eacSRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause
3d3916eacSRuslan Bukin *
4d3916eacSRuslan Bukin * Copyright (c) 2015 Mihai Carabas <mihai.carabas@gmail.com>
5d3916eacSRuslan Bukin * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
6d3916eacSRuslan Bukin *
7d3916eacSRuslan Bukin * This software was developed by the University of Cambridge Computer
8d3916eacSRuslan Bukin * Laboratory (Department of Computer Science and Technology) under Innovate
9d3916eacSRuslan Bukin * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
10d3916eacSRuslan Bukin * Prototype".
11d3916eacSRuslan Bukin *
12d3916eacSRuslan Bukin * Redistribution and use in source and binary forms, with or without
13d3916eacSRuslan Bukin * modification, are permitted provided that the following conditions
14d3916eacSRuslan Bukin * are met:
15d3916eacSRuslan Bukin * 1. Redistributions of source code must retain the above copyright
16d3916eacSRuslan Bukin * notice, this list of conditions and the following disclaimer.
17d3916eacSRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright
18d3916eacSRuslan Bukin * notice, this list of conditions and the following disclaimer in the
19d3916eacSRuslan Bukin * documentation and/or other materials provided with the distribution.
20d3916eacSRuslan Bukin *
21d3916eacSRuslan Bukin * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22d3916eacSRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23d3916eacSRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24d3916eacSRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25d3916eacSRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26d3916eacSRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27d3916eacSRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28d3916eacSRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29d3916eacSRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30d3916eacSRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31d3916eacSRuslan Bukin * SUCH DAMAGE.
32d3916eacSRuslan Bukin */
33d3916eacSRuslan Bukin
34d3916eacSRuslan Bukin #include <sys/param.h>
35d3916eacSRuslan Bukin #include <sys/systm.h>
36d3916eacSRuslan Bukin #include <sys/cpuset.h>
37d3916eacSRuslan Bukin #include <sys/kernel.h>
38d3916eacSRuslan Bukin #include <sys/linker.h>
39d3916eacSRuslan Bukin #include <sys/lock.h>
40d3916eacSRuslan Bukin #include <sys/malloc.h>
41d3916eacSRuslan Bukin #include <sys/module.h>
42d3916eacSRuslan Bukin #include <sys/mutex.h>
43d3916eacSRuslan Bukin #include <sys/pcpu.h>
44d3916eacSRuslan Bukin #include <sys/proc.h>
45d3916eacSRuslan Bukin #include <sys/queue.h>
46d3916eacSRuslan Bukin #include <sys/rwlock.h>
47d3916eacSRuslan Bukin #include <sys/sched.h>
48d3916eacSRuslan Bukin #include <sys/smp.h>
49d3916eacSRuslan Bukin #include <sys/sysctl.h>
50d3916eacSRuslan Bukin
51d3916eacSRuslan Bukin #include <vm/vm.h>
52d3916eacSRuslan Bukin #include <vm/vm_object.h>
53d3916eacSRuslan Bukin #include <vm/vm_page.h>
54d3916eacSRuslan Bukin #include <vm/pmap.h>
55d3916eacSRuslan Bukin #include <vm/vm_map.h>
56d3916eacSRuslan Bukin #include <vm/vm_extern.h>
57d3916eacSRuslan Bukin #include <vm/vm_param.h>
58d3916eacSRuslan Bukin
59d3916eacSRuslan Bukin #include <machine/riscvreg.h>
60d3916eacSRuslan Bukin #include <machine/cpu.h>
61d3916eacSRuslan Bukin #include <machine/fpe.h>
62d3916eacSRuslan Bukin #include <machine/machdep.h>
63d3916eacSRuslan Bukin #include <machine/pcb.h>
64d3916eacSRuslan Bukin #include <machine/smp.h>
65d3916eacSRuslan Bukin #include <machine/vm.h>
66d3916eacSRuslan Bukin #include <machine/vmparam.h>
67d3916eacSRuslan Bukin #include <machine/vmm.h>
68d3916eacSRuslan Bukin #include <machine/vmm_instruction_emul.h>
69d3916eacSRuslan Bukin
70d3916eacSRuslan Bukin #include <dev/pci/pcireg.h>
71d3916eacSRuslan Bukin
72d3916eacSRuslan Bukin #include <dev/vmm/vmm_dev.h>
73d3916eacSRuslan Bukin #include <dev/vmm/vmm_ktr.h>
74*c76c2a19SMark Johnston #include <dev/vmm/vmm_mem.h>
75d3916eacSRuslan Bukin
76d3916eacSRuslan Bukin #include "vmm_stat.h"
77d3916eacSRuslan Bukin #include "riscv.h"
78d3916eacSRuslan Bukin
79d3916eacSRuslan Bukin #include "vmm_aplic.h"
80d3916eacSRuslan Bukin
81d3916eacSRuslan Bukin struct vcpu {
82d3916eacSRuslan Bukin int flags;
83d3916eacSRuslan Bukin enum vcpu_state state;
84d3916eacSRuslan Bukin struct mtx mtx;
85d3916eacSRuslan Bukin int hostcpu; /* host cpuid this vcpu last ran on */
86d3916eacSRuslan Bukin int vcpuid;
87d3916eacSRuslan Bukin void *stats;
88d3916eacSRuslan Bukin struct vm_exit exitinfo;
89d3916eacSRuslan Bukin uint64_t nextpc; /* (x) next instruction to execute */
90d3916eacSRuslan Bukin struct vm *vm; /* (o) */
91d3916eacSRuslan Bukin void *cookie; /* (i) cpu-specific data */
92d3916eacSRuslan Bukin struct fpreg *guestfpu; /* (a,i) guest fpu state */
93d3916eacSRuslan Bukin };
94d3916eacSRuslan Bukin
95d3916eacSRuslan Bukin #define vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx))
96d3916eacSRuslan Bukin #define vcpu_lock_init(v) mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN)
97d3916eacSRuslan Bukin #define vcpu_lock_destroy(v) mtx_destroy(&((v)->mtx))
98d3916eacSRuslan Bukin #define vcpu_lock(v) mtx_lock_spin(&((v)->mtx))
99d3916eacSRuslan Bukin #define vcpu_unlock(v) mtx_unlock_spin(&((v)->mtx))
100d3916eacSRuslan Bukin #define vcpu_assert_locked(v) mtx_assert(&((v)->mtx), MA_OWNED)
101d3916eacSRuslan Bukin
102d3916eacSRuslan Bukin struct vmm_mmio_region {
103d3916eacSRuslan Bukin uint64_t start;
104d3916eacSRuslan Bukin uint64_t end;
105d3916eacSRuslan Bukin mem_region_read_t read;
106d3916eacSRuslan Bukin mem_region_write_t write;
107d3916eacSRuslan Bukin };
108d3916eacSRuslan Bukin #define VM_MAX_MMIO_REGIONS 4
109d3916eacSRuslan Bukin
110d3916eacSRuslan Bukin /*
111d3916eacSRuslan Bukin * Initialization:
112d3916eacSRuslan Bukin * (o) initialized the first time the VM is created
113d3916eacSRuslan Bukin * (i) initialized when VM is created and when it is reinitialized
114d3916eacSRuslan Bukin * (x) initialized before use
115d3916eacSRuslan Bukin */
116d3916eacSRuslan Bukin struct vm {
117d3916eacSRuslan Bukin void *cookie; /* (i) cpu-specific data */
118d3916eacSRuslan Bukin volatile cpuset_t active_cpus; /* (i) active vcpus */
119d3916eacSRuslan Bukin volatile cpuset_t debug_cpus; /* (i) vcpus stopped for debug*/
120d3916eacSRuslan Bukin int suspend; /* (i) stop VM execution */
121d3916eacSRuslan Bukin bool dying; /* (o) is dying */
122d3916eacSRuslan Bukin volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */
123d3916eacSRuslan Bukin volatile cpuset_t halted_cpus; /* (x) cpus in a hard halt */
124d3916eacSRuslan Bukin struct vmspace *vmspace; /* (o) guest's address space */
125*c76c2a19SMark Johnston struct vm_mem mem; /* (i) [m+v] guest memory */
126d3916eacSRuslan Bukin char name[VM_MAX_NAMELEN]; /* (o) virtual machine name */
127d3916eacSRuslan Bukin struct vcpu **vcpu; /* (i) guest vcpus */
128d3916eacSRuslan Bukin struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS];
129d3916eacSRuslan Bukin /* (o) guest MMIO regions */
130d3916eacSRuslan Bukin /* The following describe the vm cpu topology */
131d3916eacSRuslan Bukin uint16_t sockets; /* (o) num of sockets */
132d3916eacSRuslan Bukin uint16_t cores; /* (o) num of cores/socket */
133d3916eacSRuslan Bukin uint16_t threads; /* (o) num of threads/core */
134d3916eacSRuslan Bukin uint16_t maxcpus; /* (o) max pluggable cpus */
135d3916eacSRuslan Bukin struct sx vcpus_init_lock; /* (o) */
136d3916eacSRuslan Bukin };
137d3916eacSRuslan Bukin
138d3916eacSRuslan Bukin static bool vmm_initialized = false;
139d3916eacSRuslan Bukin
140d3916eacSRuslan Bukin static MALLOC_DEFINE(M_VMM, "vmm", "vmm");
141d3916eacSRuslan Bukin
142d3916eacSRuslan Bukin /* statistics */
143d3916eacSRuslan Bukin static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
144d3916eacSRuslan Bukin
145d3916eacSRuslan Bukin SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
146d3916eacSRuslan Bukin
147d3916eacSRuslan Bukin static int vmm_ipinum;
148d3916eacSRuslan Bukin SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0,
149d3916eacSRuslan Bukin "IPI vector used for vcpu notifications");
150d3916eacSRuslan Bukin
151d3916eacSRuslan Bukin u_int vm_maxcpu;
152d3916eacSRuslan Bukin SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
153d3916eacSRuslan Bukin &vm_maxcpu, 0, "Maximum number of vCPUs");
154d3916eacSRuslan Bukin
155d3916eacSRuslan Bukin static void vcpu_notify_event_locked(struct vcpu *vcpu);
156d3916eacSRuslan Bukin
1570a897e67SMark Johnston /* global statistics */
1580a897e67SMark Johnston VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
1590a897e67SMark Johnston VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
1600a897e67SMark Johnston VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
1610a897e67SMark Johnston
162d3916eacSRuslan Bukin /*
163d3916eacSRuslan Bukin * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this
164d3916eacSRuslan Bukin * is a safe value for now.
165d3916eacSRuslan Bukin */
166d3916eacSRuslan Bukin #define VM_MAXCPU MIN(0xffff - 1, CPU_SETSIZE)
167d3916eacSRuslan Bukin
168d3916eacSRuslan Bukin static void
vcpu_cleanup(struct vcpu * vcpu,bool destroy)169d3916eacSRuslan Bukin vcpu_cleanup(struct vcpu *vcpu, bool destroy)
170d3916eacSRuslan Bukin {
171d3916eacSRuslan Bukin vmmops_vcpu_cleanup(vcpu->cookie);
172d3916eacSRuslan Bukin vcpu->cookie = NULL;
173d3916eacSRuslan Bukin if (destroy) {
174d3916eacSRuslan Bukin vmm_stat_free(vcpu->stats);
175d3916eacSRuslan Bukin fpu_save_area_free(vcpu->guestfpu);
176d3916eacSRuslan Bukin vcpu_lock_destroy(vcpu);
177d3916eacSRuslan Bukin }
178d3916eacSRuslan Bukin }
179d3916eacSRuslan Bukin
180d3916eacSRuslan Bukin static struct vcpu *
vcpu_alloc(struct vm * vm,int vcpu_id)181d3916eacSRuslan Bukin vcpu_alloc(struct vm *vm, int vcpu_id)
182d3916eacSRuslan Bukin {
183d3916eacSRuslan Bukin struct vcpu *vcpu;
184d3916eacSRuslan Bukin
185d3916eacSRuslan Bukin KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus,
186d3916eacSRuslan Bukin ("vcpu_alloc: invalid vcpu %d", vcpu_id));
187d3916eacSRuslan Bukin
188d3916eacSRuslan Bukin vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO);
189d3916eacSRuslan Bukin vcpu_lock_init(vcpu);
190d3916eacSRuslan Bukin vcpu->state = VCPU_IDLE;
191d3916eacSRuslan Bukin vcpu->hostcpu = NOCPU;
192d3916eacSRuslan Bukin vcpu->vcpuid = vcpu_id;
193d3916eacSRuslan Bukin vcpu->vm = vm;
194d3916eacSRuslan Bukin vcpu->guestfpu = fpu_save_area_alloc();
195d3916eacSRuslan Bukin vcpu->stats = vmm_stat_alloc();
196d3916eacSRuslan Bukin return (vcpu);
197d3916eacSRuslan Bukin }
198d3916eacSRuslan Bukin
199d3916eacSRuslan Bukin static void
vcpu_init(struct vcpu * vcpu)200d3916eacSRuslan Bukin vcpu_init(struct vcpu *vcpu)
201d3916eacSRuslan Bukin {
202d3916eacSRuslan Bukin vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid);
203d3916eacSRuslan Bukin MPASS(vcpu->cookie != NULL);
204d3916eacSRuslan Bukin fpu_save_area_reset(vcpu->guestfpu);
205d3916eacSRuslan Bukin vmm_stat_init(vcpu->stats);
206d3916eacSRuslan Bukin }
207d3916eacSRuslan Bukin
208d3916eacSRuslan Bukin struct vm_exit *
vm_exitinfo(struct vcpu * vcpu)209d3916eacSRuslan Bukin vm_exitinfo(struct vcpu *vcpu)
210d3916eacSRuslan Bukin {
211d3916eacSRuslan Bukin return (&vcpu->exitinfo);
212d3916eacSRuslan Bukin }
213d3916eacSRuslan Bukin
214d3916eacSRuslan Bukin static int
vmm_init(void)215d3916eacSRuslan Bukin vmm_init(void)
216d3916eacSRuslan Bukin {
217d3916eacSRuslan Bukin
218d3916eacSRuslan Bukin vm_maxcpu = mp_ncpus;
219d3916eacSRuslan Bukin
220d3916eacSRuslan Bukin TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu);
221d3916eacSRuslan Bukin
222d3916eacSRuslan Bukin if (vm_maxcpu > VM_MAXCPU) {
223d3916eacSRuslan Bukin printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU);
224d3916eacSRuslan Bukin vm_maxcpu = VM_MAXCPU;
225d3916eacSRuslan Bukin }
226d3916eacSRuslan Bukin
227d3916eacSRuslan Bukin if (vm_maxcpu == 0)
228d3916eacSRuslan Bukin vm_maxcpu = 1;
229d3916eacSRuslan Bukin
230d3916eacSRuslan Bukin return (vmmops_modinit());
231d3916eacSRuslan Bukin }
232d3916eacSRuslan Bukin
233d3916eacSRuslan Bukin static int
vmm_handler(module_t mod,int what,void * arg)234d3916eacSRuslan Bukin vmm_handler(module_t mod, int what, void *arg)
235d3916eacSRuslan Bukin {
236d3916eacSRuslan Bukin int error;
237d3916eacSRuslan Bukin
238d3916eacSRuslan Bukin switch (what) {
239d3916eacSRuslan Bukin case MOD_LOAD:
240043999b1SMark Johnston error = vmmdev_init();
241043999b1SMark Johnston if (error != 0)
242043999b1SMark Johnston break;
243d3916eacSRuslan Bukin error = vmm_init();
244d3916eacSRuslan Bukin if (error == 0)
245d3916eacSRuslan Bukin vmm_initialized = true;
2464a46ece6SMark Johnston else
2474a46ece6SMark Johnston (void)vmmdev_cleanup();
248d3916eacSRuslan Bukin break;
249d3916eacSRuslan Bukin case MOD_UNLOAD:
250d3916eacSRuslan Bukin error = vmmdev_cleanup();
251d3916eacSRuslan Bukin if (error == 0 && vmm_initialized) {
252d3916eacSRuslan Bukin error = vmmops_modcleanup();
2534a46ece6SMark Johnston if (error) {
2544a46ece6SMark Johnston /*
2554a46ece6SMark Johnston * Something bad happened - prevent new
2564a46ece6SMark Johnston * VMs from being created
2574a46ece6SMark Johnston */
258d3916eacSRuslan Bukin vmm_initialized = false;
259d3916eacSRuslan Bukin }
2604a46ece6SMark Johnston }
261d3916eacSRuslan Bukin break;
262d3916eacSRuslan Bukin default:
263d3916eacSRuslan Bukin error = 0;
264d3916eacSRuslan Bukin break;
265d3916eacSRuslan Bukin }
266d3916eacSRuslan Bukin return (error);
267d3916eacSRuslan Bukin }
268d3916eacSRuslan Bukin
269d3916eacSRuslan Bukin static moduledata_t vmm_kmod = {
270d3916eacSRuslan Bukin "vmm",
271d3916eacSRuslan Bukin vmm_handler,
272d3916eacSRuslan Bukin NULL
273d3916eacSRuslan Bukin };
274d3916eacSRuslan Bukin
275d3916eacSRuslan Bukin /*
276d3916eacSRuslan Bukin * vmm initialization has the following dependencies:
277d3916eacSRuslan Bukin *
278d7023078SMark Johnston * - vmm device initialization requires an initialized devfs.
279d3916eacSRuslan Bukin */
280d7023078SMark Johnston DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_DEVFS + 1, SI_ORDER_ANY);
281d3916eacSRuslan Bukin MODULE_VERSION(vmm, 1);
282d3916eacSRuslan Bukin
283d3916eacSRuslan Bukin static void
vm_init(struct vm * vm,bool create)284d3916eacSRuslan Bukin vm_init(struct vm *vm, bool create)
285d3916eacSRuslan Bukin {
286d3916eacSRuslan Bukin int i;
287d3916eacSRuslan Bukin
288d3916eacSRuslan Bukin vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace));
289d3916eacSRuslan Bukin MPASS(vm->cookie != NULL);
290d3916eacSRuslan Bukin
291d3916eacSRuslan Bukin CPU_ZERO(&vm->active_cpus);
292d3916eacSRuslan Bukin CPU_ZERO(&vm->debug_cpus);
293d3916eacSRuslan Bukin
294d3916eacSRuslan Bukin vm->suspend = 0;
295d3916eacSRuslan Bukin CPU_ZERO(&vm->suspended_cpus);
296d3916eacSRuslan Bukin
297d3916eacSRuslan Bukin memset(vm->mmio_region, 0, sizeof(vm->mmio_region));
298d3916eacSRuslan Bukin
299d3916eacSRuslan Bukin if (!create) {
300d3916eacSRuslan Bukin for (i = 0; i < vm->maxcpus; i++) {
301d3916eacSRuslan Bukin if (vm->vcpu[i] != NULL)
302d3916eacSRuslan Bukin vcpu_init(vm->vcpu[i]);
303d3916eacSRuslan Bukin }
304d3916eacSRuslan Bukin }
305d3916eacSRuslan Bukin }
306d3916eacSRuslan Bukin
307d3916eacSRuslan Bukin void
vm_disable_vcpu_creation(struct vm * vm)308d3916eacSRuslan Bukin vm_disable_vcpu_creation(struct vm *vm)
309d3916eacSRuslan Bukin {
310d3916eacSRuslan Bukin sx_xlock(&vm->vcpus_init_lock);
311d3916eacSRuslan Bukin vm->dying = true;
312d3916eacSRuslan Bukin sx_xunlock(&vm->vcpus_init_lock);
313d3916eacSRuslan Bukin }
314d3916eacSRuslan Bukin
315d3916eacSRuslan Bukin struct vcpu *
vm_alloc_vcpu(struct vm * vm,int vcpuid)316d3916eacSRuslan Bukin vm_alloc_vcpu(struct vm *vm, int vcpuid)
317d3916eacSRuslan Bukin {
318d3916eacSRuslan Bukin struct vcpu *vcpu;
319d3916eacSRuslan Bukin
320d3916eacSRuslan Bukin if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm))
321d3916eacSRuslan Bukin return (NULL);
322d3916eacSRuslan Bukin
323d3916eacSRuslan Bukin /* Some interrupt controllers may have a CPU limit */
324d3916eacSRuslan Bukin if (vcpuid >= aplic_max_cpu_count(vm->cookie))
325d3916eacSRuslan Bukin return (NULL);
326d3916eacSRuslan Bukin
327d3916eacSRuslan Bukin vcpu = (struct vcpu *)
328d3916eacSRuslan Bukin atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]);
329d3916eacSRuslan Bukin if (__predict_true(vcpu != NULL))
330d3916eacSRuslan Bukin return (vcpu);
331d3916eacSRuslan Bukin
332d3916eacSRuslan Bukin sx_xlock(&vm->vcpus_init_lock);
333d3916eacSRuslan Bukin vcpu = vm->vcpu[vcpuid];
334d3916eacSRuslan Bukin if (vcpu == NULL && !vm->dying) {
335d3916eacSRuslan Bukin vcpu = vcpu_alloc(vm, vcpuid);
336d3916eacSRuslan Bukin vcpu_init(vcpu);
337d3916eacSRuslan Bukin
338d3916eacSRuslan Bukin /*
339d3916eacSRuslan Bukin * Ensure vCPU is fully created before updating pointer
340d3916eacSRuslan Bukin * to permit unlocked reads above.
341d3916eacSRuslan Bukin */
342d3916eacSRuslan Bukin atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid],
343d3916eacSRuslan Bukin (uintptr_t)vcpu);
344d3916eacSRuslan Bukin }
345d3916eacSRuslan Bukin sx_xunlock(&vm->vcpus_init_lock);
346d3916eacSRuslan Bukin return (vcpu);
347d3916eacSRuslan Bukin }
348d3916eacSRuslan Bukin
349d3916eacSRuslan Bukin void
vm_slock_vcpus(struct vm * vm)350d3916eacSRuslan Bukin vm_slock_vcpus(struct vm *vm)
351d3916eacSRuslan Bukin {
352d3916eacSRuslan Bukin sx_slock(&vm->vcpus_init_lock);
353d3916eacSRuslan Bukin }
354d3916eacSRuslan Bukin
355d3916eacSRuslan Bukin void
vm_unlock_vcpus(struct vm * vm)356d3916eacSRuslan Bukin vm_unlock_vcpus(struct vm *vm)
357d3916eacSRuslan Bukin {
358d3916eacSRuslan Bukin sx_unlock(&vm->vcpus_init_lock);
359d3916eacSRuslan Bukin }
360d3916eacSRuslan Bukin
361d3916eacSRuslan Bukin int
vm_create(const char * name,struct vm ** retvm)362d3916eacSRuslan Bukin vm_create(const char *name, struct vm **retvm)
363d3916eacSRuslan Bukin {
364d3916eacSRuslan Bukin struct vm *vm;
365d3916eacSRuslan Bukin struct vmspace *vmspace;
366d3916eacSRuslan Bukin
367d3916eacSRuslan Bukin /*
368d3916eacSRuslan Bukin * If vmm.ko could not be successfully initialized then don't attempt
369d3916eacSRuslan Bukin * to create the virtual machine.
370d3916eacSRuslan Bukin */
371d3916eacSRuslan Bukin if (!vmm_initialized)
372d3916eacSRuslan Bukin return (ENXIO);
373d3916eacSRuslan Bukin
374d3916eacSRuslan Bukin if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
375d3916eacSRuslan Bukin return (EINVAL);
376d3916eacSRuslan Bukin
377d3916eacSRuslan Bukin vmspace = vmmops_vmspace_alloc(0, 1ul << 39);
378d3916eacSRuslan Bukin if (vmspace == NULL)
379d3916eacSRuslan Bukin return (ENOMEM);
380d3916eacSRuslan Bukin
381d3916eacSRuslan Bukin vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO);
382d3916eacSRuslan Bukin strcpy(vm->name, name);
383d3916eacSRuslan Bukin vm->vmspace = vmspace;
384*c76c2a19SMark Johnston vm_mem_init(&vm->mem);
385d3916eacSRuslan Bukin sx_init(&vm->vcpus_init_lock, "vm vcpus");
386d3916eacSRuslan Bukin
387d3916eacSRuslan Bukin vm->sockets = 1;
388d3916eacSRuslan Bukin vm->cores = 1; /* XXX backwards compatibility */
389d3916eacSRuslan Bukin vm->threads = 1; /* XXX backwards compatibility */
390d3916eacSRuslan Bukin vm->maxcpus = vm_maxcpu;
391d3916eacSRuslan Bukin
392d3916eacSRuslan Bukin vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM,
393d3916eacSRuslan Bukin M_WAITOK | M_ZERO);
394d3916eacSRuslan Bukin
395d3916eacSRuslan Bukin vm_init(vm, true);
396d3916eacSRuslan Bukin
397d3916eacSRuslan Bukin *retvm = vm;
398d3916eacSRuslan Bukin return (0);
399d3916eacSRuslan Bukin }
400d3916eacSRuslan Bukin
401d3916eacSRuslan Bukin void
vm_get_topology(struct vm * vm,uint16_t * sockets,uint16_t * cores,uint16_t * threads,uint16_t * maxcpus)402d3916eacSRuslan Bukin vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
403d3916eacSRuslan Bukin uint16_t *threads, uint16_t *maxcpus)
404d3916eacSRuslan Bukin {
405d3916eacSRuslan Bukin *sockets = vm->sockets;
406d3916eacSRuslan Bukin *cores = vm->cores;
407d3916eacSRuslan Bukin *threads = vm->threads;
408d3916eacSRuslan Bukin *maxcpus = vm->maxcpus;
409d3916eacSRuslan Bukin }
410d3916eacSRuslan Bukin
411d3916eacSRuslan Bukin uint16_t
vm_get_maxcpus(struct vm * vm)412d3916eacSRuslan Bukin vm_get_maxcpus(struct vm *vm)
413d3916eacSRuslan Bukin {
414d3916eacSRuslan Bukin return (vm->maxcpus);
415d3916eacSRuslan Bukin }
416d3916eacSRuslan Bukin
417d3916eacSRuslan Bukin int
vm_set_topology(struct vm * vm,uint16_t sockets,uint16_t cores,uint16_t threads,uint16_t maxcpus)418d3916eacSRuslan Bukin vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
419d3916eacSRuslan Bukin uint16_t threads, uint16_t maxcpus)
420d3916eacSRuslan Bukin {
421d3916eacSRuslan Bukin /* Ignore maxcpus. */
422d3916eacSRuslan Bukin if ((sockets * cores * threads) > vm->maxcpus)
423d3916eacSRuslan Bukin return (EINVAL);
424d3916eacSRuslan Bukin vm->sockets = sockets;
425d3916eacSRuslan Bukin vm->cores = cores;
426d3916eacSRuslan Bukin vm->threads = threads;
427d3916eacSRuslan Bukin return(0);
428d3916eacSRuslan Bukin }
429d3916eacSRuslan Bukin
430d3916eacSRuslan Bukin static void
vm_cleanup(struct vm * vm,bool destroy)431d3916eacSRuslan Bukin vm_cleanup(struct vm *vm, bool destroy)
432d3916eacSRuslan Bukin {
433d3916eacSRuslan Bukin int i;
434d3916eacSRuslan Bukin
435*c76c2a19SMark Johnston if (destroy)
436*c76c2a19SMark Johnston vm_xlock_memsegs(vm);
437*c76c2a19SMark Johnston else
438*c76c2a19SMark Johnston vm_assert_memseg_xlocked(vm);
439*c76c2a19SMark Johnston
440d3916eacSRuslan Bukin aplic_detach_from_vm(vm->cookie);
441d3916eacSRuslan Bukin
442d3916eacSRuslan Bukin for (i = 0; i < vm->maxcpus; i++) {
443d3916eacSRuslan Bukin if (vm->vcpu[i] != NULL)
444d3916eacSRuslan Bukin vcpu_cleanup(vm->vcpu[i], destroy);
445d3916eacSRuslan Bukin }
446d3916eacSRuslan Bukin
447d3916eacSRuslan Bukin vmmops_cleanup(vm->cookie);
448d3916eacSRuslan Bukin
449*c76c2a19SMark Johnston vm_mem_cleanup(vm);
450d3916eacSRuslan Bukin if (destroy) {
451*c76c2a19SMark Johnston vm_mem_destroy(vm);
452d3916eacSRuslan Bukin
453d3916eacSRuslan Bukin vmmops_vmspace_free(vm->vmspace);
454d3916eacSRuslan Bukin vm->vmspace = NULL;
455d3916eacSRuslan Bukin
456d3916eacSRuslan Bukin for (i = 0; i < vm->maxcpus; i++)
457d3916eacSRuslan Bukin free(vm->vcpu[i], M_VMM);
458d3916eacSRuslan Bukin free(vm->vcpu, M_VMM);
459d3916eacSRuslan Bukin sx_destroy(&vm->vcpus_init_lock);
460d3916eacSRuslan Bukin }
461d3916eacSRuslan Bukin }
462d3916eacSRuslan Bukin
463d3916eacSRuslan Bukin void
vm_destroy(struct vm * vm)464d3916eacSRuslan Bukin vm_destroy(struct vm *vm)
465d3916eacSRuslan Bukin {
466d3916eacSRuslan Bukin
467d3916eacSRuslan Bukin vm_cleanup(vm, true);
468d3916eacSRuslan Bukin
469d3916eacSRuslan Bukin free(vm, M_VMM);
470d3916eacSRuslan Bukin }
471d3916eacSRuslan Bukin
472d3916eacSRuslan Bukin int
vm_reinit(struct vm * vm)473d3916eacSRuslan Bukin vm_reinit(struct vm *vm)
474d3916eacSRuslan Bukin {
475d3916eacSRuslan Bukin int error;
476d3916eacSRuslan Bukin
477d3916eacSRuslan Bukin /*
478d3916eacSRuslan Bukin * A virtual machine can be reset only if all vcpus are suspended.
479d3916eacSRuslan Bukin */
480d3916eacSRuslan Bukin if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) {
481d3916eacSRuslan Bukin vm_cleanup(vm, false);
482d3916eacSRuslan Bukin vm_init(vm, false);
483d3916eacSRuslan Bukin error = 0;
484d3916eacSRuslan Bukin } else {
485d3916eacSRuslan Bukin error = EBUSY;
486d3916eacSRuslan Bukin }
487d3916eacSRuslan Bukin
488d3916eacSRuslan Bukin return (error);
489d3916eacSRuslan Bukin }
490d3916eacSRuslan Bukin
491d3916eacSRuslan Bukin const char *
vm_name(struct vm * vm)492d3916eacSRuslan Bukin vm_name(struct vm *vm)
493d3916eacSRuslan Bukin {
494d3916eacSRuslan Bukin return (vm->name);
495d3916eacSRuslan Bukin }
496d3916eacSRuslan Bukin
497d3916eacSRuslan Bukin int
vm_gla2gpa_nofault(struct vcpu * vcpu,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * is_fault)498d3916eacSRuslan Bukin vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging,
499d3916eacSRuslan Bukin uint64_t gla, int prot, uint64_t *gpa, int *is_fault)
500d3916eacSRuslan Bukin {
501*c76c2a19SMark Johnston return (vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault));
502d3916eacSRuslan Bukin }
503d3916eacSRuslan Bukin
504d3916eacSRuslan Bukin void
vm_register_inst_handler(struct vm * vm,uint64_t start,uint64_t size,mem_region_read_t mmio_read,mem_region_write_t mmio_write)505d3916eacSRuslan Bukin vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size,
506d3916eacSRuslan Bukin mem_region_read_t mmio_read, mem_region_write_t mmio_write)
507d3916eacSRuslan Bukin {
508d3916eacSRuslan Bukin int i;
509d3916eacSRuslan Bukin
510d3916eacSRuslan Bukin for (i = 0; i < nitems(vm->mmio_region); i++) {
511d3916eacSRuslan Bukin if (vm->mmio_region[i].start == 0 &&
512d3916eacSRuslan Bukin vm->mmio_region[i].end == 0) {
513d3916eacSRuslan Bukin vm->mmio_region[i].start = start;
514d3916eacSRuslan Bukin vm->mmio_region[i].end = start + size;
515d3916eacSRuslan Bukin vm->mmio_region[i].read = mmio_read;
516d3916eacSRuslan Bukin vm->mmio_region[i].write = mmio_write;
517d3916eacSRuslan Bukin return;
518d3916eacSRuslan Bukin }
519d3916eacSRuslan Bukin }
520d3916eacSRuslan Bukin
521d3916eacSRuslan Bukin panic("%s: No free MMIO region", __func__);
522d3916eacSRuslan Bukin }
523d3916eacSRuslan Bukin
524d3916eacSRuslan Bukin void
vm_deregister_inst_handler(struct vm * vm,uint64_t start,uint64_t size)525d3916eacSRuslan Bukin vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size)
526d3916eacSRuslan Bukin {
527d3916eacSRuslan Bukin int i;
528d3916eacSRuslan Bukin
529d3916eacSRuslan Bukin for (i = 0; i < nitems(vm->mmio_region); i++) {
530d3916eacSRuslan Bukin if (vm->mmio_region[i].start == start &&
531d3916eacSRuslan Bukin vm->mmio_region[i].end == start + size) {
532d3916eacSRuslan Bukin memset(&vm->mmio_region[i], 0,
533d3916eacSRuslan Bukin sizeof(vm->mmio_region[i]));
534d3916eacSRuslan Bukin return;
535d3916eacSRuslan Bukin }
536d3916eacSRuslan Bukin }
537d3916eacSRuslan Bukin
538d3916eacSRuslan Bukin panic("%s: Invalid MMIO region: %lx - %lx", __func__, start,
539d3916eacSRuslan Bukin start + size);
540d3916eacSRuslan Bukin }
541d3916eacSRuslan Bukin
542d3916eacSRuslan Bukin static int
vm_handle_inst_emul(struct vcpu * vcpu,bool * retu)543d3916eacSRuslan Bukin vm_handle_inst_emul(struct vcpu *vcpu, bool *retu)
544d3916eacSRuslan Bukin {
545d3916eacSRuslan Bukin struct vm *vm;
546d3916eacSRuslan Bukin struct vm_exit *vme;
547d3916eacSRuslan Bukin struct vie *vie;
548d3916eacSRuslan Bukin struct hyp *hyp;
549d3916eacSRuslan Bukin uint64_t fault_ipa;
550d3916eacSRuslan Bukin struct vm_guest_paging *paging;
551d3916eacSRuslan Bukin struct vmm_mmio_region *vmr;
552d3916eacSRuslan Bukin int error, i;
553d3916eacSRuslan Bukin
554d3916eacSRuslan Bukin vm = vcpu->vm;
555d3916eacSRuslan Bukin hyp = vm->cookie;
556d3916eacSRuslan Bukin if (!hyp->aplic_attached)
557d3916eacSRuslan Bukin goto out_user;
558d3916eacSRuslan Bukin
559d3916eacSRuslan Bukin vme = &vcpu->exitinfo;
560d3916eacSRuslan Bukin vie = &vme->u.inst_emul.vie;
561d3916eacSRuslan Bukin paging = &vme->u.inst_emul.paging;
562d3916eacSRuslan Bukin
563d3916eacSRuslan Bukin fault_ipa = vme->u.inst_emul.gpa;
564d3916eacSRuslan Bukin
565d3916eacSRuslan Bukin vmr = NULL;
566d3916eacSRuslan Bukin for (i = 0; i < nitems(vm->mmio_region); i++) {
567d3916eacSRuslan Bukin if (vm->mmio_region[i].start <= fault_ipa &&
568d3916eacSRuslan Bukin vm->mmio_region[i].end > fault_ipa) {
569d3916eacSRuslan Bukin vmr = &vm->mmio_region[i];
570d3916eacSRuslan Bukin break;
571d3916eacSRuslan Bukin }
572d3916eacSRuslan Bukin }
573d3916eacSRuslan Bukin if (vmr == NULL)
574d3916eacSRuslan Bukin goto out_user;
575d3916eacSRuslan Bukin
576d3916eacSRuslan Bukin error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging,
577d3916eacSRuslan Bukin vmr->read, vmr->write, retu);
578d3916eacSRuslan Bukin return (error);
579d3916eacSRuslan Bukin
580d3916eacSRuslan Bukin out_user:
581d3916eacSRuslan Bukin *retu = true;
582d3916eacSRuslan Bukin return (0);
583d3916eacSRuslan Bukin }
584d3916eacSRuslan Bukin
585d3916eacSRuslan Bukin int
vm_suspend(struct vm * vm,enum vm_suspend_how how)586d3916eacSRuslan Bukin vm_suspend(struct vm *vm, enum vm_suspend_how how)
587d3916eacSRuslan Bukin {
588d3916eacSRuslan Bukin int i;
589d3916eacSRuslan Bukin
590d3916eacSRuslan Bukin if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
591d3916eacSRuslan Bukin return (EINVAL);
592d3916eacSRuslan Bukin
593d3916eacSRuslan Bukin if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
594d3916eacSRuslan Bukin VM_CTR2(vm, "virtual machine already suspended %d/%d",
595d3916eacSRuslan Bukin vm->suspend, how);
596d3916eacSRuslan Bukin return (EALREADY);
597d3916eacSRuslan Bukin }
598d3916eacSRuslan Bukin
599d3916eacSRuslan Bukin VM_CTR1(vm, "virtual machine successfully suspended %d", how);
600d3916eacSRuslan Bukin
601d3916eacSRuslan Bukin /*
602d3916eacSRuslan Bukin * Notify all active vcpus that they are now suspended.
603d3916eacSRuslan Bukin */
604d3916eacSRuslan Bukin for (i = 0; i < vm->maxcpus; i++) {
605d3916eacSRuslan Bukin if (CPU_ISSET(i, &vm->active_cpus))
606d3916eacSRuslan Bukin vcpu_notify_event(vm_vcpu(vm, i));
607d3916eacSRuslan Bukin }
608d3916eacSRuslan Bukin
609d3916eacSRuslan Bukin return (0);
610d3916eacSRuslan Bukin }
611d3916eacSRuslan Bukin
612d3916eacSRuslan Bukin void
vm_exit_suspended(struct vcpu * vcpu,uint64_t pc)613d3916eacSRuslan Bukin vm_exit_suspended(struct vcpu *vcpu, uint64_t pc)
614d3916eacSRuslan Bukin {
615d3916eacSRuslan Bukin struct vm *vm = vcpu->vm;
616d3916eacSRuslan Bukin struct vm_exit *vmexit;
617d3916eacSRuslan Bukin
618d3916eacSRuslan Bukin KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
619d3916eacSRuslan Bukin ("vm_exit_suspended: invalid suspend type %d", vm->suspend));
620d3916eacSRuslan Bukin
621d3916eacSRuslan Bukin vmexit = vm_exitinfo(vcpu);
622d3916eacSRuslan Bukin vmexit->pc = pc;
623d3916eacSRuslan Bukin vmexit->inst_length = 4;
624d3916eacSRuslan Bukin vmexit->exitcode = VM_EXITCODE_SUSPENDED;
625d3916eacSRuslan Bukin vmexit->u.suspended.how = vm->suspend;
626d3916eacSRuslan Bukin }
627d3916eacSRuslan Bukin
628d3916eacSRuslan Bukin void
vm_exit_debug(struct vcpu * vcpu,uint64_t pc)629d3916eacSRuslan Bukin vm_exit_debug(struct vcpu *vcpu, uint64_t pc)
630d3916eacSRuslan Bukin {
631d3916eacSRuslan Bukin struct vm_exit *vmexit;
632d3916eacSRuslan Bukin
633d3916eacSRuslan Bukin vmexit = vm_exitinfo(vcpu);
634d3916eacSRuslan Bukin vmexit->pc = pc;
635d3916eacSRuslan Bukin vmexit->inst_length = 4;
636d3916eacSRuslan Bukin vmexit->exitcode = VM_EXITCODE_DEBUG;
637d3916eacSRuslan Bukin }
638d3916eacSRuslan Bukin
639d3916eacSRuslan Bukin int
vm_activate_cpu(struct vcpu * vcpu)640d3916eacSRuslan Bukin vm_activate_cpu(struct vcpu *vcpu)
641d3916eacSRuslan Bukin {
642d3916eacSRuslan Bukin struct vm *vm = vcpu->vm;
643d3916eacSRuslan Bukin
644d3916eacSRuslan Bukin if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
645d3916eacSRuslan Bukin return (EBUSY);
646d3916eacSRuslan Bukin
647d3916eacSRuslan Bukin CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus);
648d3916eacSRuslan Bukin return (0);
649d3916eacSRuslan Bukin
650d3916eacSRuslan Bukin }
651d3916eacSRuslan Bukin
652d3916eacSRuslan Bukin int
vm_suspend_cpu(struct vm * vm,struct vcpu * vcpu)653d3916eacSRuslan Bukin vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu)
654d3916eacSRuslan Bukin {
655d3916eacSRuslan Bukin if (vcpu == NULL) {
656d3916eacSRuslan Bukin vm->debug_cpus = vm->active_cpus;
657d3916eacSRuslan Bukin for (int i = 0; i < vm->maxcpus; i++) {
658d3916eacSRuslan Bukin if (CPU_ISSET(i, &vm->active_cpus))
659d3916eacSRuslan Bukin vcpu_notify_event(vm_vcpu(vm, i));
660d3916eacSRuslan Bukin }
661d3916eacSRuslan Bukin } else {
662d3916eacSRuslan Bukin if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
663d3916eacSRuslan Bukin return (EINVAL);
664d3916eacSRuslan Bukin
665d3916eacSRuslan Bukin CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
666d3916eacSRuslan Bukin vcpu_notify_event(vcpu);
667d3916eacSRuslan Bukin }
668d3916eacSRuslan Bukin return (0);
669d3916eacSRuslan Bukin }
670d3916eacSRuslan Bukin
671d3916eacSRuslan Bukin int
vm_resume_cpu(struct vm * vm,struct vcpu * vcpu)672d3916eacSRuslan Bukin vm_resume_cpu(struct vm *vm, struct vcpu *vcpu)
673d3916eacSRuslan Bukin {
674d3916eacSRuslan Bukin
675d3916eacSRuslan Bukin if (vcpu == NULL) {
676d3916eacSRuslan Bukin CPU_ZERO(&vm->debug_cpus);
677d3916eacSRuslan Bukin } else {
678d3916eacSRuslan Bukin if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus))
679d3916eacSRuslan Bukin return (EINVAL);
680d3916eacSRuslan Bukin
681d3916eacSRuslan Bukin CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
682d3916eacSRuslan Bukin }
683d3916eacSRuslan Bukin return (0);
684d3916eacSRuslan Bukin }
685d3916eacSRuslan Bukin
686d3916eacSRuslan Bukin int
vcpu_debugged(struct vcpu * vcpu)687d3916eacSRuslan Bukin vcpu_debugged(struct vcpu *vcpu)
688d3916eacSRuslan Bukin {
689d3916eacSRuslan Bukin
690d3916eacSRuslan Bukin return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus));
691d3916eacSRuslan Bukin }
692d3916eacSRuslan Bukin
693d3916eacSRuslan Bukin cpuset_t
vm_active_cpus(struct vm * vm)694d3916eacSRuslan Bukin vm_active_cpus(struct vm *vm)
695d3916eacSRuslan Bukin {
696d3916eacSRuslan Bukin
697d3916eacSRuslan Bukin return (vm->active_cpus);
698d3916eacSRuslan Bukin }
699d3916eacSRuslan Bukin
700d3916eacSRuslan Bukin cpuset_t
vm_debug_cpus(struct vm * vm)701d3916eacSRuslan Bukin vm_debug_cpus(struct vm *vm)
702d3916eacSRuslan Bukin {
703d3916eacSRuslan Bukin
704d3916eacSRuslan Bukin return (vm->debug_cpus);
705d3916eacSRuslan Bukin }
706d3916eacSRuslan Bukin
707d3916eacSRuslan Bukin cpuset_t
vm_suspended_cpus(struct vm * vm)708d3916eacSRuslan Bukin vm_suspended_cpus(struct vm *vm)
709d3916eacSRuslan Bukin {
710d3916eacSRuslan Bukin
711d3916eacSRuslan Bukin return (vm->suspended_cpus);
712d3916eacSRuslan Bukin }
713d3916eacSRuslan Bukin
714d3916eacSRuslan Bukin
715d3916eacSRuslan Bukin void *
vcpu_stats(struct vcpu * vcpu)716d3916eacSRuslan Bukin vcpu_stats(struct vcpu *vcpu)
717d3916eacSRuslan Bukin {
718d3916eacSRuslan Bukin
719d3916eacSRuslan Bukin return (vcpu->stats);
720d3916eacSRuslan Bukin }
721d3916eacSRuslan Bukin
722d3916eacSRuslan Bukin /*
723d3916eacSRuslan Bukin * This function is called to ensure that a vcpu "sees" a pending event
724d3916eacSRuslan Bukin * as soon as possible:
725d3916eacSRuslan Bukin * - If the vcpu thread is sleeping then it is woken up.
726d3916eacSRuslan Bukin * - If the vcpu is running on a different host_cpu then an IPI will be directed
727d3916eacSRuslan Bukin * to the host_cpu to cause the vcpu to trap into the hypervisor.
728d3916eacSRuslan Bukin */
729d3916eacSRuslan Bukin static void
vcpu_notify_event_locked(struct vcpu * vcpu)730d3916eacSRuslan Bukin vcpu_notify_event_locked(struct vcpu *vcpu)
731d3916eacSRuslan Bukin {
732d3916eacSRuslan Bukin int hostcpu;
733d3916eacSRuslan Bukin
734d3916eacSRuslan Bukin hostcpu = vcpu->hostcpu;
735d3916eacSRuslan Bukin if (vcpu->state == VCPU_RUNNING) {
736d3916eacSRuslan Bukin KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu"));
737d3916eacSRuslan Bukin if (hostcpu != curcpu) {
738d3916eacSRuslan Bukin ipi_cpu(hostcpu, vmm_ipinum);
739d3916eacSRuslan Bukin } else {
740d3916eacSRuslan Bukin /*
741d3916eacSRuslan Bukin * If the 'vcpu' is running on 'curcpu' then it must
742d3916eacSRuslan Bukin * be sending a notification to itself (e.g. SELF_IPI).
743d3916eacSRuslan Bukin * The pending event will be picked up when the vcpu
744d3916eacSRuslan Bukin * transitions back to guest context.
745d3916eacSRuslan Bukin */
746d3916eacSRuslan Bukin }
747d3916eacSRuslan Bukin } else {
748d3916eacSRuslan Bukin KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent "
749d3916eacSRuslan Bukin "with hostcpu %d", vcpu->state, hostcpu));
750d3916eacSRuslan Bukin if (vcpu->state == VCPU_SLEEPING)
751d3916eacSRuslan Bukin wakeup_one(vcpu);
752d3916eacSRuslan Bukin }
753d3916eacSRuslan Bukin }
754d3916eacSRuslan Bukin
755d3916eacSRuslan Bukin void
vcpu_notify_event(struct vcpu * vcpu)756d3916eacSRuslan Bukin vcpu_notify_event(struct vcpu *vcpu)
757d3916eacSRuslan Bukin {
758d3916eacSRuslan Bukin vcpu_lock(vcpu);
759d3916eacSRuslan Bukin vcpu_notify_event_locked(vcpu);
760d3916eacSRuslan Bukin vcpu_unlock(vcpu);
761d3916eacSRuslan Bukin }
762d3916eacSRuslan Bukin
763*c76c2a19SMark Johnston struct vmspace *
vm_vmspace(struct vm * vm)764*c76c2a19SMark Johnston vm_vmspace(struct vm *vm)
765*c76c2a19SMark Johnston {
766*c76c2a19SMark Johnston return (vm->vmspace);
767*c76c2a19SMark Johnston }
768*c76c2a19SMark Johnston
769*c76c2a19SMark Johnston struct vm_mem *
vm_mem(struct vm * vm)770*c76c2a19SMark Johnston vm_mem(struct vm *vm)
771*c76c2a19SMark Johnston {
772*c76c2a19SMark Johnston return (&vm->mem);
773*c76c2a19SMark Johnston }
774*c76c2a19SMark Johnston
775d3916eacSRuslan Bukin static void
restore_guest_fpustate(struct vcpu * vcpu)776d3916eacSRuslan Bukin restore_guest_fpustate(struct vcpu *vcpu)
777d3916eacSRuslan Bukin {
778d3916eacSRuslan Bukin
779d3916eacSRuslan Bukin /* Flush host state to the pcb. */
780d3916eacSRuslan Bukin fpe_state_save(curthread);
781d3916eacSRuslan Bukin
782d3916eacSRuslan Bukin /* Ensure the VFP state will be re-loaded when exiting the guest. */
783d3916eacSRuslan Bukin PCPU_SET(fpcurthread, NULL);
784d3916eacSRuslan Bukin
785d3916eacSRuslan Bukin /* restore guest FPU state */
786d3916eacSRuslan Bukin fpe_enable();
787d3916eacSRuslan Bukin fpe_restore(vcpu->guestfpu);
788d3916eacSRuslan Bukin
789d3916eacSRuslan Bukin /*
790d3916eacSRuslan Bukin * The FPU is now "dirty" with the guest's state so turn on emulation
791d3916eacSRuslan Bukin * to trap any access to the FPU by the host.
792d3916eacSRuslan Bukin */
793d3916eacSRuslan Bukin fpe_disable();
794d3916eacSRuslan Bukin }
795d3916eacSRuslan Bukin
796d3916eacSRuslan Bukin static void
save_guest_fpustate(struct vcpu * vcpu)797d3916eacSRuslan Bukin save_guest_fpustate(struct vcpu *vcpu)
798d3916eacSRuslan Bukin {
799d3916eacSRuslan Bukin
800d3916eacSRuslan Bukin /* Save guest FPE state. */
801d3916eacSRuslan Bukin fpe_enable();
802d3916eacSRuslan Bukin fpe_store(vcpu->guestfpu);
803d3916eacSRuslan Bukin fpe_disable();
804d3916eacSRuslan Bukin
805d3916eacSRuslan Bukin KASSERT(PCPU_GET(fpcurthread) == NULL,
806d3916eacSRuslan Bukin ("%s: fpcurthread set with guest registers", __func__));
807d3916eacSRuslan Bukin }
808d3916eacSRuslan Bukin
809d3916eacSRuslan Bukin static int
vcpu_set_state_locked(struct vcpu * vcpu,enum vcpu_state newstate,bool from_idle)810d3916eacSRuslan Bukin vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate,
811d3916eacSRuslan Bukin bool from_idle)
812d3916eacSRuslan Bukin {
813d3916eacSRuslan Bukin int error;
814d3916eacSRuslan Bukin
815d3916eacSRuslan Bukin vcpu_assert_locked(vcpu);
816d3916eacSRuslan Bukin
817d3916eacSRuslan Bukin /*
818d3916eacSRuslan Bukin * State transitions from the vmmdev_ioctl() must always begin from
819d3916eacSRuslan Bukin * the VCPU_IDLE state. This guarantees that there is only a single
820d3916eacSRuslan Bukin * ioctl() operating on a vcpu at any point.
821d3916eacSRuslan Bukin */
822d3916eacSRuslan Bukin if (from_idle) {
823d3916eacSRuslan Bukin while (vcpu->state != VCPU_IDLE) {
824d3916eacSRuslan Bukin vcpu_notify_event_locked(vcpu);
825215c8b79SMark Johnston msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz);
826d3916eacSRuslan Bukin }
827d3916eacSRuslan Bukin } else {
828d3916eacSRuslan Bukin KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from "
829d3916eacSRuslan Bukin "vcpu idle state"));
830d3916eacSRuslan Bukin }
831d3916eacSRuslan Bukin
832d3916eacSRuslan Bukin if (vcpu->state == VCPU_RUNNING) {
833d3916eacSRuslan Bukin KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d "
834d3916eacSRuslan Bukin "mismatch for running vcpu", curcpu, vcpu->hostcpu));
835d3916eacSRuslan Bukin } else {
836d3916eacSRuslan Bukin KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a "
837d3916eacSRuslan Bukin "vcpu that is not running", vcpu->hostcpu));
838d3916eacSRuslan Bukin }
839d3916eacSRuslan Bukin
840d3916eacSRuslan Bukin /*
841d3916eacSRuslan Bukin * The following state transitions are allowed:
842d3916eacSRuslan Bukin * IDLE -> FROZEN -> IDLE
843d3916eacSRuslan Bukin * FROZEN -> RUNNING -> FROZEN
844d3916eacSRuslan Bukin * FROZEN -> SLEEPING -> FROZEN
845d3916eacSRuslan Bukin */
846d3916eacSRuslan Bukin switch (vcpu->state) {
847d3916eacSRuslan Bukin case VCPU_IDLE:
848d3916eacSRuslan Bukin case VCPU_RUNNING:
849d3916eacSRuslan Bukin case VCPU_SLEEPING:
850d3916eacSRuslan Bukin error = (newstate != VCPU_FROZEN);
851d3916eacSRuslan Bukin break;
852d3916eacSRuslan Bukin case VCPU_FROZEN:
853d3916eacSRuslan Bukin error = (newstate == VCPU_FROZEN);
854d3916eacSRuslan Bukin break;
855d3916eacSRuslan Bukin default:
856d3916eacSRuslan Bukin error = 1;
857d3916eacSRuslan Bukin break;
858d3916eacSRuslan Bukin }
859d3916eacSRuslan Bukin
860d3916eacSRuslan Bukin if (error)
861d3916eacSRuslan Bukin return (EBUSY);
862d3916eacSRuslan Bukin
863d3916eacSRuslan Bukin vcpu->state = newstate;
864d3916eacSRuslan Bukin if (newstate == VCPU_RUNNING)
865d3916eacSRuslan Bukin vcpu->hostcpu = curcpu;
866d3916eacSRuslan Bukin else
867d3916eacSRuslan Bukin vcpu->hostcpu = NOCPU;
868d3916eacSRuslan Bukin
869d3916eacSRuslan Bukin if (newstate == VCPU_IDLE)
870d3916eacSRuslan Bukin wakeup(&vcpu->state);
871d3916eacSRuslan Bukin
872d3916eacSRuslan Bukin return (0);
873d3916eacSRuslan Bukin }
874d3916eacSRuslan Bukin
875d3916eacSRuslan Bukin static void
vcpu_require_state(struct vcpu * vcpu,enum vcpu_state newstate)876d3916eacSRuslan Bukin vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate)
877d3916eacSRuslan Bukin {
878d3916eacSRuslan Bukin int error;
879d3916eacSRuslan Bukin
880d3916eacSRuslan Bukin if ((error = vcpu_set_state(vcpu, newstate, false)) != 0)
881d3916eacSRuslan Bukin panic("Error %d setting state to %d\n", error, newstate);
882d3916eacSRuslan Bukin }
883d3916eacSRuslan Bukin
884d3916eacSRuslan Bukin static void
vcpu_require_state_locked(struct vcpu * vcpu,enum vcpu_state newstate)885d3916eacSRuslan Bukin vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate)
886d3916eacSRuslan Bukin {
887d3916eacSRuslan Bukin int error;
888d3916eacSRuslan Bukin
889d3916eacSRuslan Bukin if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0)
890d3916eacSRuslan Bukin panic("Error %d setting state to %d", error, newstate);
891d3916eacSRuslan Bukin }
892d3916eacSRuslan Bukin
893d3916eacSRuslan Bukin int
vm_get_capability(struct vcpu * vcpu,int type,int * retval)894d3916eacSRuslan Bukin vm_get_capability(struct vcpu *vcpu, int type, int *retval)
895d3916eacSRuslan Bukin {
896d3916eacSRuslan Bukin
897d3916eacSRuslan Bukin if (type < 0 || type >= VM_CAP_MAX)
898d3916eacSRuslan Bukin return (EINVAL);
899d3916eacSRuslan Bukin
900d3916eacSRuslan Bukin return (vmmops_getcap(vcpu->cookie, type, retval));
901d3916eacSRuslan Bukin }
902d3916eacSRuslan Bukin
903d3916eacSRuslan Bukin int
vm_set_capability(struct vcpu * vcpu,int type,int val)904d3916eacSRuslan Bukin vm_set_capability(struct vcpu *vcpu, int type, int val)
905d3916eacSRuslan Bukin {
906d3916eacSRuslan Bukin
907d3916eacSRuslan Bukin if (type < 0 || type >= VM_CAP_MAX)
908d3916eacSRuslan Bukin return (EINVAL);
909d3916eacSRuslan Bukin
910d3916eacSRuslan Bukin return (vmmops_setcap(vcpu->cookie, type, val));
911d3916eacSRuslan Bukin }
912d3916eacSRuslan Bukin
913d3916eacSRuslan Bukin struct vm *
vcpu_vm(struct vcpu * vcpu)914d3916eacSRuslan Bukin vcpu_vm(struct vcpu *vcpu)
915d3916eacSRuslan Bukin {
916d3916eacSRuslan Bukin
917d3916eacSRuslan Bukin return (vcpu->vm);
918d3916eacSRuslan Bukin }
919d3916eacSRuslan Bukin
920d3916eacSRuslan Bukin int
vcpu_vcpuid(struct vcpu * vcpu)921d3916eacSRuslan Bukin vcpu_vcpuid(struct vcpu *vcpu)
922d3916eacSRuslan Bukin {
923d3916eacSRuslan Bukin
924d3916eacSRuslan Bukin return (vcpu->vcpuid);
925d3916eacSRuslan Bukin }
926d3916eacSRuslan Bukin
927d3916eacSRuslan Bukin void *
vcpu_get_cookie(struct vcpu * vcpu)928d3916eacSRuslan Bukin vcpu_get_cookie(struct vcpu *vcpu)
929d3916eacSRuslan Bukin {
930d3916eacSRuslan Bukin
931d3916eacSRuslan Bukin return (vcpu->cookie);
932d3916eacSRuslan Bukin }
933d3916eacSRuslan Bukin
934d3916eacSRuslan Bukin struct vcpu *
vm_vcpu(struct vm * vm,int vcpuid)935d3916eacSRuslan Bukin vm_vcpu(struct vm *vm, int vcpuid)
936d3916eacSRuslan Bukin {
937d3916eacSRuslan Bukin
938d3916eacSRuslan Bukin return (vm->vcpu[vcpuid]);
939d3916eacSRuslan Bukin }
940d3916eacSRuslan Bukin
941d3916eacSRuslan Bukin int
vcpu_set_state(struct vcpu * vcpu,enum vcpu_state newstate,bool from_idle)942d3916eacSRuslan Bukin vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle)
943d3916eacSRuslan Bukin {
944d3916eacSRuslan Bukin int error;
945d3916eacSRuslan Bukin
946d3916eacSRuslan Bukin vcpu_lock(vcpu);
947d3916eacSRuslan Bukin error = vcpu_set_state_locked(vcpu, newstate, from_idle);
948d3916eacSRuslan Bukin vcpu_unlock(vcpu);
949d3916eacSRuslan Bukin
950d3916eacSRuslan Bukin return (error);
951d3916eacSRuslan Bukin }
952d3916eacSRuslan Bukin
953d3916eacSRuslan Bukin enum vcpu_state
vcpu_get_state(struct vcpu * vcpu,int * hostcpu)954d3916eacSRuslan Bukin vcpu_get_state(struct vcpu *vcpu, int *hostcpu)
955d3916eacSRuslan Bukin {
956d3916eacSRuslan Bukin enum vcpu_state state;
957d3916eacSRuslan Bukin
958d3916eacSRuslan Bukin vcpu_lock(vcpu);
959d3916eacSRuslan Bukin state = vcpu->state;
960d3916eacSRuslan Bukin if (hostcpu != NULL)
961d3916eacSRuslan Bukin *hostcpu = vcpu->hostcpu;
962d3916eacSRuslan Bukin vcpu_unlock(vcpu);
963d3916eacSRuslan Bukin
964d3916eacSRuslan Bukin return (state);
965d3916eacSRuslan Bukin }
966d3916eacSRuslan Bukin
967d3916eacSRuslan Bukin int
vm_get_register(struct vcpu * vcpu,int reg,uint64_t * retval)968d3916eacSRuslan Bukin vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
969d3916eacSRuslan Bukin {
970d3916eacSRuslan Bukin
971d3916eacSRuslan Bukin if (reg >= VM_REG_LAST)
972d3916eacSRuslan Bukin return (EINVAL);
973d3916eacSRuslan Bukin
974d3916eacSRuslan Bukin return (vmmops_getreg(vcpu->cookie, reg, retval));
975d3916eacSRuslan Bukin }
976d3916eacSRuslan Bukin
977d3916eacSRuslan Bukin int
vm_set_register(struct vcpu * vcpu,int reg,uint64_t val)978d3916eacSRuslan Bukin vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
979d3916eacSRuslan Bukin {
980d3916eacSRuslan Bukin int error;
981d3916eacSRuslan Bukin
982d3916eacSRuslan Bukin if (reg >= VM_REG_LAST)
983d3916eacSRuslan Bukin return (EINVAL);
984d3916eacSRuslan Bukin error = vmmops_setreg(vcpu->cookie, reg, val);
985d3916eacSRuslan Bukin if (error || reg != VM_REG_GUEST_SEPC)
986d3916eacSRuslan Bukin return (error);
987d3916eacSRuslan Bukin
988d3916eacSRuslan Bukin vcpu->nextpc = val;
989d3916eacSRuslan Bukin
990d3916eacSRuslan Bukin return (0);
991d3916eacSRuslan Bukin }
992d3916eacSRuslan Bukin
993d3916eacSRuslan Bukin void *
vm_get_cookie(struct vm * vm)994d3916eacSRuslan Bukin vm_get_cookie(struct vm *vm)
995d3916eacSRuslan Bukin {
996d3916eacSRuslan Bukin
997d3916eacSRuslan Bukin return (vm->cookie);
998d3916eacSRuslan Bukin }
999d3916eacSRuslan Bukin
1000d3916eacSRuslan Bukin int
vm_inject_exception(struct vcpu * vcpu,uint64_t scause)1001d3916eacSRuslan Bukin vm_inject_exception(struct vcpu *vcpu, uint64_t scause)
1002d3916eacSRuslan Bukin {
1003d3916eacSRuslan Bukin
1004d3916eacSRuslan Bukin return (vmmops_exception(vcpu->cookie, scause));
1005d3916eacSRuslan Bukin }
1006d3916eacSRuslan Bukin
1007d3916eacSRuslan Bukin int
vm_attach_aplic(struct vm * vm,struct vm_aplic_descr * descr)1008d3916eacSRuslan Bukin vm_attach_aplic(struct vm *vm, struct vm_aplic_descr *descr)
1009d3916eacSRuslan Bukin {
1010d3916eacSRuslan Bukin
1011d3916eacSRuslan Bukin return (aplic_attach_to_vm(vm->cookie, descr));
1012d3916eacSRuslan Bukin }
1013d3916eacSRuslan Bukin
1014d3916eacSRuslan Bukin int
vm_assert_irq(struct vm * vm,uint32_t irq)1015d3916eacSRuslan Bukin vm_assert_irq(struct vm *vm, uint32_t irq)
1016d3916eacSRuslan Bukin {
1017d3916eacSRuslan Bukin
1018d3916eacSRuslan Bukin return (aplic_inject_irq(vm->cookie, -1, irq, true));
1019d3916eacSRuslan Bukin }
1020d3916eacSRuslan Bukin
1021d3916eacSRuslan Bukin int
vm_deassert_irq(struct vm * vm,uint32_t irq)1022d3916eacSRuslan Bukin vm_deassert_irq(struct vm *vm, uint32_t irq)
1023d3916eacSRuslan Bukin {
1024d3916eacSRuslan Bukin
1025d3916eacSRuslan Bukin return (aplic_inject_irq(vm->cookie, -1, irq, false));
1026d3916eacSRuslan Bukin }
1027d3916eacSRuslan Bukin
1028d3916eacSRuslan Bukin int
vm_raise_msi(struct vm * vm,uint64_t msg,uint64_t addr,int bus,int slot,int func)1029d3916eacSRuslan Bukin vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot,
1030d3916eacSRuslan Bukin int func)
1031d3916eacSRuslan Bukin {
1032d3916eacSRuslan Bukin
1033d3916eacSRuslan Bukin return (aplic_inject_msi(vm->cookie, msg, addr));
1034d3916eacSRuslan Bukin }
1035d3916eacSRuslan Bukin
1036d3916eacSRuslan Bukin static int
vm_handle_wfi(struct vcpu * vcpu,struct vm_exit * vme,bool * retu)1037d3916eacSRuslan Bukin vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
1038d3916eacSRuslan Bukin {
1039d3916eacSRuslan Bukin
1040d3916eacSRuslan Bukin vcpu_lock(vcpu);
1041d3916eacSRuslan Bukin
1042d3916eacSRuslan Bukin while (1) {
1043d3916eacSRuslan Bukin if (aplic_check_pending(vcpu->cookie))
1044d3916eacSRuslan Bukin break;
1045d3916eacSRuslan Bukin
1046d3916eacSRuslan Bukin if (riscv_check_ipi(vcpu->cookie, false))
1047d3916eacSRuslan Bukin break;
1048d3916eacSRuslan Bukin
10499be0058eSRuslan Bukin if (riscv_check_interrupts_pending(vcpu->cookie))
10509be0058eSRuslan Bukin break;
10519be0058eSRuslan Bukin
1052d3916eacSRuslan Bukin if (vcpu_should_yield(vcpu))
1053d3916eacSRuslan Bukin break;
1054d3916eacSRuslan Bukin
1055d3916eacSRuslan Bukin vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
1056d3916eacSRuslan Bukin /*
1057d3916eacSRuslan Bukin * XXX msleep_spin() cannot be interrupted by signals so
1058d3916eacSRuslan Bukin * wake up periodically to check pending signals.
1059d3916eacSRuslan Bukin */
1060215c8b79SMark Johnston msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz);
1061d3916eacSRuslan Bukin vcpu_require_state_locked(vcpu, VCPU_FROZEN);
1062d3916eacSRuslan Bukin }
1063d3916eacSRuslan Bukin vcpu_unlock(vcpu);
1064d3916eacSRuslan Bukin
1065d3916eacSRuslan Bukin *retu = false;
1066d3916eacSRuslan Bukin
1067d3916eacSRuslan Bukin return (0);
1068d3916eacSRuslan Bukin }
1069d3916eacSRuslan Bukin
1070d3916eacSRuslan Bukin static int
vm_handle_paging(struct vcpu * vcpu,bool * retu)1071d3916eacSRuslan Bukin vm_handle_paging(struct vcpu *vcpu, bool *retu)
1072d3916eacSRuslan Bukin {
1073d3916eacSRuslan Bukin struct vm *vm;
1074d3916eacSRuslan Bukin struct vm_exit *vme;
1075d3916eacSRuslan Bukin struct vm_map *map;
1076d3916eacSRuslan Bukin uint64_t addr;
1077d3916eacSRuslan Bukin pmap_t pmap;
1078d3916eacSRuslan Bukin int ftype, rv;
1079d3916eacSRuslan Bukin
1080d3916eacSRuslan Bukin vm = vcpu->vm;
1081d3916eacSRuslan Bukin vme = &vcpu->exitinfo;
1082d3916eacSRuslan Bukin
1083d3916eacSRuslan Bukin pmap = vmspace_pmap(vm->vmspace);
1084d3916eacSRuslan Bukin addr = (vme->htval << 2) & ~(PAGE_SIZE - 1);
1085d3916eacSRuslan Bukin
1086d3916eacSRuslan Bukin dprintf("%s: %lx\n", __func__, addr);
1087d3916eacSRuslan Bukin
1088d3916eacSRuslan Bukin switch (vme->scause) {
1089d3916eacSRuslan Bukin case SCAUSE_STORE_GUEST_PAGE_FAULT:
1090d3916eacSRuslan Bukin ftype = VM_PROT_WRITE;
1091d3916eacSRuslan Bukin break;
1092d3916eacSRuslan Bukin case SCAUSE_FETCH_GUEST_PAGE_FAULT:
1093d3916eacSRuslan Bukin ftype = VM_PROT_EXECUTE;
1094d3916eacSRuslan Bukin break;
1095d3916eacSRuslan Bukin case SCAUSE_LOAD_GUEST_PAGE_FAULT:
1096d3916eacSRuslan Bukin ftype = VM_PROT_READ;
1097d3916eacSRuslan Bukin break;
1098d3916eacSRuslan Bukin default:
1099d3916eacSRuslan Bukin panic("unknown page trap: %lu", vme->scause);
1100d3916eacSRuslan Bukin }
1101d3916eacSRuslan Bukin
1102d3916eacSRuslan Bukin /* The page exists, but the page table needs to be updated. */
1103d3916eacSRuslan Bukin if (pmap_fault(pmap, addr, ftype))
1104d3916eacSRuslan Bukin return (0);
1105d3916eacSRuslan Bukin
1106d3916eacSRuslan Bukin map = &vm->vmspace->vm_map;
1107d3916eacSRuslan Bukin rv = vm_fault(map, addr, ftype, VM_FAULT_NORMAL, NULL);
1108d3916eacSRuslan Bukin if (rv != KERN_SUCCESS) {
1109d3916eacSRuslan Bukin printf("%s: vm_fault failed, addr %lx, ftype %d, err %d\n",
1110d3916eacSRuslan Bukin __func__, addr, ftype, rv);
1111d3916eacSRuslan Bukin return (EFAULT);
1112d3916eacSRuslan Bukin }
1113d3916eacSRuslan Bukin
1114d3916eacSRuslan Bukin return (0);
1115d3916eacSRuslan Bukin }
1116d3916eacSRuslan Bukin
1117d3916eacSRuslan Bukin static int
vm_handle_suspend(struct vcpu * vcpu,bool * retu)1118d3916eacSRuslan Bukin vm_handle_suspend(struct vcpu *vcpu, bool *retu)
1119d3916eacSRuslan Bukin {
1120d3916eacSRuslan Bukin struct vm *vm = vcpu->vm;
1121d3916eacSRuslan Bukin int error, i;
1122d3916eacSRuslan Bukin struct thread *td;
1123d3916eacSRuslan Bukin
1124d3916eacSRuslan Bukin error = 0;
1125d3916eacSRuslan Bukin td = curthread;
1126d3916eacSRuslan Bukin
1127d3916eacSRuslan Bukin CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus);
1128d3916eacSRuslan Bukin
1129d3916eacSRuslan Bukin /*
1130d3916eacSRuslan Bukin * Wait until all 'active_cpus' have suspended themselves.
1131d3916eacSRuslan Bukin *
1132d3916eacSRuslan Bukin * Since a VM may be suspended at any time including when one or
1133d3916eacSRuslan Bukin * more vcpus are doing a rendezvous we need to call the rendezvous
1134d3916eacSRuslan Bukin * handler while we are waiting to prevent a deadlock.
1135d3916eacSRuslan Bukin */
1136d3916eacSRuslan Bukin vcpu_lock(vcpu);
1137d3916eacSRuslan Bukin while (error == 0) {
1138d3916eacSRuslan Bukin if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0)
1139d3916eacSRuslan Bukin break;
1140d3916eacSRuslan Bukin
1141d3916eacSRuslan Bukin vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
1142d3916eacSRuslan Bukin msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz);
1143d3916eacSRuslan Bukin vcpu_require_state_locked(vcpu, VCPU_FROZEN);
1144d3916eacSRuslan Bukin if (td_ast_pending(td, TDA_SUSPEND)) {
1145d3916eacSRuslan Bukin vcpu_unlock(vcpu);
1146d3916eacSRuslan Bukin error = thread_check_susp(td, false);
1147d3916eacSRuslan Bukin vcpu_lock(vcpu);
1148d3916eacSRuslan Bukin }
1149d3916eacSRuslan Bukin }
1150d3916eacSRuslan Bukin vcpu_unlock(vcpu);
1151d3916eacSRuslan Bukin
1152d3916eacSRuslan Bukin /*
1153d3916eacSRuslan Bukin * Wakeup the other sleeping vcpus and return to userspace.
1154d3916eacSRuslan Bukin */
1155d3916eacSRuslan Bukin for (i = 0; i < vm->maxcpus; i++) {
1156d3916eacSRuslan Bukin if (CPU_ISSET(i, &vm->suspended_cpus)) {
1157d3916eacSRuslan Bukin vcpu_notify_event(vm_vcpu(vm, i));
1158d3916eacSRuslan Bukin }
1159d3916eacSRuslan Bukin }
1160d3916eacSRuslan Bukin
1161d3916eacSRuslan Bukin *retu = true;
1162d3916eacSRuslan Bukin return (error);
1163d3916eacSRuslan Bukin }
1164d3916eacSRuslan Bukin
1165d3916eacSRuslan Bukin int
vm_run(struct vcpu * vcpu)1166d3916eacSRuslan Bukin vm_run(struct vcpu *vcpu)
1167d3916eacSRuslan Bukin {
1168d3916eacSRuslan Bukin struct vm_eventinfo evinfo;
1169d3916eacSRuslan Bukin struct vm_exit *vme;
1170d3916eacSRuslan Bukin struct vm *vm;
1171d3916eacSRuslan Bukin pmap_t pmap;
1172d3916eacSRuslan Bukin int error;
1173d3916eacSRuslan Bukin int vcpuid;
1174d3916eacSRuslan Bukin bool retu;
1175d3916eacSRuslan Bukin
1176d3916eacSRuslan Bukin vm = vcpu->vm;
1177d3916eacSRuslan Bukin
1178d3916eacSRuslan Bukin dprintf("%s\n", __func__);
1179d3916eacSRuslan Bukin
1180d3916eacSRuslan Bukin vcpuid = vcpu->vcpuid;
1181d3916eacSRuslan Bukin
1182d3916eacSRuslan Bukin if (!CPU_ISSET(vcpuid, &vm->active_cpus))
1183d3916eacSRuslan Bukin return (EINVAL);
1184d3916eacSRuslan Bukin
1185d3916eacSRuslan Bukin if (CPU_ISSET(vcpuid, &vm->suspended_cpus))
1186d3916eacSRuslan Bukin return (EINVAL);
1187d3916eacSRuslan Bukin
1188d3916eacSRuslan Bukin pmap = vmspace_pmap(vm->vmspace);
1189d3916eacSRuslan Bukin vme = &vcpu->exitinfo;
1190d3916eacSRuslan Bukin evinfo.rptr = NULL;
1191d3916eacSRuslan Bukin evinfo.sptr = &vm->suspend;
1192d3916eacSRuslan Bukin evinfo.iptr = NULL;
1193d3916eacSRuslan Bukin restart:
1194d3916eacSRuslan Bukin critical_enter();
1195d3916eacSRuslan Bukin
1196d3916eacSRuslan Bukin restore_guest_fpustate(vcpu);
1197d3916eacSRuslan Bukin
1198d3916eacSRuslan Bukin vcpu_require_state(vcpu, VCPU_RUNNING);
1199d3916eacSRuslan Bukin error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo);
1200d3916eacSRuslan Bukin vcpu_require_state(vcpu, VCPU_FROZEN);
1201d3916eacSRuslan Bukin
1202d3916eacSRuslan Bukin save_guest_fpustate(vcpu);
1203d3916eacSRuslan Bukin
1204d3916eacSRuslan Bukin critical_exit();
1205d3916eacSRuslan Bukin
1206d3916eacSRuslan Bukin if (error == 0) {
1207d3916eacSRuslan Bukin retu = false;
1208d3916eacSRuslan Bukin switch (vme->exitcode) {
1209d3916eacSRuslan Bukin case VM_EXITCODE_INST_EMUL:
1210d3916eacSRuslan Bukin vcpu->nextpc = vme->pc + vme->inst_length;
1211d3916eacSRuslan Bukin error = vm_handle_inst_emul(vcpu, &retu);
1212d3916eacSRuslan Bukin break;
1213d3916eacSRuslan Bukin case VM_EXITCODE_WFI:
1214d3916eacSRuslan Bukin vcpu->nextpc = vme->pc + vme->inst_length;
1215d3916eacSRuslan Bukin error = vm_handle_wfi(vcpu, vme, &retu);
1216d3916eacSRuslan Bukin break;
1217d3916eacSRuslan Bukin case VM_EXITCODE_ECALL:
1218d3916eacSRuslan Bukin /* Handle in userland. */
1219d3916eacSRuslan Bukin vcpu->nextpc = vme->pc + vme->inst_length;
1220d3916eacSRuslan Bukin retu = true;
1221d3916eacSRuslan Bukin break;
1222d3916eacSRuslan Bukin case VM_EXITCODE_PAGING:
1223d3916eacSRuslan Bukin vcpu->nextpc = vme->pc;
1224d3916eacSRuslan Bukin error = vm_handle_paging(vcpu, &retu);
1225d3916eacSRuslan Bukin break;
1226d3916eacSRuslan Bukin case VM_EXITCODE_BOGUS:
1227d3916eacSRuslan Bukin vcpu->nextpc = vme->pc;
1228d3916eacSRuslan Bukin retu = false;
1229d3916eacSRuslan Bukin error = 0;
1230d3916eacSRuslan Bukin break;
1231d3916eacSRuslan Bukin case VM_EXITCODE_SUSPENDED:
1232d3916eacSRuslan Bukin vcpu->nextpc = vme->pc;
1233d3916eacSRuslan Bukin error = vm_handle_suspend(vcpu, &retu);
1234d3916eacSRuslan Bukin break;
1235d3916eacSRuslan Bukin default:
1236d3916eacSRuslan Bukin /* Handle in userland. */
1237d3916eacSRuslan Bukin vcpu->nextpc = vme->pc;
1238d3916eacSRuslan Bukin retu = true;
1239d3916eacSRuslan Bukin break;
1240d3916eacSRuslan Bukin }
1241d3916eacSRuslan Bukin }
1242d3916eacSRuslan Bukin
1243d3916eacSRuslan Bukin if (error == 0 && retu == false)
1244d3916eacSRuslan Bukin goto restart;
1245d3916eacSRuslan Bukin
1246d3916eacSRuslan Bukin return (error);
1247d3916eacSRuslan Bukin }
1248