master
c 1,244 lines 34.4 KB
Raw
1 /*
2 * Copyright (c) 2018-2019 Maxime Villard, All rights reserved.
3 *
4 * NetBSD Virtual Machine Monitor (NVMM) accelerator for QEMU.
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 */
9
10 #include "qemu/osdep.h"
11 #include "cpu.h"
12 #include "system/address-spaces.h"
13 #include "system/ioport.h"
14 #include "qemu/accel.h"
15 #include "accel/accel-ops.h"
16 #include "system/nvmm.h"
17 #include "system/cpus.h"
18 #include "system/memory.h"
19 #include "system/ramlist.h"
20 #include "system/runstate.h"
21 #include "qemu/main-loop.h"
22 #include "qemu/error-report.h"
23 #include "qapi/error.h"
24 #include "qemu/queue.h"
25 #include "accel/accel-cpu-target.h"
26 #include "host-cpu.h"
27 #include "migration/blocker.h"
28 #include "strings.h"
29
30 #include "nvmm-accel-ops.h"
31
32 #include <nvmm.h>
33
34 struct AccelCPUState {
35 struct nvmm_vcpu vcpu;
36 uint8_t tpr;
37 bool stop;
38
39 /* Window-exiting for INTs/NMIs. */
40 bool int_window_exit;
41 bool nmi_window_exit;
42
43 /* The guest is in an interrupt shadow (POP SS, etc). */
44 bool int_shadow;
45 };
46
47 struct qemu_machine {
48 struct nvmm_capability cap;
49 struct nvmm_machine mach;
50 };
51
52 /* -------------------------------------------------------------------------- */
53
54 bool nvmm_allowed;
55 static struct qemu_machine qemu_mach;
56
57 static struct nvmm_machine *
58 get_nvmm_mach(void)
59 {
60 return &qemu_mach.mach;
61 }
62
63 /* -------------------------------------------------------------------------- */
64
65 static void
66 nvmm_set_segment(struct nvmm_x64_state_seg *nseg, const SegmentCache *qseg)
67 {
68 uint32_t attrib = qseg->flags;
69
70 nseg->selector = qseg->selector;
71 nseg->limit = qseg->limit;
72 nseg->base = qseg->base;
73 nseg->attrib.type = __SHIFTOUT(attrib, DESC_TYPE_MASK);
74 nseg->attrib.s = __SHIFTOUT(attrib, DESC_S_MASK);
75 nseg->attrib.dpl = __SHIFTOUT(attrib, DESC_DPL_MASK);
76 nseg->attrib.p = __SHIFTOUT(attrib, DESC_P_MASK);
77 nseg->attrib.avl = __SHIFTOUT(attrib, DESC_AVL_MASK);
78 nseg->attrib.l = __SHIFTOUT(attrib, DESC_L_MASK);
79 nseg->attrib.def = __SHIFTOUT(attrib, DESC_B_MASK);
80 nseg->attrib.g = __SHIFTOUT(attrib, DESC_G_MASK);
81 }
82
83 static void
84 nvmm_set_registers(CPUState *cpu)
85 {
86 CPUX86State *env = cpu_env(cpu);
87 struct nvmm_machine *mach = get_nvmm_mach();
88 AccelCPUState *qcpu = cpu->accel;
89 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
90 struct nvmm_x64_state *state = vcpu->state;
91 uint64_t bitmap;
92 size_t i;
93 int ret;
94
95 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
96
97 /* GPRs. */
98 state->gprs[NVMM_X64_GPR_RAX] = env->regs[R_EAX];
99 state->gprs[NVMM_X64_GPR_RCX] = env->regs[R_ECX];
100 state->gprs[NVMM_X64_GPR_RDX] = env->regs[R_EDX];
101 state->gprs[NVMM_X64_GPR_RBX] = env->regs[R_EBX];
102 state->gprs[NVMM_X64_GPR_RSP] = env->regs[R_ESP];
103 state->gprs[NVMM_X64_GPR_RBP] = env->regs[R_EBP];
104 state->gprs[NVMM_X64_GPR_RSI] = env->regs[R_ESI];
105 state->gprs[NVMM_X64_GPR_RDI] = env->regs[R_EDI];
106 #ifdef TARGET_X86_64
107 state->gprs[NVMM_X64_GPR_R8] = env->regs[R_R8];
108 state->gprs[NVMM_X64_GPR_R9] = env->regs[R_R9];
109 state->gprs[NVMM_X64_GPR_R10] = env->regs[R_R10];
110 state->gprs[NVMM_X64_GPR_R11] = env->regs[R_R11];
111 state->gprs[NVMM_X64_GPR_R12] = env->regs[R_R12];
112 state->gprs[NVMM_X64_GPR_R13] = env->regs[R_R13];
113 state->gprs[NVMM_X64_GPR_R14] = env->regs[R_R14];
114 state->gprs[NVMM_X64_GPR_R15] = env->regs[R_R15];
115 #endif
116
117 /* RIP and RFLAGS. */
118 state->gprs[NVMM_X64_GPR_RIP] = env->eip;
119 state->gprs[NVMM_X64_GPR_RFLAGS] = env->eflags;
120
121 /* Segments. */
122 nvmm_set_segment(&state->segs[NVMM_X64_SEG_CS], &env->segs[R_CS]);
123 nvmm_set_segment(&state->segs[NVMM_X64_SEG_DS], &env->segs[R_DS]);
124 nvmm_set_segment(&state->segs[NVMM_X64_SEG_ES], &env->segs[R_ES]);
125 nvmm_set_segment(&state->segs[NVMM_X64_SEG_FS], &env->segs[R_FS]);
126 nvmm_set_segment(&state->segs[NVMM_X64_SEG_GS], &env->segs[R_GS]);
127 nvmm_set_segment(&state->segs[NVMM_X64_SEG_SS], &env->segs[R_SS]);
128
129 /* Special segments. */
130 nvmm_set_segment(&state->segs[NVMM_X64_SEG_GDT], &env->gdt);
131 nvmm_set_segment(&state->segs[NVMM_X64_SEG_LDT], &env->ldt);
132 nvmm_set_segment(&state->segs[NVMM_X64_SEG_TR], &env->tr);
133 nvmm_set_segment(&state->segs[NVMM_X64_SEG_IDT], &env->idt);
134
135 /* Control registers. */
136 state->crs[NVMM_X64_CR_CR0] = env->cr[0];
137 state->crs[NVMM_X64_CR_CR2] = env->cr[2];
138 state->crs[NVMM_X64_CR_CR3] = env->cr[3];
139 state->crs[NVMM_X64_CR_CR4] = env->cr[4];
140 state->crs[NVMM_X64_CR_CR8] = qcpu->tpr;
141 state->crs[NVMM_X64_CR_XCR0] = env->xcr0;
142
143 /* Debug registers. */
144 state->drs[NVMM_X64_DR_DR0] = env->dr[0];
145 state->drs[NVMM_X64_DR_DR1] = env->dr[1];
146 state->drs[NVMM_X64_DR_DR2] = env->dr[2];
147 state->drs[NVMM_X64_DR_DR3] = env->dr[3];
148 state->drs[NVMM_X64_DR_DR6] = env->dr[6];
149 state->drs[NVMM_X64_DR_DR7] = env->dr[7];
150
151 /* FPU. */
152 state->fpu.fx_cw = env->fpuc;
153 state->fpu.fx_sw = (env->fpus & ~0x3800) | ((env->fpstt & 0x7) << 11);
154 state->fpu.fx_tw = 0;
155 for (i = 0; i < 8; i++) {
156 state->fpu.fx_tw |= (!env->fptags[i]) << i;
157 }
158 state->fpu.fx_opcode = env->fpop;
159 state->fpu.fx_ip.fa_64 = env->fpip;
160 state->fpu.fx_dp.fa_64 = env->fpdp;
161 state->fpu.fx_mxcsr = env->mxcsr;
162 state->fpu.fx_mxcsr_mask = 0x0000FFFF;
163 assert(sizeof(state->fpu.fx_87_ac) == sizeof(env->fpregs));
164 memcpy(state->fpu.fx_87_ac, env->fpregs, sizeof(env->fpregs));
165 for (i = 0; i < CPU_NB_REGS; i++) {
166 memcpy(&state->fpu.fx_xmm[i].xmm_bytes[0],
167 &env->xmm_regs[i].ZMM_Q(0), 8);
168 memcpy(&state->fpu.fx_xmm[i].xmm_bytes[8],
169 &env->xmm_regs[i].ZMM_Q(1), 8);
170 }
171
172 /* MSRs. */
173 state->msrs[NVMM_X64_MSR_EFER] = env->efer;
174 state->msrs[NVMM_X64_MSR_STAR] = env->star;
175 #ifdef TARGET_X86_64
176 state->msrs[NVMM_X64_MSR_LSTAR] = env->lstar;
177 state->msrs[NVMM_X64_MSR_CSTAR] = env->cstar;
178 state->msrs[NVMM_X64_MSR_SFMASK] = env->fmask;
179 state->msrs[NVMM_X64_MSR_KERNELGSBASE] = env->kernelgsbase;
180 #endif
181 state->msrs[NVMM_X64_MSR_SYSENTER_CS] = env->sysenter_cs;
182 state->msrs[NVMM_X64_MSR_SYSENTER_ESP] = env->sysenter_esp;
183 state->msrs[NVMM_X64_MSR_SYSENTER_EIP] = env->sysenter_eip;
184 state->msrs[NVMM_X64_MSR_PAT] = env->pat;
185 state->msrs[NVMM_X64_MSR_TSC] = env->tsc;
186
187 bitmap =
188 NVMM_X64_STATE_SEGS |
189 NVMM_X64_STATE_GPRS |
190 NVMM_X64_STATE_CRS |
191 NVMM_X64_STATE_DRS |
192 NVMM_X64_STATE_MSRS |
193 NVMM_X64_STATE_FPU;
194
195 ret = nvmm_vcpu_setstate(mach, vcpu, bitmap);
196 if (ret == -1) {
197 error_report("NVMM: Failed to set virtual processor context,"
198 " error=%d", errno);
199 }
200 }
201
202 static void
203 nvmm_get_segment(SegmentCache *qseg, const struct nvmm_x64_state_seg *nseg)
204 {
205 qseg->selector = nseg->selector;
206 qseg->limit = nseg->limit;
207 qseg->base = nseg->base;
208
209 qseg->flags =
210 __SHIFTIN((uint32_t)nseg->attrib.type, DESC_TYPE_MASK) |
211 __SHIFTIN((uint32_t)nseg->attrib.s, DESC_S_MASK) |
212 __SHIFTIN((uint32_t)nseg->attrib.dpl, DESC_DPL_MASK) |
213 __SHIFTIN((uint32_t)nseg->attrib.p, DESC_P_MASK) |
214 __SHIFTIN((uint32_t)nseg->attrib.avl, DESC_AVL_MASK) |
215 __SHIFTIN((uint32_t)nseg->attrib.l, DESC_L_MASK) |
216 __SHIFTIN((uint32_t)nseg->attrib.def, DESC_B_MASK) |
217 __SHIFTIN((uint32_t)nseg->attrib.g, DESC_G_MASK);
218 }
219
220 static void
221 nvmm_get_registers(CPUState *cpu)
222 {
223 CPUX86State *env = cpu_env(cpu);
224 struct nvmm_machine *mach = get_nvmm_mach();
225 AccelCPUState *qcpu = cpu->accel;
226 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
227 X86CPU *x86_cpu = X86_CPU(cpu);
228 struct nvmm_x64_state *state = vcpu->state;
229 uint64_t bitmap, tpr;
230 size_t i;
231 int ret;
232
233 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
234
235 bitmap =
236 NVMM_X64_STATE_SEGS |
237 NVMM_X64_STATE_GPRS |
238 NVMM_X64_STATE_CRS |
239 NVMM_X64_STATE_DRS |
240 NVMM_X64_STATE_MSRS |
241 NVMM_X64_STATE_FPU;
242
243 ret = nvmm_vcpu_getstate(mach, vcpu, bitmap);
244 if (ret == -1) {
245 error_report("NVMM: Failed to get virtual processor context,"
246 " error=%d", errno);
247 }
248
249 /* GPRs. */
250 env->regs[R_EAX] = state->gprs[NVMM_X64_GPR_RAX];
251 env->regs[R_ECX] = state->gprs[NVMM_X64_GPR_RCX];
252 env->regs[R_EDX] = state->gprs[NVMM_X64_GPR_RDX];
253 env->regs[R_EBX] = state->gprs[NVMM_X64_GPR_RBX];
254 env->regs[R_ESP] = state->gprs[NVMM_X64_GPR_RSP];
255 env->regs[R_EBP] = state->gprs[NVMM_X64_GPR_RBP];
256 env->regs[R_ESI] = state->gprs[NVMM_X64_GPR_RSI];
257 env->regs[R_EDI] = state->gprs[NVMM_X64_GPR_RDI];
258 #ifdef TARGET_X86_64
259 env->regs[R_R8] = state->gprs[NVMM_X64_GPR_R8];
260 env->regs[R_R9] = state->gprs[NVMM_X64_GPR_R9];
261 env->regs[R_R10] = state->gprs[NVMM_X64_GPR_R10];
262 env->regs[R_R11] = state->gprs[NVMM_X64_GPR_R11];
263 env->regs[R_R12] = state->gprs[NVMM_X64_GPR_R12];
264 env->regs[R_R13] = state->gprs[NVMM_X64_GPR_R13];
265 env->regs[R_R14] = state->gprs[NVMM_X64_GPR_R14];
266 env->regs[R_R15] = state->gprs[NVMM_X64_GPR_R15];
267 #endif
268
269 /* RIP and RFLAGS. */
270 env->eip = state->gprs[NVMM_X64_GPR_RIP];
271 env->eflags = state->gprs[NVMM_X64_GPR_RFLAGS];
272
273 /* Segments. */
274 nvmm_get_segment(&env->segs[R_ES], &state->segs[NVMM_X64_SEG_ES]);
275 nvmm_get_segment(&env->segs[R_CS], &state->segs[NVMM_X64_SEG_CS]);
276 nvmm_get_segment(&env->segs[R_SS], &state->segs[NVMM_X64_SEG_SS]);
277 nvmm_get_segment(&env->segs[R_DS], &state->segs[NVMM_X64_SEG_DS]);
278 nvmm_get_segment(&env->segs[R_FS], &state->segs[NVMM_X64_SEG_FS]);
279 nvmm_get_segment(&env->segs[R_GS], &state->segs[NVMM_X64_SEG_GS]);
280
281 /* Special segments. */
282 nvmm_get_segment(&env->gdt, &state->segs[NVMM_X64_SEG_GDT]);
283 nvmm_get_segment(&env->ldt, &state->segs[NVMM_X64_SEG_LDT]);
284 nvmm_get_segment(&env->tr, &state->segs[NVMM_X64_SEG_TR]);
285 nvmm_get_segment(&env->idt, &state->segs[NVMM_X64_SEG_IDT]);
286
287 /* Control registers. */
288 env->cr[0] = state->crs[NVMM_X64_CR_CR0];
289 env->cr[2] = state->crs[NVMM_X64_CR_CR2];
290 env->cr[3] = state->crs[NVMM_X64_CR_CR3];
291 env->cr[4] = state->crs[NVMM_X64_CR_CR4];
292 tpr = state->crs[NVMM_X64_CR_CR8];
293 if (tpr != qcpu->tpr) {
294 qcpu->tpr = tpr;
295 cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
296 }
297 env->xcr0 = state->crs[NVMM_X64_CR_XCR0];
298
299 /* Debug registers. */
300 env->dr[0] = state->drs[NVMM_X64_DR_DR0];
301 env->dr[1] = state->drs[NVMM_X64_DR_DR1];
302 env->dr[2] = state->drs[NVMM_X64_DR_DR2];
303 env->dr[3] = state->drs[NVMM_X64_DR_DR3];
304 env->dr[6] = state->drs[NVMM_X64_DR_DR6];
305 env->dr[7] = state->drs[NVMM_X64_DR_DR7];
306
307 /* FPU. */
308 env->fpuc = state->fpu.fx_cw;
309 env->fpstt = (state->fpu.fx_sw >> 11) & 0x7;
310 env->fpus = state->fpu.fx_sw & ~0x3800;
311 for (i = 0; i < 8; i++) {
312 env->fptags[i] = !((state->fpu.fx_tw >> i) & 1);
313 }
314 env->fpop = state->fpu.fx_opcode;
315 env->fpip = state->fpu.fx_ip.fa_64;
316 env->fpdp = state->fpu.fx_dp.fa_64;
317 env->mxcsr = state->fpu.fx_mxcsr;
318 assert(sizeof(state->fpu.fx_87_ac) == sizeof(env->fpregs));
319 memcpy(env->fpregs, state->fpu.fx_87_ac, sizeof(env->fpregs));
320 for (i = 0; i < CPU_NB_REGS; i++) {
321 memcpy(&env->xmm_regs[i].ZMM_Q(0),
322 &state->fpu.fx_xmm[i].xmm_bytes[0], 8);
323 memcpy(&env->xmm_regs[i].ZMM_Q(1),
324 &state->fpu.fx_xmm[i].xmm_bytes[8], 8);
325 }
326
327 /* MSRs. */
328 env->efer = state->msrs[NVMM_X64_MSR_EFER];
329 env->star = state->msrs[NVMM_X64_MSR_STAR];
330 #ifdef TARGET_X86_64
331 env->lstar = state->msrs[NVMM_X64_MSR_LSTAR];
332 env->cstar = state->msrs[NVMM_X64_MSR_CSTAR];
333 env->fmask = state->msrs[NVMM_X64_MSR_SFMASK];
334 env->kernelgsbase = state->msrs[NVMM_X64_MSR_KERNELGSBASE];
335 #endif
336 env->sysenter_cs = state->msrs[NVMM_X64_MSR_SYSENTER_CS];
337 env->sysenter_esp = state->msrs[NVMM_X64_MSR_SYSENTER_ESP];
338 env->sysenter_eip = state->msrs[NVMM_X64_MSR_SYSENTER_EIP];
339 env->pat = state->msrs[NVMM_X64_MSR_PAT];
340 env->tsc = state->msrs[NVMM_X64_MSR_TSC];
341
342 x86_update_hflags(env);
343 }
344
345 static bool
346 nvmm_can_take_int(CPUState *cpu)
347 {
348 AccelCPUState *qcpu = cpu->accel;
349 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
350 struct nvmm_machine *mach = get_nvmm_mach();
351
352 if (qcpu->int_window_exit) {
353 return false;
354 }
355
356 if (qcpu->int_shadow || !(cpu_env(cpu)->eflags & IF_MASK)) {
357 struct nvmm_x64_state *state = vcpu->state;
358
359 /* Exit on interrupt window. */
360 nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_INTR);
361 state->intr.int_window_exiting = 1;
362 nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_INTR);
363
364 return false;
365 }
366
367 return true;
368 }
369
370 static bool
371 nvmm_can_take_nmi(CPUState *cpu)
372 {
373 AccelCPUState *qcpu = cpu->accel;
374
375 /*
376 * Contrary to INTs, NMIs always schedule an exit when they are
377 * completed. Therefore, if window-exiting is enabled, it means
378 * NMIs are blocked.
379 */
380 if (qcpu->nmi_window_exit) {
381 return false;
382 }
383
384 return true;
385 }
386
387 /*
388 * Called before the VCPU is run. We inject events generated by the I/O
389 * thread, and synchronize the guest TPR.
390 */
391 static void
392 nvmm_vcpu_pre_run(CPUState *cpu)
393 {
394 CPUX86State *env = cpu_env(cpu);
395 struct nvmm_machine *mach = get_nvmm_mach();
396 AccelCPUState *qcpu = cpu->accel;
397 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
398 X86CPU *x86_cpu = X86_CPU(cpu);
399 struct nvmm_x64_state *state = vcpu->state;
400 struct nvmm_vcpu_event *event = vcpu->event;
401 bool has_event = false;
402 bool sync_tpr = false;
403 uint8_t tpr;
404 int ret;
405
406 bql_lock();
407
408 tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
409 if (tpr != qcpu->tpr) {
410 qcpu->tpr = tpr;
411 sync_tpr = true;
412 }
413
414 /*
415 * Force the VCPU out of its inner loop to process any INIT requests
416 * or commit pending TPR access.
417 */
418 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
419 qatomic_set(&cpu->exit_request, true);
420 }
421
422 if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
423 if (nvmm_can_take_nmi(cpu)) {
424 cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI);
425 event->type = NVMM_VCPU_EVENT_INTR;
426 event->vector = 2;
427 has_event = true;
428 }
429 }
430
431 if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) {
432 if (nvmm_can_take_int(cpu)) {
433 cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
434 event->type = NVMM_VCPU_EVENT_INTR;
435 event->vector = cpu_get_pic_interrupt(env);
436 has_event = true;
437 }
438 }
439
440 /* Don't want SMIs. */
441 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) {
442 cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI);
443 }
444
445 if (sync_tpr) {
446 ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_CRS);
447 if (ret == -1) {
448 error_report("NVMM: Failed to get CPU state,"
449 " error=%d", errno);
450 }
451
452 state->crs[NVMM_X64_CR_CR8] = qcpu->tpr;
453
454 ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_CRS);
455 if (ret == -1) {
456 error_report("NVMM: Failed to set CPU state,"
457 " error=%d", errno);
458 }
459 }
460
461 if (has_event) {
462 ret = nvmm_vcpu_inject(mach, vcpu);
463 if (ret == -1) {
464 error_report("NVMM: Failed to inject event,"
465 " error=%d", errno);
466 }
467 }
468
469 bql_unlock();
470 }
471
472 /*
473 * Called after the VCPU ran. We synchronize the host view of the TPR and
474 * RFLAGS.
475 */
476 static void
477 nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit)
478 {
479 AccelCPUState *qcpu = cpu->accel;
480 X86CPU *x86_cpu = X86_CPU(cpu);
481 CPUX86State *env = &x86_cpu->env;
482 uint64_t tpr;
483
484 env->eflags = exit->exitstate.rflags;
485 qcpu->int_shadow = exit->exitstate.int_shadow;
486 qcpu->int_window_exit = exit->exitstate.int_window_exiting;
487 qcpu->nmi_window_exit = exit->exitstate.nmi_window_exiting;
488
489 tpr = exit->exitstate.cr8;
490 if (qcpu->tpr != tpr) {
491 qcpu->tpr = tpr;
492 bql_lock();
493 cpu_set_apic_tpr(x86_cpu->apic_state, qcpu->tpr);
494 bql_unlock();
495 }
496 }
497
498 /* -------------------------------------------------------------------------- */
499
500 static void
501 nvmm_io_callback(struct nvmm_io *io)
502 {
503 MemTxAttrs attrs = { 0 };
504 int ret;
505
506 ret = address_space_rw(&address_space_io, io->port, attrs, io->data,
507 io->size, !io->in);
508 if (ret != MEMTX_OK) {
509 error_report("NVMM: I/O Transaction Failed "
510 "[%s, port=%u, size=%zu]", (io->in ? "in" : "out"),
511 io->port, io->size);
512 }
513
514 /* Needed, otherwise infinite loop. */
515 current_cpu->vcpu_dirty = false;
516 }
517
518 static void
519 nvmm_mem_callback(struct nvmm_mem *mem)
520 {
521 /* TODO: Get CPUState via mem->vcpu? */
522 address_space_rw(&address_space_memory, mem->gpa, MEMTXATTRS_UNSPECIFIED,
523 mem->data, mem->size, mem->write);
524
525 /* Needed, otherwise infinite loop. */
526 current_cpu->vcpu_dirty = false;
527 }
528
529 static struct nvmm_assist_callbacks nvmm_callbacks = {
530 .io = nvmm_io_callback,
531 .mem = nvmm_mem_callback
532 };
533
534 /* -------------------------------------------------------------------------- */
535
536 static int
537 nvmm_handle_mem(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
538 {
539 int ret;
540
541 ret = nvmm_assist_mem(mach, vcpu);
542 if (ret == -1) {
543 error_report("NVMM: Mem Assist Failed [gpa=%p]",
544 (void *)vcpu->exit->u.mem.gpa);
545 }
546
547 return ret;
548 }
549
550 static int
551 nvmm_handle_io(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
552 {
553 int ret;
554
555 ret = nvmm_assist_io(mach, vcpu);
556 if (ret == -1) {
557 error_report("NVMM: I/O Assist Failed [port=%d]",
558 (int)vcpu->exit->u.io.port);
559 }
560
561 return ret;
562 }
563
564 static int
565 nvmm_handle_rdmsr(struct nvmm_machine *mach, CPUState *cpu,
566 struct nvmm_vcpu_exit *exit)
567 {
568 AccelCPUState *qcpu = cpu->accel;
569 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
570 X86CPU *x86_cpu = X86_CPU(cpu);
571 struct nvmm_x64_state *state = vcpu->state;
572 uint64_t val;
573 int ret;
574
575 switch (exit->u.rdmsr.msr) {
576 case MSR_IA32_APICBASE:
577 val = cpu_get_apic_base(x86_cpu->apic_state);
578 break;
579 case MSR_MTRRcap:
580 case MSR_MTRRdefType:
581 case MSR_MCG_CAP:
582 case MSR_MCG_STATUS:
583 val = 0;
584 break;
585 default: /* More MSRs to add? */
586 val = 0;
587 error_report("NVMM: Unexpected RDMSR 0x%x, ignored",
588 exit->u.rdmsr.msr);
589 break;
590 }
591
592 ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS);
593 if (ret == -1) {
594 return -1;
595 }
596
597 state->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF);
598 state->gprs[NVMM_X64_GPR_RDX] = (val >> 32);
599 state->gprs[NVMM_X64_GPR_RIP] = exit->u.rdmsr.npc;
600
601 ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_GPRS);
602 if (ret == -1) {
603 return -1;
604 }
605
606 return 0;
607 }
608
609 static int
610 nvmm_handle_wrmsr(struct nvmm_machine *mach, CPUState *cpu,
611 struct nvmm_vcpu_exit *exit)
612 {
613 AccelCPUState *qcpu = cpu->accel;
614 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
615 X86CPU *x86_cpu = X86_CPU(cpu);
616 struct nvmm_x64_state *state = vcpu->state;
617 uint64_t val;
618 int ret;
619
620 val = exit->u.wrmsr.val;
621
622 switch (exit->u.wrmsr.msr) {
623 case MSR_IA32_APICBASE:
624 cpu_set_apic_base(x86_cpu->apic_state, val);
625 break;
626 case MSR_MTRRdefType:
627 case MSR_MCG_STATUS:
628 break;
629 default: /* More MSRs to add? */
630 error_report("NVMM: Unexpected WRMSR 0x%x [val=0x%lx], ignored",
631 exit->u.wrmsr.msr, val);
632 break;
633 }
634
635 ret = nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS);
636 if (ret == -1) {
637 return -1;
638 }
639
640 state->gprs[NVMM_X64_GPR_RIP] = exit->u.wrmsr.npc;
641
642 ret = nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_GPRS);
643 if (ret == -1) {
644 return -1;
645 }
646
647 return 0;
648 }
649
650 static int
651 nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu,
652 struct nvmm_vcpu_exit *exit)
653 {
654 int ret = 0;
655
656 bql_lock();
657
658 if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
659 (cpu_env(cpu)->eflags & IF_MASK)) &&
660 !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
661 cpu->exception_index = EXCP_HLT;
662 cpu->halted = true;
663 ret = 1;
664 }
665
666 bql_unlock();
667
668 return ret;
669 }
670
671 static int
672 nvmm_inject_ud(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
673 {
674 struct nvmm_vcpu_event *event = vcpu->event;
675
676 event->type = NVMM_VCPU_EVENT_EXCP;
677 event->vector = 6;
678 event->u.excp.error = 0;
679
680 return nvmm_vcpu_inject(mach, vcpu);
681 }
682
683 static int
684 nvmm_vcpu_loop(CPUState *cpu)
685 {
686 struct nvmm_machine *mach = get_nvmm_mach();
687 AccelCPUState *qcpu = cpu->accel;
688 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
689 X86CPU *x86_cpu = X86_CPU(cpu);
690 CPUX86State *env = &x86_cpu->env;
691 struct nvmm_vcpu_exit *exit = vcpu->exit;
692 int ret;
693
694 /*
695 * Some asynchronous events must be handled outside of the inner
696 * VCPU loop. They are handled here.
697 */
698 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT)) {
699 nvmm_cpu_synchronize_state(cpu);
700 do_cpu_init(x86_cpu);
701 /* set int/nmi windows back to the reset state */
702 }
703 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) {
704 cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
705 apic_poll_irq(x86_cpu->apic_state);
706 }
707 if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
708 (env->eflags & IF_MASK)) ||
709 cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
710 cpu->halted = false;
711 }
712 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) {
713 cpu_reset_interrupt(cpu, CPU_INTERRUPT_SIPI);
714 nvmm_cpu_synchronize_state(cpu);
715 do_cpu_sipi(x86_cpu);
716 }
717 if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) {
718 cpu_reset_interrupt(cpu, CPU_INTERRUPT_TPR);
719 nvmm_cpu_synchronize_state(cpu);
720 apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
721 env->tpr_access_type);
722 }
723
724 if (cpu->halted) {
725 cpu->exception_index = EXCP_HLT;
726 qatomic_set(&cpu->exit_request, false);
727 return 0;
728 }
729
730 bql_unlock();
731 cpu_exec_start(cpu);
732
733 /*
734 * Inner VCPU loop.
735 */
736 do {
737 if (cpu->vcpu_dirty) {
738 nvmm_set_registers(cpu);
739 cpu->vcpu_dirty = false;
740 }
741
742 if (qcpu->stop) {
743 cpu->exception_index = EXCP_INTERRUPT;
744 qcpu->stop = false;
745 ret = 1;
746 break;
747 }
748
749 nvmm_vcpu_pre_run(cpu);
750
751 /* Corresponding store-release is in cpu_exit. */
752 if (qatomic_load_acquire(&cpu->exit_request)) {
753 #if NVMM_USER_VERSION >= 2
754 nvmm_vcpu_stop(vcpu);
755 #else
756 qemu_cpu_kick_self();
757 #endif
758 }
759
760 ret = nvmm_vcpu_run(mach, vcpu);
761 if (ret == -1) {
762 error_report("NVMM: Failed to exec a virtual processor,"
763 " error=%d", errno);
764 break;
765 }
766
767 nvmm_vcpu_post_run(cpu, exit);
768
769 switch (exit->reason) {
770 case NVMM_VCPU_EXIT_NONE:
771 break;
772 #if NVMM_USER_VERSION >= 2
773 case NVMM_VCPU_EXIT_STOPPED:
774 /*
775 * The kernel cleared the immediate exit flag; cpu->exit_request
776 * must be cleared after
777 */
778 smp_wmb();
779 qcpu->stop = true;
780 break;
781 #endif
782 case NVMM_VCPU_EXIT_MEMORY:
783 ret = nvmm_handle_mem(mach, vcpu);
784 break;
785 case NVMM_VCPU_EXIT_IO:
786 ret = nvmm_handle_io(mach, vcpu);
787 break;
788 case NVMM_VCPU_EXIT_INT_READY:
789 case NVMM_VCPU_EXIT_NMI_READY:
790 case NVMM_VCPU_EXIT_TPR_CHANGED:
791 break;
792 case NVMM_VCPU_EXIT_HALTED:
793 ret = nvmm_handle_halted(mach, cpu, exit);
794 break;
795 case NVMM_VCPU_EXIT_SHUTDOWN:
796 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
797 cpu->exception_index = EXCP_INTERRUPT;
798 ret = 1;
799 break;
800 case NVMM_VCPU_EXIT_RDMSR:
801 ret = nvmm_handle_rdmsr(mach, cpu, exit);
802 break;
803 case NVMM_VCPU_EXIT_WRMSR:
804 ret = nvmm_handle_wrmsr(mach, cpu, exit);
805 break;
806 case NVMM_VCPU_EXIT_MONITOR:
807 case NVMM_VCPU_EXIT_MWAIT:
808 ret = nvmm_inject_ud(mach, vcpu);
809 break;
810 default:
811 error_report("NVMM: Unexpected VM exit code 0x%lx [hw=0x%lx]",
812 exit->reason, exit->u.inv.hwcode);
813 nvmm_get_registers(cpu);
814 bql_lock();
815 qemu_system_guest_panicked(cpu_get_crash_info(cpu));
816 bql_unlock();
817 ret = -1;
818 break;
819 }
820 } while (ret == 0);
821
822 cpu_exec_end(cpu);
823 bql_lock();
824
825 return ret < 0;
826 }
827
828 /* -------------------------------------------------------------------------- */
829
830 static void
831 do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
832 {
833 nvmm_get_registers(cpu);
834 cpu->vcpu_dirty = true;
835 }
836
837 static void
838 do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
839 {
840 nvmm_set_registers(cpu);
841 cpu->vcpu_dirty = false;
842 }
843
844 static void
845 do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
846 {
847 nvmm_set_registers(cpu);
848 cpu->vcpu_dirty = false;
849 }
850
851 static void
852 do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg)
853 {
854 cpu->vcpu_dirty = true;
855 }
856
857 void nvmm_cpu_synchronize_state(CPUState *cpu)
858 {
859 if (!cpu->vcpu_dirty) {
860 run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL);
861 }
862 }
863
864 void nvmm_cpu_synchronize_post_reset(CPUState *cpu)
865 {
866 run_on_cpu(cpu, do_nvmm_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
867 }
868
869 void nvmm_cpu_synchronize_post_init(CPUState *cpu)
870 {
871 run_on_cpu(cpu, do_nvmm_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
872 }
873
874 void nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu)
875 {
876 run_on_cpu(cpu, do_nvmm_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
877 }
878
879 /* -------------------------------------------------------------------------- */
880
881 static Error *nvmm_migration_blocker;
882
883 /*
884 * The nvmm_vcpu_stop() mechanism breaks races between entering the VMM
885 * and another thread signaling the vCPU thread to exit.
886 */
887
888 static void
889 nvmm_ipi_signal(int sigcpu)
890 {
891 if (current_cpu) {
892 AccelCPUState *qcpu = current_cpu->accel;
893 #if NVMM_USER_VERSION >= 2
894 struct nvmm_vcpu *vcpu = &qcpu->vcpu;
895 nvmm_vcpu_stop(vcpu);
896 #else
897 qcpu->stop = true;
898 #endif
899 }
900 }
901
902 static void
903 nvmm_init_cpu_signals(void)
904 {
905 struct sigaction sigact;
906 sigset_t set;
907
908 /* Install the IPI handler. */
909 memset(&sigact, 0, sizeof(sigact));
910 sigact.sa_handler = nvmm_ipi_signal;
911 sigaction(SIG_IPI, &sigact, NULL);
912
913 /* Allow IPIs on the current thread. */
914 sigprocmask(SIG_BLOCK, NULL, &set);
915 sigdelset(&set, SIG_IPI);
916 pthread_sigmask(SIG_SETMASK, &set, NULL);
917 }
918
919 int
920 nvmm_init_vcpu(CPUState *cpu)
921 {
922 struct nvmm_machine *mach = get_nvmm_mach();
923 struct nvmm_vcpu_conf_cpuid cpuid;
924 struct nvmm_vcpu_conf_tpr tpr;
925 Error *local_error = NULL;
926 AccelCPUState *qcpu;
927 int ret, err;
928
929 nvmm_init_cpu_signals();
930
931 if (nvmm_migration_blocker == NULL) {
932 error_setg(&nvmm_migration_blocker,
933 "NVMM: Migration not supported");
934
935 if (migrate_add_blocker(&nvmm_migration_blocker, &local_error) < 0) {
936 error_report_err(local_error);
937 return -EINVAL;
938 }
939 }
940
941 qcpu = g_new0(AccelCPUState, 1);
942
943 ret = nvmm_vcpu_create(mach, cpu->cpu_index, &qcpu->vcpu);
944 if (ret == -1) {
945 err = errno;
946 error_report("NVMM: Failed to create a virtual processor,"
947 " error=%d", err);
948 g_free(qcpu);
949 return -err;
950 }
951
952 memset(&cpuid, 0, sizeof(cpuid));
953 cpuid.mask = 1;
954 cpuid.leaf = 0x00000001;
955 cpuid.u.mask.set.edx = CPUID_MCE | CPUID_MCA | CPUID_MTRR;
956 ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CPUID,
957 &cpuid);
958 if (ret == -1) {
959 err = errno;
960 error_report("NVMM: Failed to configure a virtual processor,"
961 " error=%d", err);
962 g_free(qcpu);
963 return -err;
964 }
965
966 ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_CALLBACKS,
967 &nvmm_callbacks);
968 if (ret == -1) {
969 err = errno;
970 error_report("NVMM: Failed to configure a virtual processor,"
971 " error=%d", err);
972 g_free(qcpu);
973 return -err;
974 }
975
976 if (qemu_mach.cap.arch.vcpu_conf_support & NVMM_CAP_ARCH_VCPU_CONF_TPR) {
977 memset(&tpr, 0, sizeof(tpr));
978 tpr.exit_changed = 1;
979 ret = nvmm_vcpu_configure(mach, &qcpu->vcpu, NVMM_VCPU_CONF_TPR, &tpr);
980 if (ret == -1) {
981 err = errno;
982 error_report("NVMM: Failed to configure a virtual processor,"
983 " error=%d", err);
984 g_free(qcpu);
985 return -err;
986 }
987 }
988
989 cpu->vcpu_dirty = true;
990 cpu->accel = qcpu;
991
992 return 0;
993 }
994
995 int
996 nvmm_vcpu_exec(CPUState *cpu)
997 {
998 int ret, fatal;
999
1000 while (1) {
1001 if (cpu->exception_index >= EXCP_INTERRUPT) {
1002 ret = cpu->exception_index;
1003 cpu->exception_index = -1;
1004 break;
1005 }
1006
1007 fatal = nvmm_vcpu_loop(cpu);
1008
1009 if (fatal) {
1010 error_report("NVMM: Failed to execute a VCPU.");
1011 abort();
1012 }
1013 }
1014
1015 return ret;
1016 }
1017
1018 void
1019 nvmm_destroy_vcpu(CPUState *cpu)
1020 {
1021 struct nvmm_machine *mach = get_nvmm_mach();
1022 AccelCPUState *qcpu = cpu->accel;
1023
1024 nvmm_vcpu_destroy(mach, &qcpu->vcpu);
1025 g_free(cpu->accel);
1026 }
1027
1028 /* -------------------------------------------------------------------------- */
1029
1030 static void
1031 nvmm_update_mapping(hwaddr start_pa, ram_addr_t size, uintptr_t hva,
1032 bool add, bool rom, const char *name)
1033 {
1034 struct nvmm_machine *mach = get_nvmm_mach();
1035 int ret, prot;
1036
1037 if (add) {
1038 prot = PROT_READ | PROT_EXEC;
1039 if (!rom) {
1040 prot |= PROT_WRITE;
1041 }
1042 ret = nvmm_gpa_map(mach, hva, start_pa, size, prot);
1043 } else {
1044 ret = nvmm_gpa_unmap(mach, hva, start_pa, size);
1045 }
1046
1047 if (ret == -1) {
1048 error_report("NVMM: Failed to %s GPA range '%s' PA:%p, "
1049 "Size:%p bytes, HostVA:%p, error=%d",
1050 (add ? "map" : "unmap"), name, (void *)(uintptr_t)start_pa,
1051 (void *)size, (void *)hva, errno);
1052 }
1053 }
1054
1055 static void
1056 nvmm_process_section(MemoryRegionSection *section, int add)
1057 {
1058 MemoryRegion *mr = section->mr;
1059 hwaddr start_pa = section->offset_within_address_space;
1060 ram_addr_t size = int128_get64(section->size);
1061 unsigned int delta;
1062 uintptr_t hva;
1063
1064 if (!memory_region_is_ram(mr)) {
1065 return;
1066 }
1067
1068 /* Adjust start_pa and size so that they are page-aligned. */
1069 delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask());
1070 delta &= ~qemu_real_host_page_mask();
1071 if (delta > size) {
1072 return;
1073 }
1074 start_pa += delta;
1075 size -= delta;
1076 size &= qemu_real_host_page_mask();
1077 if (!size || (start_pa & ~qemu_real_host_page_mask())) {
1078 return;
1079 }
1080
1081 hva = (uintptr_t)memory_region_get_ram_ptr(mr) +
1082 section->offset_within_region + delta;
1083
1084 nvmm_update_mapping(start_pa, size, hva, add,
1085 memory_region_is_rom(mr), mr->name);
1086 }
1087
1088 static void
1089 nvmm_region_add(MemoryListener *listener, MemoryRegionSection *section)
1090 {
1091 memory_region_ref(section->mr);
1092 nvmm_process_section(section, 1);
1093 }
1094
1095 static void
1096 nvmm_region_del(MemoryListener *listener, MemoryRegionSection *section)
1097 {
1098 nvmm_process_section(section, 0);
1099 memory_region_unref(section->mr);
1100 }
1101
1102 static void
1103 nvmm_transaction_begin(MemoryListener *listener)
1104 {
1105 /* nothing */
1106 }
1107
1108 static void
1109 nvmm_transaction_commit(MemoryListener *listener)
1110 {
1111 /* nothing */
1112 }
1113
1114 static void
1115 nvmm_log_sync(MemoryListener *listener, MemoryRegionSection *section)
1116 {
1117 MemoryRegion *mr = section->mr;
1118
1119 if (!memory_region_is_ram(mr)) {
1120 return;
1121 }
1122
1123 memory_region_set_dirty(mr, 0, int128_get64(section->size));
1124 }
1125
1126 static MemoryListener nvmm_memory_listener = {
1127 .name = "nvmm",
1128 .begin = nvmm_transaction_begin,
1129 .commit = nvmm_transaction_commit,
1130 .region_add = nvmm_region_add,
1131 .region_del = nvmm_region_del,
1132 .log_sync = nvmm_log_sync,
1133 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
1134 };
1135
1136 static void
1137 nvmm_ram_block_added(RAMBlockNotifier *n, void *host, size_t size,
1138 size_t max_size)
1139 {
1140 struct nvmm_machine *mach = get_nvmm_mach();
1141 uintptr_t hva = (uintptr_t)host;
1142 int ret;
1143
1144 ret = nvmm_hva_map(mach, hva, max_size);
1145
1146 if (ret == -1) {
1147 error_report("NVMM: Failed to map HVA, HostVA:%p "
1148 "Size:%p bytes, error=%d",
1149 (void *)hva, (void *)size, errno);
1150 }
1151 }
1152
1153 static struct RAMBlockNotifier nvmm_ram_notifier = {
1154 .ram_block_added = nvmm_ram_block_added
1155 };
1156
1157 /* -------------------------------------------------------------------------- */
1158
1159 static int
1160 nvmm_accel_init(AccelState *as, MachineState *ms)
1161 {
1162 int ret, err;
1163
1164 ret = nvmm_init();
1165 if (ret == -1) {
1166 err = errno;
1167 error_report("NVMM: Initialization failed, error=%d", errno);
1168 return -err;
1169 }
1170
1171 ret = nvmm_capability(&qemu_mach.cap);
1172 if (ret == -1) {
1173 err = errno;
1174 error_report("NVMM: Unable to fetch capability, error=%d", errno);
1175 return -err;
1176 }
1177 if (qemu_mach.cap.version < NVMM_KERN_VERSION) {
1178 error_report("NVMM: Unsupported version %u", qemu_mach.cap.version);
1179 return -EPROGMISMATCH;
1180 }
1181 if (qemu_mach.cap.state_size != sizeof(struct nvmm_x64_state)) {
1182 error_report("NVMM: Wrong state size %u", qemu_mach.cap.state_size);
1183 return -EPROGMISMATCH;
1184 }
1185
1186 ret = nvmm_machine_create(&qemu_mach.mach);
1187 if (ret == -1) {
1188 err = errno;
1189 error_report("NVMM: Machine creation failed, error=%d", errno);
1190 return -err;
1191 }
1192
1193 memory_listener_register(&nvmm_memory_listener, &address_space_memory);
1194 ram_block_notifier_add(&nvmm_ram_notifier);
1195
1196 printf("NetBSD Virtual Machine Monitor accelerator is operational\n");
1197 return 0;
1198 }
1199
1200 static void
1201 nvmm_accel_class_init(ObjectClass *oc, const void *data)
1202 {
1203 AccelClass *ac = ACCEL_CLASS(oc);
1204 ac->name = "NVMM";
1205 ac->init_machine = nvmm_accel_init;
1206 ac->allowed = &nvmm_allowed;
1207 }
1208
1209 static const TypeInfo nvmm_accel_type = {
1210 .name = ACCEL_CLASS_NAME("nvmm"),
1211 .parent = TYPE_ACCEL,
1212 .class_init = nvmm_accel_class_init,
1213 };
1214
1215 static void nvmm_cpu_instance_init(CPUState *cs)
1216 {
1217 X86CPU *cpu = X86_CPU(cs);
1218
1219 host_cpu_instance_init(cpu);
1220 }
1221
1222 static void nvmm_cpu_accel_class_init(ObjectClass *oc, const void *data)
1223 {
1224 AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
1225
1226 acc->cpu_instance_init = nvmm_cpu_instance_init;
1227 }
1228
1229 static const TypeInfo nvmm_cpu_accel_type = {
1230 .name = ACCEL_CPU_NAME("nvmm"),
1231
1232 .parent = TYPE_ACCEL_CPU,
1233 .class_init = nvmm_cpu_accel_class_init,
1234 .abstract = true,
1235 };
1236
1237 static void
1238 nvmm_type_init(void)
1239 {
1240 type_register_static(&nvmm_accel_type);
1241 type_register_static(&nvmm_cpu_accel_type);
1242 }
1243
1244 type_init(nvmm_type_init);