]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/exith.c
merge
[plan9front.git] / sys / src / cmd / vmx / exith.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 int persist = 1;
9
10 typedef struct ExitInfo ExitInfo;
11 struct ExitInfo {
12         char *raw;
13         char *name;
14         uvlong qual;
15         uvlong pa, va;
16         u32int ilen, iinfo;
17 };
18
19 char *x86reg[16] = {
20         RAX, RCX, RDX, RBX,
21         RSP, RBP, RSI, RDI,
22         R8, R9, R10, R11,
23         R12, R13, R14, R15
24 };
25 char *x86segreg[8] = {
26         "cs", "ds", "es", "fs", "gs", "ss",
27 };
28
29 static void
30 skipinstr(ExitInfo *ei)
31 {
32         rset(RPC, rget(RPC) + ei->ilen);
33 }
34
35 static void
36 iohandler(ExitInfo *ei)
37 {
38         int port, len, inc, isin;
39         int asz, seg;
40         uintptr addr;
41         u32int val;
42         uvlong vval;
43         uintptr cx;
44         static int seglook[8] = {SEGES, SEGCS, SEGSS, SEGDS, SEGFS, SEGGS};
45         TLB tlb;
46         
47         port = ei->qual >> 16 & 0xffff;
48         len = (ei->qual & 7) + 1;
49         isin = (ei->qual & 8) != 0;
50         if((ei->qual & 1<<4) == 0){ /* not a string instruction */
51                 if(isin){
52                         val = io(1, port, 0, len);
53                         rsetsz(RAX, val, len);
54                 }else
55                         io(0, port, rget(RAX), len);
56                 skipinstr(ei);
57                 return;
58         }
59         if((rget("flags") & 0x400) != 0) inc = -len;
60         else inc = len;
61         switch(ei->iinfo >> 7 & 7){
62         case 0: asz = 2; break;
63         default: asz = 4; break;
64         case 2: asz = 8; break;
65         }
66         if((ei->qual & 1<<5) != 0)
67                 cx = rgetsz(RCX, asz);
68         else
69                 cx = 1;
70         addr = isin ? rget(RDI) : rget(RSI);
71         if(isin)
72                 seg = SEGES;
73         else
74                 seg = seglook[ei->iinfo >> 15 & 7];
75         memset(&tlb, 0, sizeof(TLB));
76         for(; cx > 0; cx--){
77                 if(isin){
78                         vval = io(1, port, 0, len);
79                         if(x86access(seg, addr, asz, &vval, len, ACCW, &tlb) < 0)
80                                 goto err;
81                 }else{
82                         if(x86access(seg, addr, asz, &vval, len, ACCR, &tlb) < 0)
83                                 goto err;
84                         io(0, port, vval, len);
85                 }
86                 addr += inc;
87         }
88         skipinstr(ei);
89 err:
90         if((ei->qual & 1<<5) != 0)
91                 rsetsz(RCX, cx, asz);
92         if(isin)
93                 rsetsz(RDI, addr, asz);
94         else
95                 rsetsz(RSI, addr, asz);
96 }
97
98 static uvlong
99 defaultmmio(int op, uvlong addr, uvlong val)
100 {
101         switch(op){
102         case MMIORD:
103                 vmerror("read from unmapped address %#ullx (pc=%#ullx)", addr, rget(RPC));
104                 break;
105         case MMIOWR:
106                 vmerror("write to unmapped address %#ullx (val=%#ullx,pc=%#ullx)", addr, val, rget(RPC));
107                 break;
108         }
109         return 0;
110 }
111
112 static void
113 eptfault(ExitInfo *ei)
114 {
115         if(x86step() > 0)
116                 skipinstr(ei);
117 }
118
119 typedef struct CPUID CPUID;
120 struct CPUID {
121         u32int idx;
122         u32int ax, bx, cx, dx;
123 };
124 static CPUID *cpuidf;
125 static int ncpuidf;
126
127 static void
128 auxcpuidproc(void *vpfd)
129 {
130         int *pfd;
131         
132         pfd = vpfd;
133         close(pfd[1]);
134         close(0);
135         open("/dev/null", OREAD);
136         dup(pfd[0], 1);
137         close(pfd[0]);
138         procexecl(nil, "/bin/aux/cpuid", "cpuid", "-r", nil);
139         threadexits("exec: %r");
140 }
141
142 void
143 cpuidinit(void)
144 {
145         int pfd[2];
146         Biobuf *bp;
147         char *l, *f[5];
148         CPUID *cp;
149         
150         pipe(pfd);
151         procrfork(auxcpuidproc, pfd, 4096, RFFDG);
152         close(pfd[0]);
153         bp = Bfdopen(pfd[1], OREAD);
154         if(bp == nil) sysfatal("Bopenfd: %r");
155         for(; l = Brdstr(bp, '\n', 1), l != nil; free(l)){
156                 if(tokenize(l, f, 5) < 5) continue;
157                 cpuidf = realloc(cpuidf, (ncpuidf + 1) * sizeof(CPUID));
158                 cp = cpuidf + ncpuidf++;
159                 cp->idx = strtoul(f[0], nil, 16);
160                 cp->ax = strtoul(f[1], nil, 16);
161                 cp->bx = strtoul(f[2], nil, 16);
162                 cp->cx = strtoul(f[3], nil, 16);
163                 cp->dx = strtoul(f[4], nil, 16);
164         }
165         Bterm(bp);
166         close(pfd[1]);
167 }
168
169 CPUID *
170 getcpuid(ulong idx)
171 {
172         CPUID *cp;
173         
174         for(cp = cpuidf; cp < cpuidf + ncpuidf; cp++)
175                 if(cp->idx == idx)
176                         return cp;
177         return nil;
178 }
179
180 int maxcpuid = 7;
181
182 static void
183 cpuid(ExitInfo *ei)
184 {
185         u32int ax, bx, cx, dx;
186         CPUID *cp;
187         static CPUID def;
188         
189         ax = rget(RAX);
190         cp = getcpuid(ax);
191         if(cp == nil) cp = &def;
192         switch(ax){
193         case 0: /* highest register & GenuineIntel */
194                 ax = maxcpuid;
195                 bx = cp->bx;
196                 dx = cp->dx;
197                 cx = cp->cx;
198                 break;
199         case 1: /* features */
200                 ax = cp->ax;
201                 bx = cp->bx & 0xffff;
202                 cx = cp->cx & 0x60de2203;
203                 dx = cp->dx & 0x0782a179;
204                 break;
205         case 2: goto literal; /* cache stuff */
206         case 3: goto zero; /* processor serial number */
207         case 4: goto zero; /* cache stuff */
208         case 5: goto zero; /* monitor/mwait */
209         case 6: goto zero; /* thermal management */
210         case 7: goto zero; /* more features */
211         case 10: goto zero; /* performance counters */
212         case 0x80000000: /* highest register */
213                 ax = 0x80000008;
214                 bx = cx = dx = 0;
215                 break;
216         case 0x80000001: /* signature & ext features */
217                 ax = cp->ax;
218                 bx = 0;
219                 cx = cp->cx & 0x121;
220                 if(sizeof(uintptr) == 8)
221                         dx = cp->dx & 0x24100800;
222                 else
223                         dx = cp->dx & 0x04100000;
224                 break;
225         case 0x80000002: goto literal; /* brand string */
226         case 0x80000003: goto literal; /* brand string */
227         case 0x80000004: goto literal; /* brand string */
228         case 0x80000005: goto zero; /* reserved */
229         case 0x80000006: goto literal; /* cache info */
230         case 0x80000007: goto zero; /* invariant tsc */
231         case 0x80000008: goto literal; /* address bits */
232         literal:
233                 ax = cp->ax;
234                 bx = cp->bx;
235                 cx = cp->cx;
236                 dx = cp->dx;
237                 break;
238         default:
239                 vmerror("unknown cpuid field eax=%#ux", ax);
240         zero:
241                 ax = 0;
242                 bx = 0;
243                 cx = 0;
244                 dx = 0;
245                 break;
246         }
247         rset(RAX, ax);
248         rset(RBX, bx);
249         rset(RCX, cx);
250         rset(RDX, dx);
251         skipinstr(ei);
252 }
253
254 static void
255 rdwrmsr(ExitInfo *ei)
256 {
257         u32int cx;
258         u64int val;
259         int rd;
260         
261         rd = ei->name[1] == 'r';
262         cx = rget(RCX);
263         val = (uvlong)rget(RDX) << 32 | rget(RAX);
264         switch(cx){
265         case 0x277:
266                 if(rd) val = rget("pat");
267                 else rset("pat", val);
268                 break;
269         case 0x8B: val = 0; break; /* microcode update */
270         default:
271                 if(rd){
272                         vmerror("read from unknown MSR %#ux ignored", cx);
273                         val = 0;
274                 }else
275                         vmerror("write to unknown MSR %#ux ignored (val=%#ullx)", cx, val);
276                 break;
277         }
278         if(rd){
279                 rset(RAX, (u32int)val);
280                 rset(RDX, (u32int)(val >> 32));
281         }
282         skipinstr(ei);
283 }
284
285 static void
286 movdr(ExitInfo *ei)
287 {
288         static char *dr[8] = { "dr0", "dr1", "dr2", "dr3", nil, nil, "dr6", "dr7" };
289         int q;
290         
291         q = ei->qual;
292         if((q & 6) == 4){
293                 postexc("#gp", 0);
294                 return;
295         }
296         if((q & 16) != 0)
297                 rset(x86reg[q >> 8 & 15], rget(dr[q & 7]));
298         else
299                 rset(dr[q & 7], rget(x86reg[q >> 8 & 15]));
300         skipinstr(ei);
301 }
302
303 static void
304 movcr(ExitInfo *ei)
305 {
306         u32int q;
307         
308         q = ei->qual;
309         switch(q & 15){
310         case 0:
311                 switch(q >> 4 & 3){
312                 case 0:
313                         vmdebug("illegal CR0 write, value %#ux", rget(x86reg[q >> 8 & 15]));
314                         rset("cr0real", rget(x86reg[q >> 8 & 15]));
315                         skipinstr(ei);
316                         break;
317                 case 1:
318                         vmerror("shouldn't happen: trap on MOV from CR0");
319                         rset(x86reg[q >> 8 & 15], rget("cr0fake"));
320                         skipinstr(ei);
321                         break;
322                 case 2:
323                         vmerror("shouldn't happen: trap on CLTS");
324                         rset("cr0real", rget("cr0real") & ~8);
325                         skipinstr(ei);
326                         break;
327                 case 3:
328                         vmerror("LMSW handler unimplemented");
329                         postexc("#ud", NOERRC);
330                 }
331                 break;
332         case 4:
333                 switch(q >> 4 & 3){
334                 case 0:
335                         vmdebug("illegal CR4 write, value %#ux", rget(x86reg[q >> 8 & 15]));
336                         rset("cr4real", rget(x86reg[q >> 8 & 15]));
337                         skipinstr(ei);
338                         break;
339                 case 1:
340                         vmerror("shouldn't happen: trap on MOV from CR4");
341                         rset(x86reg[q >> 8 & 15], rget("cr3fake"));
342                         skipinstr(ei);
343                         break;
344                 default:
345                         vmerror("unknown CR4 operation %d", q);
346                         postexc("#ud", NOERRC);
347                 }
348                 break;
349         default:
350                 vmerror("access to unknown control register CR%d", ei->qual & 15);
351                 postexc("#ud", NOERRC);
352         }
353 }
354
355 static void
356 dbgexc(ExitInfo *ei)
357 {
358         rset("dr6", rget("dr6") | ei->qual);
359         postexc("#db", NOERRC);
360 }
361
362 static void
363 hlt(ExitInfo *ei)
364 {
365         if(irqactive < 0)
366                 state = VMHALT;
367         skipinstr(ei);
368 }
369
370 static void
371 irqackhand(ExitInfo *ei)
372 {
373         irqack(ei->qual);
374 }
375
376 typedef struct ExitType ExitType;
377 struct ExitType {
378         char *name;
379         void (*f)(ExitInfo *);
380 };
381 static ExitType etypes[] = {
382         {"io", iohandler},
383         {".cpuid", cpuid},
384         {".hlt", hlt},
385         {"eptfault", eptfault},
386         {"*ack", irqackhand},
387         {".rdmsr", rdwrmsr},
388         {".wrmsr", rdwrmsr},
389         {".movdr", movdr},
390         {"#db", dbgexc},
391         {"movcr", movcr},
392 };
393
394 void
395 processexit(char *msg)
396 {
397         static char msgc[1024];
398         char *f[32];
399         int nf;
400         ExitType *et;
401         int i;
402         ExitInfo ei;
403         extern int getexit;
404
405         strcpy(msgc, msg);
406         nf = tokenize(msgc, f, nelem(f));
407         if(nf < 2) sysfatal("invalid wait message: %s", msg);
408         memset(&ei, 0, sizeof(ei));
409         ei.raw = msg;
410         ei.name = f[0];
411         ei.qual = strtoull(f[1], nil, 0);
412         for(i = 2; i < nf; i += 2){
413                 if(strcmp(f[i], "pc") == 0)
414                         rpoke(RPC, strtoull(f[i+1], nil, 0), 1);
415                 else if(strcmp(f[i], "sp") == 0)
416                         rpoke(RSP, strtoull(f[i+1], nil, 0), 1);
417                 else if(strcmp(f[i], "ax") == 0)
418                         rpoke(RAX, strtoull(f[i+1], nil, 0), 1);
419                 else if(strcmp(f[i], "ilen") == 0)
420                         ei.ilen = strtoul(f[i+1], nil, 0);
421                 else if(strcmp(f[i], "iinfo") == 0)
422                         ei.iinfo = strtoul(f[i+1], nil, 0);
423                 else if(strcmp(f[i], "pa") == 0)
424                         ei.pa = strtoull(f[i+1], nil, 0);
425                 else if(strcmp(f[i], "va") == 0)
426                         ei.va = strtoull(f[i+1], nil, 0);
427         }
428         if(*f[0] == '*') getexit++;
429         for(et = etypes; et < etypes + nelem(etypes); et++)
430                 if(strcmp(et->name, f[0]) == 0){
431                         et->f(&ei);
432                         return;
433                 }
434         if(*f[0] == '.'){
435                 vmerror("vmx: unknown instruction %s", f[0]+1);
436                 postexc("#ud", NOERRC);
437                 return;
438         }
439         if(*f[0] == '*'){
440                 vmerror("vmx: unknown notification %s", f[0]+1);
441                 return;
442         }
443         if(persist){
444                 vmerror("unknown exit: %s", msg);
445                 state = VMDEAD;
446         }else
447                 sysfatal("unknown exit: %s", msg);
448 }