2 #include "../port/lib.h"
6 #include "../port/error.h"
9 extern int vmxon(u64int);
10 extern int vmxoff(void);
11 extern int vmclear(u64int);
12 extern int vmptrld(u64int);
13 extern int vmlaunch(Ureg *, int);
14 extern int vmread(u32int, uintptr *);
15 extern int vmwrite(u32int, uintptr);
16 extern int invept(u32int, uvlong, uvlong);
17 extern int invvpid(u32int, uvlong, uvlong);
19 static vlong procb_ctls, pinb_ctls;
22 VMX_BASIC_MSR = 0x480,
23 VMX_PINB_CTLS_MSR = 0x481,
24 VMX_PROCB_CTLS_MSR = 0x482,
25 VMX_VMEXIT_CTLS_MSR = 0x483,
26 VMX_VMENTRY_CTLS_MSR = 0x484,
28 VMX_CR0_FIXED0 = 0x486,
29 VMX_CR0_FIXED1 = 0x487,
30 VMX_CR4_FIXED0 = 0x488,
31 VMX_CR4_FIXED1 = 0x489,
32 VMX_VMCS_ENUM = 0x48A,
33 VMX_PROCB_CTLS2_MSR = 0x48B,
34 VMX_TRUE_PINB_CTLS_MSR = 0x48D,
35 VMX_TRUE_PROCB_CTLS_MSR = 0x48E,
36 VMX_TRUE_EXIT_CTLS_MSR = 0x48F,
37 VMX_TRUE_ENTRY_CTLS_MSR = 0x490,
38 VMX_VMFUNC_MSR = 0x491,
47 PROCB_EXITINVLPG = 1<<9,
48 PROCB_EXITMWAIT = 1<<10,
49 PROCB_EXITRDPMC = 1<<11,
50 PROCB_EXITRDTSC = 1<<12,
51 PROCB_EXITCR3LD = 1<<15,
52 PROCB_EXITCR3ST = 1<<16,
53 PROCB_EXITCR8LD = 1<<19,
54 PROCB_EXITCR8ST = 1<<20,
55 PROCB_EXITMOVDR = 1<<23,
57 PROCB_MONTRAP = 1<<27,
58 PROCB_MSRBITMAP = 1<<28,
59 PROCB_EXITMONITOR = 1<<29,
60 PROCB_EXITPAUSE = 1<<30,
61 PROCB_USECTLS2 = 1<<31,
71 PFAULT_MATCH = 0x4008,
76 VMEXIT_ST_DEBUG = 1<<2,
78 VMEXIT_LD_IA32_PERF_GLOBAL_CTRL = 1<<12,
79 VMEXIT_ST_IA32_PAT = 1<<18,
80 VMEXIT_LD_IA32_PAT = 1<<19,
81 VMEXIT_ST_IA32_EFER = 1<<20,
82 VMEXIT_LD_IA32_EFER = 1<<21,
84 VMEXIT_MSRSTCNT = 0x400e,
85 VMEXIT_MSRLDCNT = 0x4010,
86 VMEXIT_MSRSTADDR = 0x2006,
87 VMEXIT_MSRLDADDR = 0x2008,
88 VMENTRY_MSRLDADDR = 0x200A,
90 VMENTRY_CTLS = 0x4012,
91 VMENTRY_LD_DEBUG = 1<<2,
92 VMENTRY_GUEST64 = 1<<9,
93 VMENTRY_LD_IA32_PERF_GLOBAL_CTRL = 1<<13,
94 VMENTRY_LD_IA32_PAT = 1<<14,
95 VMENTRY_LD_IA32_EFER = 1<<15,
97 VMENTRY_MSRLDCNT = 0x4014,
98 VMENTRY_INTRINFO = 0x4016,
99 VMENTRY_INTRCODE = 0x4018,
100 VMENTRY_INTRILEN = 0x401a,
115 GUEST_ESLIMIT = 0x4800,
116 GUEST_CSLIMIT = 0x4802,
117 GUEST_SSLIMIT = 0x4804,
118 GUEST_DSLIMIT = 0x4806,
119 GUEST_FSLIMIT = 0x4808,
120 GUEST_GSLIMIT = 0x480A,
121 GUEST_LDTRLIMIT = 0x480C,
122 GUEST_TRLIMIT = 0x480E,
123 GUEST_GDTRLIMIT = 0x4810,
124 GUEST_IDTRLIMIT = 0x4812,
125 GUEST_ESPERM = 0x4814,
126 GUEST_CSPERM = 0x4816,
127 GUEST_SSPERM = 0x4818,
128 GUEST_DSPERM = 0x481A,
129 GUEST_FSPERM = 0x481C,
130 GUEST_GSPERM = 0x481E,
131 GUEST_LDTRPERM = 0x4820,
132 GUEST_TRPERM = 0x4822,
133 GUEST_CR0MASK = 0x6000,
134 GUEST_CR4MASK = 0x6002,
135 GUEST_CR0SHADOW = 0x6004,
136 GUEST_CR4SHADOW = 0x6006,
137 GUEST_ESBASE = 0x6806,
138 GUEST_CSBASE = 0x6808,
139 GUEST_SSBASE = 0x680A,
140 GUEST_DSBASE = 0x680C,
141 GUEST_FSBASE = 0x680E,
142 GUEST_GSBASE = 0x6810,
143 GUEST_LDTRBASE = 0x6812,
144 GUEST_TRBASE = 0x6814,
145 GUEST_GDTRBASE = 0x6816,
146 GUEST_IDTRBASE = 0x6818,
150 GUEST_RFLAGS = 0x6820,
151 GUEST_IA32_DEBUGCTL = 0x2802,
152 GUEST_IA32_PAT = 0x2804,
153 GUEST_IA32_EFER = 0x2806,
154 GUEST_IA32_PERF_GLOBAL_CTRL = 0x2808,
166 HOST_FSBASE = 0x6C06,
167 HOST_GSBASE = 0x6C08,
168 HOST_TRBASE = 0x6C0A,
173 HOST_IA32_PAT = 0x2C00,
174 HOST_IA32_EFER = 0x2C02,
175 HOST_IA32_PERF_GLOBAL_CTRL = 0x2C04,
177 GUEST_CANINTR = 0x4824,
179 VM_INSTRERR = 0x4400,
180 VM_EXREASON = 0x4402,
181 VM_EXINTRINFO = 0x4404,
182 VM_EXINTRCODE = 0x4406,
183 VM_IDTVECINFO = 0x4408,
184 VM_IDTVECCODE = 0x440A,
185 VM_EXINSTRLEN = 0x440C,
186 VM_EXINSTRINFO = 0x440E,
187 VM_EXQUALIF = 0x6400,
205 CR0RSVD = 0x1ffaffc0,
206 CR4RSVD = 0xff889000,
212 CR0KERNEL = CR0RSVD | 0x30 | (uintptr)0xFFFFFFFF00000000ULL,
213 CR4KERNEL = CR4RSVD | CR4VMXE | CR4SMXE | CR4MCE | CR4PKE | (uintptr)0xFFFFFFFF00000000ULL
220 typedef struct Vmx Vmx;
221 typedef struct VmCmd VmCmd;
222 typedef struct VmMem VmMem;
223 typedef struct VmIntr VmIntr;
235 u32int info, code, ilen;
251 uintptr dr[8]; /* DR7 is also kept in VMCS */
266 VmCmd *firstcmd, **lastcmd;
278 VmIntr exc, irq, irqack;
280 u64int *msrhost, *msrguest;
294 int (*cmd)(VmCmd *, va_list);
301 static char Equit[] = "vmx: ending";
303 static char *statenames[] = {
305 [VMXINACTIVE] "inactive",
308 [VMXRUNNING] "running",
316 vmcsread(u32int addr)
322 rc = vmread(addr, (uintptr *) &val);
323 if(rc >= 0 && sizeof(uintptr) == 4 && (addr & 0x6000) == 0x2000)
324 rc = vmread(addr | 1, (uintptr *) &val + 1);
327 snprint(errbuf, sizeof(errbuf), "vmcsread failed (%#.4ux)", addr);
334 vmcswrite(u32int addr, u64int val)
338 rc = vmwrite(addr, val);
339 if(rc >= 0 && sizeof(uintptr) == 4 && (addr & 0x6000) == 0x2000)
340 rc = vmwrite(addr | 1, val >> 32);
343 snprint(errbuf, sizeof(errbuf), "vmcswrite failed (%#.4ux = %#.16ullx)", addr, val);
354 v = strtoull(s, &p, 0);
355 if(p == s || *p != 0) error("invalid value");
360 cr0fakeread(char *p, char *e)
362 uvlong guest, mask, shadow;
364 guest = vmcsread(GUEST_CR0);
365 mask = vmcsread(GUEST_CR0MASK);
366 shadow = vmcsread(GUEST_CR0SHADOW);
367 return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & ~mask | shadow & mask);
371 cr4fakeread(char *p, char *e)
373 uvlong guest, mask, shadow;
375 guest = vmcsread(GUEST_CR4);
376 mask = vmcsread(GUEST_CR4MASK);
377 shadow = vmcsread(GUEST_CR4SHADOW);
378 return seprint(p, e, "%#.*ullx", sizeof(uintptr) * 2, guest & ~mask | shadow & mask);
384 uvlong cr0, efer, nefer, ectrl;
386 if(sizeof(uintptr) != 8) return;
387 cr0 = vmcsread(GUEST_CR0);
388 efer = vmcsread(GUEST_IA32_EFER);
389 nefer = efer & ~0x400 | efer << 2 & cr0 >> 21 & 0x400;
390 if(efer == nefer) return;
391 vmcswrite(GUEST_IA32_EFER, nefer);
392 ectrl = vmcsread(VMENTRY_CTLS);
393 ectrl = ectrl & ~0x200 | nefer >> 1 & 0x200;
394 vmcswrite(VMENTRY_CTLS, ectrl);
398 cr0realwrite(char *s)
403 vmcswrite(GUEST_CR0, vmcsread(GUEST_CR0) & CR0KERNEL | v & ~CR0KERNEL);
409 cr0maskwrite(char *s)
414 vmcswrite(GUEST_CR0MASK, v | CR0KERNEL);
424 vmcswrite(GUEST_IA32_EFER, v);
430 cr4realwrite(char *s)
435 vmcswrite(GUEST_CR4, vmcsread(GUEST_CR4) & CR4KERNEL | v & ~CR4KERNEL);
440 cr4maskwrite(char *s)
445 vmcswrite(GUEST_CR4MASK, v | CR4KERNEL);
454 v = (u32int) parseval(s);
455 vmcswrite(GUEST_DR7, vmx.dr[7] = (u32int) v);
471 vmx.dr[6] = (u32int) v;
475 typedef struct GuestReg GuestReg;
478 u8int size; /* in bytes; 0 means == uintptr */
480 char *(*read)(char *, char *);
481 int (*write)(char *);
483 #define VMXVAR(x) ~(ulong)&(((Vmx*)0)->x)
484 #define UREG(x) VMXVAR(ureg.x)
485 static GuestReg guestregs[] = {
486 {GUEST_RIP, 0, "pc"},
487 {GUEST_RSP, 0, "sp"},
488 {GUEST_RFLAGS, 0, "flags"},
499 {UREG(r10), 0, "r10"},
500 {UREG(r11), 0, "r11"},
501 {UREG(r12), 0, "r12"},
502 {UREG(r13), 0, "r13"},
503 {UREG(r14), 0, "r14"},
504 {UREG(r15), 0, "r15"},
506 {GUEST_GDTRBASE, 0, "gdtrbase"},
507 {GUEST_GDTRLIMIT, 4, "gdtrlimit"},
508 {GUEST_IDTRBASE, 0, "idtrbase"},
509 {GUEST_IDTRLIMIT, 4, "idtrlimit"},
511 {GUEST_CSBASE, 0, "csbase"},
512 {GUEST_CSLIMIT, 4, "cslimit"},
513 {GUEST_CSPERM, 4, "csperm"},
515 {GUEST_DSBASE, 0, "dsbase"},
516 {GUEST_DSLIMIT, 4, "dslimit"},
517 {GUEST_DSPERM, 4, "dsperm"},
519 {GUEST_ESBASE, 0, "esbase"},
520 {GUEST_ESLIMIT, 4, "eslimit"},
521 {GUEST_ESPERM, 4, "esperm"},
523 {GUEST_FSBASE, 0, "fsbase"},
524 {GUEST_FSLIMIT, 4, "fslimit"},
525 {GUEST_FSPERM, 4, "fsperm"},
527 {GUEST_GSBASE, 0, "gsbase"},
528 {GUEST_GSLIMIT, 4, "gslimit"},
529 {GUEST_GSPERM, 4, "gsperm"},
531 {GUEST_SSBASE, 0, "ssbase"},
532 {GUEST_SSLIMIT, 4, "sslimit"},
533 {GUEST_SSPERM, 4, "ssperm"},
535 {GUEST_TRBASE, 0, "trbase"},
536 {GUEST_TRLIMIT, 4, "trlimit"},
537 {GUEST_TRPERM, 4, "trperm"},
538 {GUEST_LDTR, 2, "ldtr"},
539 {GUEST_LDTRBASE, 0, "ldtrbase"},
540 {GUEST_LDTRLIMIT, 4, "ldtrlimit"},
541 {GUEST_LDTRPERM, 4, "ldtrperm"},
542 {GUEST_CR0, 0, "cr0real", nil, cr0realwrite},
543 {GUEST_CR0SHADOW, 0, "cr0fake", cr0fakeread},
544 {GUEST_CR0MASK, 0, "cr0mask", nil, cr0maskwrite},
545 {VMXVAR(cr2), 0, "cr2"},
546 {GUEST_CR3, 0, "cr3"},
547 {GUEST_CR4, 0, "cr4real", nil, cr4realwrite},
548 {GUEST_CR4SHADOW, 0, "cr4fake", cr4fakeread},
549 {GUEST_CR4MASK, 0, "cr4mask", nil, cr4maskwrite},
550 {GUEST_IA32_PAT, 8, "pat"},
551 {GUEST_IA32_EFER, 8, "efer", nil, eferwrite},
552 {VMXVAR(dr[0]), 0, "dr0"},
553 {VMXVAR(dr[1]), 0, "dr1"},
554 {VMXVAR(dr[2]), 0, "dr2"},
555 {VMXVAR(dr[3]), 0, "dr3"},
556 {VMXVAR(dr[6]), 0, "dr6", nil, dr6write},
557 {GUEST_DR7, 0, "dr7", nil, dr7write},
558 {VM_INSTRERR, 4, "instructionerror", nil, readonly},
559 {VM_EXREASON, 4, "exitreason", nil, readonly},
560 {VM_EXQUALIF, 0, "exitqualification", nil, readonly},
561 {VM_EXINTRINFO, 4, "exitinterruptinfo", nil, readonly},
562 {VM_EXINTRCODE, 4, "exitinterruptcode", nil, readonly},
563 {VM_EXINSTRLEN, 4, "exitinstructionlen", nil, readonly},
564 {VM_EXINSTRINFO, 4, "exitinstructioninfo", nil, readonly},
565 {VM_GUESTVA, 0, "exitva", nil, readonly},
566 {VM_GUESTPA, 0, "exitpa", nil, readonly},
567 {VM_IDTVECINFO, 4, "idtinterruptinfo", nil, readonly},
568 {VM_IDTVECCODE, 4, "idtinterruptcode", nil, readonly},
572 vmokpage(u64int addr)
574 return (addr & 0xfff) == 0 && addr >> 48 == 0;
585 if(tab == nil) error(Egreg);
586 for(i = 3; i >= 1; i--){
587 tab += addr >> 12 + 9 * i & 0x1ff;
590 nt = mallocalign(BY2PG, BY2PG, 0, 0);
591 if(nt == nil) error(Enomem);
592 memset(nt, 0, BY2PG);
593 v = PADDR(nt) | 0x407;
596 tab = KADDR(v & ~0xfff);
598 return tab + (addr >> 12 & 0x1ff);
602 eptfree(uvlong *tab, int level)
607 if(tab == nil) error(Egreg);
609 for(i = 0; i < 512; i++){
611 if((v & 3) == 0) continue;
612 t = KADDR(v & ~0xfff);
613 eptfree(t, level + 1);
622 epttranslate(VmMem *mp)
626 if(mp->seg != nil && (mp->seg->type & SG_TYPE) != SG_FIXED || (mp->lo & 0xfff) != 0 || (mp->hi & 0xfff) != 0 || (uint)mp->attr >= 0x1000)
629 if(mp->seg->base + mp->off + (mp->hi - mp->lo) > mp->seg->top)
631 hpa = mp->seg->map[0]->pages[0]->pa + mp->off;
634 for(p = mp->lo; p < mp->hi; p += BY2PG)
635 *eptwalk(p) = hpa + (p - mp->lo) + mp->attr;
636 vmx.onentry |= FLUSHEPT;
639 static char *mtype[] = {"uc", "wc", "02", "03", "wt", "wp", "wb", "07"};
642 cmdgetmeminfo(VmCmd *, va_list va)
649 p0 = va_arg(va, char *);
650 e = va_arg(va, char *);
652 for(mp = vmx.mem.next; mp != &vmx.mem; mp = mp->next){
653 attr[0] = (mp->attr & 1) != 0 ? 'r' : '-';
654 attr[1] = (mp->attr & 2) != 0 ? 'w' : '-';
655 attr[2] = (mp->attr & 4) != 0 ? 'x' : '-';
657 *(ushort*)mt = *(u16int*)mtype[mp->attr >> 3 & 7];
658 mt[2] = (mp->attr & 0x40) != 0 ? '!' : 0;
661 p = seprint(p, e, "%s %s %#llux %#llux\n", attr, mt, mp->lo, mp->hi);
663 p = seprint(p, e, "%s %s %#llux %#llux %s %#llux\n", attr, mt, mp->lo, mp->hi, mp->name, (uvlong)mp->off);
669 cmdclearmeminfo(VmCmd *, va_list)
673 eptfree(vmx.pml4, 0);
674 for(mp = vmx.mem.next; mp != &vmx.mem; mp = mn){
680 vmx.mem.prev = &vmx.mem;
681 vmx.mem.next = &vmx.mem;
682 vmx.onentry |= FLUSHEPT;
686 extern Segment* (*_globalsegattach)(char*);
689 cmdsetmeminfo(VmCmd *, va_list va)
691 char *p0, *p, *q, *r;
699 p0 = va_arg(va, char *);
707 mp = malloc(sizeof(VmMem));
711 memset(mp, 0, sizeof(VmMem));
718 rc = tokenize(p, f, nelem(f));
720 if(rc == 0) goto next;
721 if(rc != 4 && rc != 6) error("number of fields wrong");
722 for(q = f[0]; *q != 0; q++)
724 case 'r': if((mp->attr & 1) != 0) goto tinval; mp->attr |= 1; break;
725 case 'w': if((mp->attr & 2) != 0) goto tinval; mp->attr |= 2; break;
726 case 'x': if((mp->attr & 4) != 0) goto tinval; mp->attr |= 0x404; break;
728 default: tinval: error("invalid access field");
730 for(j = 0; j < 8; j++)
731 if(strncmp(mtype[j], f[1], 2) == 0){
735 if(j == 8 || strlen(f[1]) > 3) error("invalid memory type");
736 if(f[1][2] == '!') mp->attr |= 0x40;
737 else if(f[1][2] != 0) error("invalid memory type");
738 mp->lo = strtoull(f[2], &r, 0);
739 if(*r != 0 || !vmokpage(mp->lo)) error("invalid low guest physical address");
740 mp->hi = strtoull(f[3], &r, 0);
741 if(*r != 0 || !vmokpage(mp->hi) || mp->hi <= mp->lo) error("invalid high guest physical address");
742 mp->off = strtoull(f[5], &r, 0);
743 if(*r != 0 || !vmokpage(mp->off)) error("invalid offset");
744 if((mp->attr & 7) != 0){
745 if(rc != 6) error("number of fields wrong");
746 mp->seg = _globalsegattach(f[4]);
747 if(mp->seg == nil) error("no such segment");
748 if(mp->seg->base + mp->off + (mp->hi - mp->lo) > mp->seg->top) error("out of bounds");
749 kstrdup(&mp->name, f[4]);
752 mp->prev = vmx.mem.prev;
771 if((regs[2] & 1<<5) == 0) return;
772 /* check if disabled by BIOS */
773 if(rdmsr(0x3a, &msr) < 0) return;
775 if((msr & 1) == 0){ /* msr still unlocked */
776 wrmsr(0x3a, msr | 5);
777 if(rdmsr(0x3a, &msr) < 0)
783 if(rdmsr(VMX_PROCB_CTLS_MSR, &msr) < 0) return;
784 if((vlong)msr >= 0) return;
785 if(rdmsr(VMX_PROCB_CTLS2_MSR, &msr) < 0) return;
786 if((msr >> 32 & PROCB_EPT) == 0 || (msr >> 32 & PROCB_VPID) == 0) return;
787 vmx.state = VMXINACTIVE;
788 vmx.lastcmd = &vmx.firstcmd;
789 vmx.mem.next = &vmx.mem;
790 vmx.mem.prev = &vmx.mem;
803 vmxaddmsr(u32int msr, u64int gval)
807 if(vmx.nmsr >= MAXMSR)
808 error("too many MSRs");
810 vmx.msrhost[i] = msr;
811 rdmsr(msr, (vlong *) &vmx.msrhost[i+1]);
812 vmx.msrguest[i] = msr;
813 vmx.msrguest[i+1] = gval;
814 vmcswrite(VMENTRY_MSRLDCNT, vmx.nmsr);
815 vmcswrite(VMEXIT_MSRSTCNT, vmx.nmsr);
816 vmcswrite(VMEXIT_MSRLDCNT, vmx.nmsr);
820 vmxtrapmsr(u32int msr, enum { TRAPRD = 1, TRAPWR = 2 } state)
824 if(msr >= 0x2000 && (u32int)(msr - 0xc0000000) >= 0x2000)
826 msr = msr & 0x1fff | msr >> 18 & 0x2000;
828 if((state & TRAPRD) != 0)
829 vmx.msrbits[msr / 32] |= m;
831 vmx.msrbits[msr / 32] &= ~m;
832 if((state & TRAPWR) != 0)
833 vmx.msrbits[msr / 32 + 512] |= m;
835 vmx.msrbits[msr / 32 + 512] &= ~m;
844 memset(&vmx.ureg, 0, sizeof(vmx.ureg));
848 if(rdmsr(VMX_BASIC_MSR, &msr) < 0) error("rdmsr(VMX_BASIC_MSR) failed");
849 if((msr & 1ULL<<55) != 0){
850 if(rdmsr(VMX_TRUE_PROCB_CTLS_MSR, &procb_ctls) < 0) error("rdmsr(VMX_TRUE_PROCB_CTLS_MSR) failed");
851 if(rdmsr(VMX_TRUE_PINB_CTLS_MSR, &pinb_ctls) < 0) error("rdmsr(VMX_TRUE_PINB_CTLS_MSR) failed");
853 if(rdmsr(VMX_PROCB_CTLS_MSR, &procb_ctls) < 0) error("rdmsr(VMX_PROCB_CTLS_MSR) failed");
854 if(rdmsr(VMX_PINB_CTLS_MSR, &pinb_ctls) < 0) error("rdmsr(VMX_PINB_CTLS_MSR) failed");
857 if(rdmsr(VMX_PINB_CTLS_MSR, &msr) < 0) error("rdmsr(VMX_PINB_CTLS_MSR failed");
858 x = (u32int)pinb_ctls | 1<<1 | 1<<2 | 1<<4; /* currently reserved default1 bits */
859 x |= PINB_EXITIRQ | PINB_EXITNMI;
860 x &= pinb_ctls >> 32;
861 vmcswrite(PINB_CTLS, x);
863 if(rdmsr(VMX_PROCB_CTLS_MSR, &msr) < 0) error("rdmsr(VMX_PROCB_CTLS_MSR failed");
864 x = (u32int)procb_ctls | 1<<1 | 7<<4 | 1<<8 | 1<<13 | 1<<14 | 1<<26; /* currently reserved default1 bits */
865 x |= PROCB_EXITHLT | PROCB_EXITMWAIT;
866 x |= PROCB_EXITMOVDR | PROCB_EXITIO | PROCB_EXITMONITOR | PROCB_MSRBITMAP;
869 vmcswrite(PROCB_CTLS, x);
871 if(rdmsr(VMX_PROCB_CTLS2_MSR, &msr) < 0) error("rdmsr(VMX_PROCB_CTLS2_MSR failed");
872 x = PROCB_EPT | PROCB_VPID | PROCB_UNRESTR;
874 vmcswrite(PROCB_CTLS2, x);
876 if(rdmsr(VMX_VMEXIT_CTLS_MSR, &msr) < 0) error("rdmsr(VMX_VMEXIT_CTLS_MSR failed");
878 if(sizeof(uintptr) == 8) x |= VMEXIT_HOST64;
879 x |= VMEXIT_LD_IA32_PAT | VMEXIT_LD_IA32_EFER | VMEXIT_ST_DEBUG | VMEXIT_ST_IA32_EFER;
881 vmcswrite(VMEXIT_CTLS, x);
883 if(rdmsr(VMX_VMENTRY_CTLS_MSR, &msr) < 0) error("rdmsr(VMX_VMENTRY_CTLS_MSR failed");
885 x |= VMENTRY_LD_IA32_PAT | VMENTRY_LD_IA32_EFER | VMENTRY_LD_DEBUG;
887 vmcswrite(VMENTRY_CTLS, x);
889 vmcswrite(CR3_TARGCNT, 0);
890 vmcswrite(VMENTRY_INTRINFO, 0);
891 vmcswrite(VMCS_LINK, -1);
893 vmcswrite(HOST_CS, KESEL);
894 vmcswrite(HOST_DS, KDSEL);
895 vmcswrite(HOST_ES, KDSEL);
896 vmcswrite(HOST_FS, KDSEL);
897 vmcswrite(HOST_GS, KDSEL);
898 vmcswrite(HOST_SS, KDSEL);
899 vmcswrite(HOST_TR, TSSSEL);
900 vmcswrite(HOST_CR0, getcr0() & ~0xe);
901 vmcswrite(HOST_CR3, getcr3());
902 vmcswrite(HOST_CR4, getcr4());
904 vmcswrite(HOST_FSBASE, msr);
906 vmcswrite(HOST_GSBASE, msr);
907 vmcswrite(HOST_TRBASE, (uintptr) m->tss);
908 vmcswrite(HOST_GDTR, (uintptr) m->gdt);
909 vmcswrite(HOST_IDTR, IDTADDR);
910 if(rdmsr(0x277, &msr) < 0) error("rdmsr(IA32_PAT) failed");
911 vmcswrite(HOST_IA32_PAT, msr);
912 if(rdmsr(Efer, &msr) < 0) error("rdmsr(IA32_EFER) failed");
913 vmcswrite(HOST_IA32_EFER, msr);
915 vmcswrite(EXC_BITMAP, 1<<18|1<<1);
916 vmcswrite(PFAULT_MASK, 0);
917 vmcswrite(PFAULT_MATCH, 0);
919 vmcswrite(GUEST_CSBASE, 0);
920 vmcswrite(GUEST_DSBASE, 0);
921 vmcswrite(GUEST_ESBASE, 0);
922 vmcswrite(GUEST_FSBASE, 0);
923 vmcswrite(GUEST_GSBASE, 0);
924 vmcswrite(GUEST_SSBASE, 0);
925 vmcswrite(GUEST_CSLIMIT, -1);
926 vmcswrite(GUEST_DSLIMIT, -1);
927 vmcswrite(GUEST_ESLIMIT, -1);
928 vmcswrite(GUEST_FSLIMIT, -1);
929 vmcswrite(GUEST_GSLIMIT, -1);
930 vmcswrite(GUEST_SSLIMIT, -1);
931 vmcswrite(GUEST_CSPERM, (SEGG|SEGD|SEGP|SEGPL(0)|SEGEXEC|SEGR) >> 8 | 1);
932 vmcswrite(GUEST_DSPERM, (SEGG|SEGB|SEGP|SEGPL(0)|SEGDATA|SEGW) >> 8 | 1);
933 vmcswrite(GUEST_ESPERM, (SEGG|SEGB|SEGP|SEGPL(0)|SEGDATA|SEGW) >> 8 | 1);
934 vmcswrite(GUEST_FSPERM, (SEGG|SEGB|SEGP|SEGPL(0)|SEGDATA|SEGW) >> 8 | 1);
935 vmcswrite(GUEST_GSPERM, (SEGG|SEGB|SEGP|SEGPL(0)|SEGDATA|SEGW) >> 8 | 1);
936 vmcswrite(GUEST_SSPERM, (SEGG|SEGB|SEGP|SEGPL(0)|SEGDATA|SEGW) >> 8 | 1);
937 vmcswrite(GUEST_LDTRPERM, 1<<16);
939 vmcswrite(GUEST_CR0MASK, CR0KERNEL);
940 vmcswrite(GUEST_CR4MASK, CR4KERNEL);
941 vmcswrite(GUEST_CR0, getcr0() & CR0KERNEL | 0x31);
942 vmcswrite(GUEST_CR3, 0);
943 vmcswrite(GUEST_CR4, getcr4() & CR4KERNEL);
944 vmcswrite(GUEST_CR0SHADOW, getcr0() & CR0KERNEL | 0x31);
945 vmcswrite(GUEST_CR4SHADOW, getcr4() & ~CR4VMXE & CR4KERNEL);
947 vmcswrite(GUEST_IA32_PAT, 0x0007040600070406ULL);
948 vmcswrite(GUEST_IA32_EFER, 0);
950 vmcswrite(GUEST_TRBASE, 0);
951 vmcswrite(GUEST_TRLIMIT, 0xffff);
952 vmcswrite(GUEST_TRPERM, (SEGTSS|SEGPL(0)|SEGP) >> 8 | 2);
954 vmx.pml4 = mallocalign(BY2PG, BY2PG, 0, 0);
955 memset(vmx.pml4, 0, BY2PG);
956 vmcswrite(VM_EPTP, PADDR(vmx.pml4) | 3<<3);
958 vmcswrite(VM_VPID, vmx.vpid);
960 vmcswrite(GUEST_RFLAGS, 2);
962 vmx.onentry = FLUSHVPID | FLUSHEPT;
964 vmx.fp = mallocalign(512, 512, 0, 0);
970 vmx.msrhost = mallocalign(MAXMSR*16, 16, 0, 0);
971 vmx.msrguest = mallocalign(MAXMSR*16, 16, 0, 0);
972 vmx.msrbits = mallocalign(4096, 4096, 0, 0);
973 if(vmx.msrhost == nil || vmx.msrguest == nil || vmx.msrbits == nil)
975 memset(vmx.msrbits, -1, 4096);
977 vmcswrite(VMENTRY_MSRLDADDR, PADDR(vmx.msrguest));
978 vmcswrite(VMEXIT_MSRSTADDR, PADDR(vmx.msrguest));
979 vmcswrite(VMEXIT_MSRLDADDR, PADDR(vmx.msrhost));
980 vmcswrite(MSR_BITMAP, PADDR(vmx.msrbits));
982 if(sizeof(uintptr) == 8){
986 vmxaddmsr(Sfmask, 0);
987 vmxaddmsr(KernelGSbase, 0);
989 vmxtrapmsr(Lstar, 0);
990 vmxtrapmsr(Cstar, 0);
991 vmxtrapmsr(Sfmask, 0);
992 vmxtrapmsr(FSbase, 0);
993 vmxtrapmsr(GSbase, 0);
994 vmxtrapmsr(KernelGSbase, 0);
1001 static uchar *vmcs; /* also vmxon region */
1006 putcr4(getcr4() | 0x2000); /* set VMXE */
1007 putcr0(getcr0() | 0x20); /* set NE */
1009 if(rdmsr(VMX_CR0_FIXED0, &msr) < 0) error("rdmsr(VMX_CR0_FIXED0) failed");
1010 if(rdmsr(VMX_CR0_FIXED1, &msr2) < 0) error("rdmsr(VMX_CR0_FIXED1) failed");
1011 if((cr & ~msr & ~msr2 | ~cr & msr & msr2) != 0) error("invalid CR0 value");
1013 if(rdmsr(VMX_CR4_FIXED0, &msr) < 0) error("rdmsr(VMX_CR4_FIXED0) failed");
1014 if(rdmsr(VMX_CR4_FIXED1, &msr2) < 0) error("rdmsr(VMX_CR4_FIXED1) failed");
1015 if((cr & ~msr & ~msr2 | ~cr & msr & msr2) != 0) error("invalid CR4 value");
1018 vmcs = mallocalign(8192, 4096, 0, 0);
1022 memset(vmcs, 0, 8192);
1023 rdmsr(VMX_BASIC_MSR, &x);
1025 *(ulong*)&vmcs[4096] = x;
1026 if(vmxon(PADDR(vmcs + 4096)) < 0)
1027 error("vmxon failed");
1029 if(vmclear(PADDR(vmcs)) < 0)
1030 error("vmclear failed");
1031 if(vmptrld(PADDR(vmcs)) < 0)
1032 error("vmptrld failed");
1037 cmdrelease(VmCmd *p, int f)
1040 p->flags |= CMDFDONE | f;
1046 killcmds(VmCmd *notme)
1050 for(p = vmx.postponed; p != nil; p = pn){
1053 if(p == notme) continue;
1054 kstrcpy(p->errstr, Equit, ERRMAX);
1055 cmdrelease(p, CMDFFAIL);
1057 vmx.postponed = nil;
1058 ilock(&vmx.cmdlock);
1059 for(p = vmx.firstcmd; p != nil; p = pn){
1062 if(p == notme) continue;
1063 kstrcpy(p->errstr, Equit, ERRMAX);
1064 cmdrelease(p, CMDFFAIL);
1067 vmx.lastcmd = &vmx.firstcmd;
1068 iunlock(&vmx.cmdlock);
1072 cmdquit(VmCmd *p, va_list va)
1074 vmx.state = VMXENDING;
1077 if(vmx.pml4 != nil){
1078 cmdclearmeminfo(p, va);
1094 vmx.state = VMXINACTIVE;
1105 reason = vmcsread(VM_EXREASON);
1106 if((reason & 1<<31) == 0)
1107 switch(reason & 0xffff){
1108 case 1: /* external interrupt */
1111 case 5: /* IO SMI */
1113 case 7: /* IRQ window */
1114 case 8: /* NMI window */
1117 if((vmx.onentry & STEP) != 0){
1118 vmx.state = VMXREADY;
1120 vmx.onentry &= ~STEP;
1125 if((vmx.onentry & STEP) != 0){
1126 print("VMX: exit reason %#x when expected step...\n", reason & 0xffff);
1127 vmx.onentry &= ~STEP;
1128 vmx.got |= GOTSTEP|GOTSTEPERR;
1130 vmx.state = VMXREADY;
1135 cmdgetregs(VmCmd *, va_list va)
1143 p0 = va_arg(va, char *);
1144 e = va_arg(va, char *);
1146 for(r = guestregs; r < guestregs + nelem(guestregs); r++)
1148 p = seprint(p, e, "%s ", r->name);
1150 p = strecpy(p, e, "\n");
1153 val = vmcsread(r->offset);
1155 val = *(uintptr*)((uchar*)&vmx + ~r->offset);
1157 if(s == 0) s = sizeof(uintptr);
1158 p = seprint(p, e, "%s %#.*llux\n", r->name, s * 2, val);
1164 setregs(char *p0, char rs, char *fs)
1178 rc = getfields(p, f, nelem(f), 1, fs);
1180 if(rc == 0) continue;
1181 if(rc != 2) error("number of fields wrong");
1183 for(r = guestregs; r < guestregs + nelem(guestregs); r++)
1184 if(strcmp(r->name, f[0]) == 0)
1186 if(r == guestregs + nelem(guestregs))
1187 error("unknown register");
1188 if(r->write != nil){
1192 val = strtoull(f[1], &rp, 0);
1194 if(sz == 0) sz = sizeof(uintptr);
1195 if(rp == f[1] || *rp != 0) error("invalid value");
1197 vmcswrite(r->offset, val);
1199 assert((u32int)~r->offset + sz <= sizeof(Vmx));
1201 case 1: *(u8int*)((u8int*)&vmx + (u32int)~r->offset) = val; break;
1202 case 2: *(u16int*)((u8int*)&vmx + (u32int)~r->offset) = val; break;
1203 case 4: *(u32int*)((u8int*)&vmx + (u32int)~r->offset) = val; break;
1204 case 8: *(u64int*)((u8int*)&vmx + (u32int)~r->offset) = val; break;
1205 default: error(Egreg);
1213 cmdsetregs(VmCmd *, va_list va)
1215 return setregs(va_arg(va, char *), '\n', " \t");
1219 cmdgetfpregs(VmCmd *, va_list va)
1223 p = va_arg(va, uchar *);
1224 memmove(p, vmx.fp, sizeof(FPsave));
1225 return sizeof(FPsave);
1229 cmdsetfpregs(VmCmd *, va_list va)
1235 p = va_arg(va, uchar *);
1236 n = va_arg(va, ulong);
1237 off = va_arg(va, vlong);
1238 if(off < 0 || off >= sizeof(FPsave)) n = 0;
1239 else if(off + n > sizeof(FPsave)) n = sizeof(FPsave) - n;
1240 memmove((uchar*)vmx.fp + off, p, n);
1245 cmdgo(VmCmd *, va_list va)
1249 if(vmx.state != VMXREADY)
1250 error("VM not ready");
1251 r = va_arg(va, char *);
1252 if(r != nil) setregs(r, ';', "=");
1253 vmx.state = VMXRUNNING;
1258 cmdstop(VmCmd *, va_list)
1260 if(vmx.state != VMXREADY && vmx.state != VMXRUNNING)
1261 error("VM not ready or running");
1262 vmx.state = VMXREADY;
1267 cmdstatus(VmCmd *, va_list va)
1269 kstrcpy(va_arg(va, char *), vmx.errstr, ERRMAX);
1273 static char *exitreasons[] = {
1274 [0] "exc", [1] "extirq", [2] "triplef", [3] "initsig", [4] "sipi", [5] "smiio", [6] "smiother", [7] "irqwin",
1275 [8] "nmiwin", [9] "taskswitch", [10] ".cpuid", [11] ".getsec", [12] ".hlt", [13] ".invd", [14] ".invlpg", [15] ".rdpmc",
1276 [16] ".rdtsc", [17] ".rsm", [18] ".vmcall", [19] ".vmclear", [20] ".vmlaunch", [21] ".vmptrld", [22] ".vmptrst", [23] ".vmread",
1277 [24] ".vmresume", [25] ".vmwrite", [26] ".vmxoff", [27] ".vmxon", [28] "movcr", [29] ".movdr", [30] "io", [31] ".rdmsr",
1278 [32] ".wrmsr", [33] "entrystate", [34] "entrymsr", [36] ".mwait", [37] "monitortrap", [39] ".monitor",
1279 [40] ".pause", [41] "mcheck", [43] "tpr", [44] "apicacc", [45] "eoi", [46] "gdtr_idtr", [47] "ldtr_tr",
1280 [48] "eptfault", [49] "eptinval", [50] ".invept", [51] ".rdtscp", [52] "preempt", [53] ".invvpid", [54] ".wbinvd", [55] ".xsetbv",
1281 [56] "apicwrite", [57] ".rdrand", [58] ".invpcid", [59] ".vmfunc", [60] ".encls", [61] ".rdseed", [62] "pmlfull", [63] ".xsaves",
1285 static char *except[] = {
1286 [0] "#de", [1] "#db", [3] "#bp", [4] "#of", [5] "#br", [6] "#ud", [7] "#nm",
1287 [8] "#df", [10] "#ts", [11] "#np", [12] "#ss", [13] "#gp", [14] "#pf",
1288 [16] "#mf", [17] "#ac", [18] "#mc", [19] "#xm", [20] "#ve",
1292 cmdwait(VmCmd *cp, va_list va)
1295 u32int reason, intr;
1301 p0 = p = va_arg(va, char *);
1302 e = va_arg(va, char *);
1303 if((vmx.got & GOTIRQACK) != 0){
1304 p = seprint(p, e, "*ack %d\n", vmx.irqack.info & 0xff);
1305 vmx.got &= ~GOTIRQACK;
1308 if((vmx.got & GOTEXIT) == 0){
1309 cp->flags |= CMDFPOSTP;
1312 vmx.got &= ~GOTEXIT;
1313 reason = vmcsread(VM_EXREASON);
1314 qual = vmcsread(VM_EXQUALIF);
1316 intr = vmcsread(VM_EXINTRINFO);
1317 if((reason & 1<<31) != 0)
1318 p = seprint(p, e, "!");
1319 if(rno == 0 && (intr & 1<<31) != 0){
1320 if((intr & 0xff) >= nelem(except) || except[intr & 0xff] == nil)
1321 p = seprint(p, e, "#%d ", intr & 0xff);
1323 p = seprint(p, e, "%s ", except[intr & 0xff]);
1324 }else if(rno >= nelem(exitreasons) || exitreasons[rno] == nil)
1325 p = seprint(p, e, "?%d ", rno);
1327 p = seprint(p, e, "%s ", exitreasons[rno]);
1328 p = seprint(p, e, "%#ullx pc %#ullx sp %#ullx ilen %#ullx iinfo %#ullx", qual, vmcsread(GUEST_RIP), vmcsread(GUEST_RSP), vmcsread(VM_EXINSTRLEN), vmcsread(VM_EXINSTRINFO));
1329 if((intr & 1<<11) != 0) p = seprint(p, e, " excode %#ullx", vmcsread(VM_EXINTRCODE));
1330 if(rno == 48 && (qual & 0x80) != 0) p = seprint(p, e, " va %#ullx", vmcsread(VM_GUESTVA));
1331 if(rno == 48 || rno == 49) p = seprint(p, e, " pa %#ullx", vmcsread(VM_GUESTPA));
1332 if(rno == 30) p = seprint(p, e, " ax %#ullx", (uvlong)vmx.ureg.ax);
1333 p = seprint(p, e, "\n");
1338 cmdstep(VmCmd *cp, va_list va)
1342 if((vmx.got & GOTSTEP) != 0 || (vmx.onentry & STEP) != 0)
1344 if(vmx.state != VMXREADY){
1345 print("pre-step in state %s\n", statenames[vmx.state]);
1348 vmx.stepmap = va_arg(va, VmMem *);
1349 vmx.onentry |= STEP;
1350 vmx.state = VMXRUNNING;
1351 cp->flags |= CMDFPOSTP;
1354 if(vmx.state != VMXREADY){
1355 print("post-step in state %s\n", statenames[vmx.state]);
1356 vmx.onentry &= ~STEP;
1357 vmx.got &= ~(GOTSTEP|GOTSTEPERR);
1360 if((vmx.got & GOTSTEP) == 0){
1361 cp->flags |= CMDFPOSTP;
1364 if((vmx.got & GOTSTEPERR) != 0){
1365 vmx.got &= ~(GOTSTEP|GOTSTEPERR);
1366 error("step failed");
1368 vmx.got &= ~(GOTSTEP|GOTSTEPERR);
1375 eventparse(char *p, VmIntr *vi)
1380 memset(vi, 0, sizeof(VmIntr));
1385 memset(vi, 0, sizeof(VmIntr));
1390 if(r != nil) *r++ = 0;
1391 for(i = 0; i < nelem(except); i++)
1392 if(except[i] != nil && strcmp(except[i], q) == 0)
1398 if(i == nelem(except)){
1399 i = strtoul(q, &q, 10);
1400 if(*q != 0 || i > 255) error(Ebadctl);
1403 if((vi->info & 0x7ff) == 3 || (vi->info & 0x7ff) == 4)
1405 if(r == nil) goto out;
1407 vi->code = strtoul(r, &r, 0);
1411 vi->ilen = strtoul(r + 1, &r, 0);
1412 if(*r != 0) error(Ebadctl);
1419 cmdexcept(VmCmd *cp, va_list va)
1421 if(cp->scratched) error(Eintr);
1422 if((vmx.onentry & POSTEX) != 0){
1423 cp->flags |= CMDFPOSTP;
1426 eventparse(va_arg(va, char *), &vmx.exc);
1427 vmx.onentry |= POSTEX;
1432 cmdirq(VmCmd *, va_list va)
1437 p = va_arg(va, char *);
1439 vmx.onentry &= ~POSTIRQ;
1443 vmx.onentry |= POSTIRQ;
1454 ilock(&vmx.cmdlock);
1455 rc = vmx.firstcmd != nil;
1456 iunlock(&vmx.cmdlock);
1461 markcmddone(VmCmd *p, VmCmd ***pp)
1463 if((p->flags & (CMDFFAIL|CMDFPOSTP)) == CMDFPOSTP){
1467 p->flags = p->flags & ~CMDFPOSTP;
1473 markppcmddone(VmCmd **pp)
1478 if((p->flags & (CMDFFAIL|CMDFPOSTP)) == CMDFPOSTP)
1482 p->flags = p->flags & ~CMDFPOSTP;
1493 for(pp = &vmx.postponed; p = *pp, p != nil; ){
1495 kstrcpy(p->errstr, up->errstr, ERRMAX);
1496 p->flags |= CMDFFAIL;
1497 pp = markppcmddone(pp);
1500 p->flags &= ~CMDFPOSTP;
1501 p->retval = p->cmd(p, p->va);
1503 pp = markppcmddone(pp);
1506 ilock(&vmx.cmdlock);
1509 iunlock(&vmx.cmdlock);
1512 vmx.firstcmd = p->next;
1513 if(vmx.lastcmd == &p->next)
1514 vmx.lastcmd = &vmx.firstcmd;
1515 iunlock(&vmx.cmdlock);
1518 kstrcpy(p->errstr, up->errstr, ERRMAX);
1519 p->flags |= CMDFFAIL;
1520 markcmddone(p, &pp);
1523 if(p->scratched) error(Eintr);
1524 p->retval = p->cmd(p, p->va);
1526 markcmddone(p, &pp);
1533 static uvlong oldmap;
1534 static uvlong *mapptr;
1537 if(vmx.stepmap != nil){
1538 mapptr = eptwalk(vmx.stepmap->lo);
1540 epttranslate(vmx.stepmap);
1543 vmcswrite(PROCB_CTLS, vmcsread(PROCB_CTLS) & ~(uvlong)PROCB_MONTRAP);
1544 if(vmx.stepmap != nil){
1547 vmx.onentry |= FLUSHEPT;
1556 u32int procbctls, defprocbctls;
1564 kstrcpy(vmx.errstr, up->errstr, ERRMAX);
1565 vmx.state = VMXDEAD;
1571 vmx.state = VMXREADY;
1572 defprocbctls = vmcsread(PROCB_CTLS);
1575 if(vmx.state == VMXRUNNING){
1576 procbctls = defprocbctls;
1577 if((vmx.onentry & STEP) != 0){
1578 procbctls |= PROCB_MONTRAP;
1585 if((vmx.onentry & POSTEX) != 0){
1586 vmcswrite(VMENTRY_INTRINFO, vmx.exc.info);
1587 vmcswrite(VMENTRY_INTRCODE, vmx.exc.code);
1588 vmcswrite(VMENTRY_INTRILEN, vmx.exc.ilen);
1589 vmx.onentry &= ~POSTEX;
1591 if((vmx.onentry & POSTIRQ) != 0 && (vmx.onentry & STEP) == 0){
1592 if((vmx.onentry & POSTEX) == 0 && (vmcsread(GUEST_RFLAGS) & 1<<9) != 0 && (vmcsread(GUEST_CANINTR) & 3) == 0){
1593 vmcswrite(VMENTRY_INTRINFO, vmx.irq.info);
1594 vmcswrite(VMENTRY_INTRCODE, vmx.irq.code);
1595 vmcswrite(VMENTRY_INTRILEN, vmx.irq.ilen);
1596 vmx.onentry &= ~POSTIRQ;
1597 vmx.got |= GOTIRQACK;
1598 vmx.irqack = vmx.irq;
1600 procbctls |= PROCB_IRQWIN;
1602 if((vmx.onentry & FLUSHVPID) != 0){
1603 if(invvpid(INVLOCAL, vmx.vpid, 0) < 0)
1604 error("invvpid failed");
1605 vmx.onentry &= ~FLUSHVPID;
1607 if((vmx.onentry & FLUSHEPT) != 0){
1608 if(invept(INVLOCAL, PADDR(vmx.pml4) | 3<<3, 0) < 0)
1609 error("invept failed");
1610 vmx.onentry &= ~FLUSHEPT;
1612 vmcswrite(PROCB_CTLS, procbctls);
1613 vmx.got &= ~GOTEXIT;
1616 if(sizeof(uintptr) == 8){
1618 vmwrite(HOST_FSBASE, v);
1620 if((vmx.dr[7] & ~0xd400) != 0)
1622 fpsserestore(vmx.fp);
1624 rc = vmlaunch(&vmx.ureg, vmx.launched);
1629 error("vmlaunch failed");
1631 if((vmx.onentry & STEP) != 0){
1637 up->psstate = "Idle";
1638 sleep(&vmx.cmdwait, gotcmd, nil);
1654 static Dirtab vmxdir[] = {
1655 ".", { Qdir, 0, QTDIR }, 0, 0550,
1656 "ctl", { Qctl, 0, 0 }, 0, 0660,
1657 "regs", { Qregs, 0, 0 }, 0, 0660,
1658 "status", { Qstatus, 0, 0 }, 0, 0440,
1659 "map", { Qmap, 0, 0 }, 0, 0660,
1660 "wait", { Qwait, 0, 0 }, 0, 0440,
1661 "fpregs", { Qfpregs, 0, 0 }, 0, 0660,
1674 static Cmdtab vmxctlmsg[] = {
1687 return (((VmCmd*)cp)->flags & CMDFDONE) != 0;
1691 vmxcmd(int (*f)(VmCmd *, va_list), ...)
1695 if(vmx.state == VMXINACTIVE)
1697 if(vmx.state == VMXENDING)
1700 memset(&cmd, 0, sizeof(VmCmd));
1701 cmd.errstr = up->errstr;
1703 va_start(cmd.va, f);
1705 ilock(&vmx.cmdlock);
1706 if(vmx.state == VMXENDING){
1707 iunlock(&vmx.cmdlock);
1710 *vmx.lastcmd = &cmd;
1711 vmx.lastcmd = &cmd.next;
1712 iunlock(&vmx.cmdlock);
1716 wakeup(&vmx.cmdwait);
1718 sleep(&cmd, iscmddone, &cmd);
1719 while(!iscmddone(&cmd));
1723 if((cmd.flags & CMDFFAIL) != 0)
1729 vmxattach(char *spec)
1731 if(vmx.state == NOVMX) error(Enodev);
1732 return devattach('X', spec);
1736 vmxwalk(Chan *c, Chan *nc, char **name, int nname)
1738 return devwalk(c, nc, name, nname, vmxdir, nelem(vmxdir), devgen);
1742 vmxstat(Chan *c, uchar *dp, int n)
1744 return devstat(c, dp, n, vmxdir, nelem(vmxdir), devgen);
1748 vmxopen(Chan* c, int omode)
1752 if(c->qid.path != Qdir && !iseve()) error(Eperm);
1753 ch = devopen(c, omode, vmxdir, nelem(vmxdir), devgen);
1754 if(ch->qid.path == Qmap){
1755 if((omode & OTRUNC) != 0)
1756 vmxcmd(cmdclearmeminfo);
1767 vmxread(Chan* c, void* a, long n, vlong off)
1769 static char regbuf[4096];
1770 static char membuf[4096];
1773 switch((ulong)c->qid.path){
1775 return devdirread(c, a, n, vmxdir, nelem(vmxdir), devgen);
1778 vmxcmd(cmdgetregs, regbuf, regbuf + sizeof(regbuf));
1779 return readstr(off, a, n, regbuf);
1782 vmxcmd(cmdgetmeminfo, membuf, membuf + sizeof(membuf));
1783 return readstr(off, a, n, membuf);
1786 char buf[ERRMAX+128];
1787 char errbuf[ERRMAX];
1791 if(status == VMXDEAD){
1792 vmxcmd(cmdstatus, errbuf);
1793 snprint(buf, sizeof(buf), "%s %#q\n", statenames[status], errbuf);
1794 }else if(status >= 0 && status < nelem(statenames))
1795 snprint(buf, sizeof(buf), "%s\n", statenames[status]);
1797 snprint(buf, sizeof(buf), "%d\n", status);
1798 return readstr(off, a, n, buf);
1804 rc = vmxcmd(cmdwait, buf, buf + sizeof(buf));
1806 if(rc > 0) memmove(a, buf, rc);
1811 char buf[sizeof(FPsave)];
1813 vmxcmd(cmdgetfpregs, buf);
1814 if(n < 0 || off < 0 || off >= sizeof(buf)) n = 0;
1815 else if(off + n > sizeof(buf)) n = sizeof(buf) - off;
1816 if(n != 0) memmove(a, buf + off, n);
1827 vmxwrite(Chan* c, void* a, long n, vlong off)
1829 static QLock initlock;
1837 switch((ulong)c->qid.path){
1841 cb = parsecmd(a, n);
1846 ct = lookupcmd(cb, vmxctlmsg, nelem(vmxctlmsg));
1854 if(vmx.state != VMXINACTIVE)
1855 error("vmx already active");
1856 vmx.state = VMXINIT;
1857 kproc("kvmx", vmxproc, nil);
1860 if(vmxcmd(cmdstatus, up->errstr) == VMXDEAD)
1868 if(cb->nf == 2) kstrdup(&s, cb->f[1]);
1869 else if(cb->nf != 1) error(Ebadarg);
1883 for(i = 1; i < cb->nf; i++)
1884 if(strcmp(cb->f[i], "-map") == 0){
1886 if(i+4 > cb->nf) error("missing argument");
1887 memset(&tmpmem, 0, sizeof(tmpmem));
1888 tmpmem.lo = strtoull(cb->f[i+1], &s, 0);
1889 if(*s != 0 || !vmokpage(tmpmem.lo)) error("invalid address");
1890 tmpmem.hi = tmpmem.lo + BY2PG;
1891 tmpmem.attr = 0x407;
1892 tmpmem.seg = _globalsegattach(cb->f[i+2]);
1893 if(tmpmem.seg == nil) error("unknown segment");
1894 tmpmem.off = strtoull(cb->f[i+3], &s, 0);
1895 if(*s != 0 || !vmokpage(tmpmem.off)) error("invalid offset");
1899 vmxcmd(cmdstep, rc ? &tmpmem : nil);
1903 kstrdup(&s, cb->f[1]);
1908 vmxcmd(cmdexcept, s);
1915 kstrdup(&s, cb->f[1]);
1933 if(s == nil) error(Enomem);
1940 rc = vmxcmd((ulong)c->qid.path == Qregs ? cmdsetregs : cmdsetmeminfo, s);
1946 char buf[sizeof(FPsave)];
1948 if(n > sizeof(FPsave)) n = sizeof(FPsave);
1950 return vmxcmd(cmdsetfpregs, buf, n, off);