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