]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/vmx.c
devvmx, vmx: lilu dallas multivm
[plan9front.git] / sys / src / cmd / vmx / vmx.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 char *segname;
9 int segrclose;
10 Region *mmap;
11 int ctlfd, regsfd, mapfd, waitfd;
12 Channel *waitch, *sleepch, *notifch;
13 enum { MSEC = 1000*1000, MinSleep = MSEC, SleeperPoll = 2000*MSEC } ;
14 int getexit, state;
15 typedef struct VmxNotif VmxNotif;
16 struct VmxNotif {
17         void (*f)(void *);
18         void *arg;
19 };
20
21 int mainstacksize = 65536;
22
23 void *
24 emalloc(ulong sz)
25 {
26         void *v;
27         
28         v = malloc(sz);
29         if(v == nil)
30                 sysfatal("malloc: %r");
31         memset(v, 0, sz);
32         setmalloctag(v, getcallerpc(&sz));
33         return v;
34 }
35
36 void
37 vmerror(char *fmt, ...)
38 {
39         Fmt f;
40         char buf[256];
41         va_list arg;
42         
43         fmtfdinit(&f, 2, buf, sizeof buf);
44         va_start(arg, fmt);
45         fmtvprint(&f, fmt, arg);
46         va_end(arg);
47         fmtprint(&f, "\n");
48         fmtfdflush(&f);
49 }
50
51 int
52 ctl(char *fmt, ...)
53 {
54         va_list va;
55         int rc;
56         
57         va_start(va, fmt);
58         rc = vfprint(ctlfd, fmt, va);
59         va_end(va);
60         return rc;
61 }
62
63 void
64 modregion(Region *r)
65 {
66         if(r->segname == nil){
67                 if(fprint(mapfd, "--- wb %#ullx %#ullx\n", (uvlong)r->start, (uvlong)r->end) < 0)
68                         vmerror("updating memory map: %r");
69         }else
70                 if(fprint(mapfd, "%c%c%c wb %#ullx %#ullx %s %#ullx\n",
71                         (r->type & REGR) != 0 ? 'r' : '-',
72                         (r->type & REGW) != 0 ? 'w' : '-',
73                         (r->type & REGX) != 0 ? 'x' : '-',
74                         (uvlong)r->start, (uvlong)r->end, r->segname, (uvlong)r->segoff) < 0)
75                         vmerror("updating memory map: %r");
76 }
77
78 static void
79 vmxsetup(void)
80 {
81         static char buf[128];
82         static char name[128];
83         int rc;
84         
85         ctlfd = open("#X/clone", ORDWR|ORCLOSE);
86         if(ctlfd < 0) sysfatal("open: %r");
87         rc = read(ctlfd, name, sizeof(name) - 1);
88         if(rc < 0) sysfatal("read: %r");
89         name[rc] = 0;
90         srand(atoi(name));
91         if(segname == nil){
92                 segname = smprint("vm.%s", name);
93                 segrclose = ORCLOSE;
94         }
95         
96         snprint(buf, sizeof(buf), "#X/%s/regs", name);
97         regsfd = open(buf, ORDWR);
98         if(regsfd < 0) sysfatal("open: %r");
99         
100         snprint(buf, sizeof(buf), "#X/%s/map", name);
101         mapfd = open(buf, OWRITE|OTRUNC);
102         if(mapfd < 0) sysfatal("open: %r");
103         
104         snprint(buf, sizeof(buf), "#X/%s/wait", name);
105         waitfd = open(buf, OREAD);
106         if(waitfd < 0) sysfatal("open: %r");
107 }
108
109 enum { RCENT = 256 };
110 char *rcname[RCENT];
111 uvlong rcval[RCENT];
112 uvlong rcvalid[(RCENT+63)/64], rcdirty[(RCENT+63)/64];
113
114 static int
115 rclookup(char *n)
116 {
117         int i;
118         
119         for(i = 0; i < RCENT; i++)
120                 if(rcname[i] != nil && strcmp(n, rcname[i]) == 0)
121                         return i;
122         return -1;
123 }
124
125 char *
126 rcflush(int togo)
127 {
128         int i, j;
129         static char buf[4096];
130         char *p, *e;
131         uvlong v;
132         
133         p = buf;
134         e = buf + sizeof(buf);
135         *p = 0;
136         for(i = 0; i < (RCENT+63)/64; i++){
137                 if(v = rcdirty[i], v != 0){
138                         for(j = 0; j < 64; j++)
139                                 if((v>>j & 1) != 0)
140                                         p = seprint(p, e, "%s%c%#ullx%c", rcname[i*64+j], togo?'=':' ', rcval[i*64+j], togo?';':'\n');
141                         rcdirty[i] = 0;
142                 }
143                 rcvalid[i] = 0;
144         }
145         if(!togo && p != buf && write(regsfd, buf, p - buf) < p - buf)
146                 sysfatal("rcflush: write: %r");
147         return p != buf ? buf : nil;
148 }
149
150 static void
151 rcload(void)
152 {
153         char buf[4096];
154         char *p, *q, *f[2];
155         int nf;
156         int i, rc;
157
158         rcflush(0);
159         rc = pread(regsfd, buf, sizeof(buf) - 1, 0);
160         if(rc < 0) sysfatal("rcload: pread: %r");
161         buf[rc] = 0;
162         p = buf;
163         for(i = 0; i < nelem(rcname); i++){
164                 q = strchr(p, '\n');
165                 if(q == nil) break;
166                 *q = 0;
167                 nf = tokenize(p, f, nelem(f));
168                 p = q + 1;
169                 if(nf < 2) break;
170                 free(rcname[i]);
171                 rcname[i] = strdup(f[0]);
172                 rcval[i] = strtoull(f[1], nil, 0);
173                 rcvalid[i>>6] |= 1ULL<<(i&63);
174         }
175         for(; i < nelem(rcname); i++){
176                 free(rcname[i]);
177                 rcname[i] = 0;
178                 rcvalid[i>>6] &= ~(1ULL<<(i&63));
179         }
180 }
181
182 uvlong
183 rget(char *reg)
184 {
185         int i;
186
187         i = rclookup(reg);
188         if(i < 0 || (rcvalid[i>>6]>>i&1) == 0){
189                 rcload();
190                 i = rclookup(reg);
191                 if(i < 0) sysfatal("unknown register %s", reg);
192         }
193         return rcval[i];
194 }
195
196 void
197 rpoke(char *reg, uvlong val, int clean)
198 {
199         int i;
200
201         i = rclookup(reg);
202         if(i >= 0){
203                 if((rcvalid[i>>6]>>(i&63)&1) != 0 && rcval[i] == val) return;
204                 goto goti;
205         }
206         for(i = 0; i < nelem(rcname); i++)
207                 if(rcname[i] == nil){
208                         rcname[i] = strdup(reg);
209                         break;
210                 }
211         assert(i < nelem(rcname));
212 goti:
213         rcval[i] = val;
214         rcvalid[i>>6] |= 1ULL<<(i&63);
215         if(!clean)
216                 rcdirty[i>>6] |= 1ULL<<(i&63);
217 }
218
219 uvlong
220 rgetsz(char *reg, int sz)
221 {
222         switch(sz){
223         case 1: return (u8int)rget(reg);
224         case 2: return (u16int)rget(reg);
225         case 4: return (u32int)rget(reg);
226         case 8: return rget(reg);
227         default:
228                 vmerror("invalid size operand for rgetsz");
229                 assert(0);
230                 return 0;
231         }
232 }
233
234 void
235 rsetsz(char *reg, uvlong val, int sz)
236 {
237         switch(sz){
238         case 1: rset(reg, (u8int)val | rget(reg) & ~0xffULL); break;
239         case 2: rset(reg, (u16int)val | rget(reg) & ~0xffffULL); break;
240         case 4: rset(reg, (u32int)val); break;
241         case 8: rset(reg, val); break;
242         default:
243                 vmerror("invalid size operand for rsetsz");
244                 assert(0);
245         }
246 }
247
248 Region *
249 mkregion(u64int pa, u64int end, int type)
250 {
251         Region *r, *s, **rp;
252
253         r = emalloc(sizeof(Region));
254         if(end < pa) sysfatal("end of region %p before start of region %#p", (void*)end, (void*)pa);
255         if((pa & BY2PG-1) != 0 || (end & BY2PG-1) != 0) sysfatal("address %#p not page aligned", (void*)pa);
256         r->start = pa;
257         r->end = end;
258         r->type = type;
259         for(s = mmap; s != nil; s = s->next)
260                 if(!(pa < s->start && end < s->end || pa >= s->start && pa >= s->end))
261                         sysfatal("region %#p-%#p overlaps region %#p-%#p", (void*)pa, (void*)end, (void*)s->start, (void*)s->end);
262         for(rp = &mmap; (*rp) != nil && (*rp)->start < end; rp = &(*rp)->next)
263                 ;
264         r->next = *rp;
265         *rp = r;
266         return r;
267 }
268
269 Region *
270 regptr(u64int addr)
271 {
272         Region *r;
273
274         for(r = mmap; r != nil; r = r->next)
275                 if(addr >= r->start && addr < r->end)
276                         return r;
277         return nil;
278 }
279
280 void *
281 gptr(u64int addr, u64int len)
282 {
283         Region *r;
284
285         if(addr + len < addr)
286                 return nil;
287         for(r = mmap; r != nil; r = r->next)
288                 if(addr >= r->start && addr < r->end){
289                         if(addr + len > r->end)
290                                 return nil;
291                         return (uchar *) r->v + (addr - r->start);
292                 }
293         return nil;
294 }
295
296 uintptr
297 gpa(void *v)
298 {
299         Region *r;
300
301         for(r = mmap; r != nil; r = r->next)
302                 if(v >= r->v && v < r->ve)
303                         return (uchar *) v - (uchar *) r->v + r->start;
304         return -1;
305 }
306
307 uintptr
308 gavail(void *v)
309 {
310         Region *r;
311         
312         for(r = mmap; r != nil; r = r->next)
313                 if(v >= r->v && v < r->ve)
314                         return (uchar *) r->ve - (uchar *) v;
315         return 0;
316 }
317
318 void *
319 gend(void *v)
320 {
321         return (u8int *) v + gavail(v);
322 }
323
324 void *tmp, *vgamem;
325 uvlong tmpoff, vgamemoff;
326
327 static void
328 mksegment(char *sn)
329 {
330         uintptr sz;
331         int fd;
332         Region *r;
333         char buf[256];
334         u8int *gmem, *p;
335
336         sz = BY2PG; /* temporary page */
337         sz += 256*1024; /* vga */
338         for(r = mmap; r != nil; r = r->next){
339                 if((r->type & REGALLOC) == 0)
340                         continue;
341                 r->segname = sn;
342                 if(sz + (r->end - r->start) < sz)
343                         sysfatal("out of address space");
344                 sz += r->end - r->start;
345         }
346         gmem = segattach(0, sn, nil, sz);
347         if(gmem == (void*)-1){
348                 snprint(buf, sizeof(buf), "#g/%s", sn);
349                 fd = create(buf, OREAD|segrclose, DMDIR | 0777);
350                 if(fd < 0) sysfatal("create: %r");
351                 snprint(buf, sizeof(buf), "#g/%s/ctl", sn);
352                 fd = open(buf, OWRITE|OTRUNC);
353                 if(fd < 0) sysfatal("open: %r");
354                 snprint(buf, sizeof(buf), "va %#ullx %#ullx sticky", 0x10000000ULL, (uvlong)sz);
355                 if(write(fd, buf, strlen(buf)) < 0) sysfatal("write: %r");
356                 close(fd);
357                 gmem = segattach(0, sn, nil, sz);
358                 if(gmem == (void*)-1) sysfatal("segattach: %r");
359         }
360         memset(gmem, 0, sz > 1<<24 ? 1<<24 : sz);
361         p = gmem;
362         for(r = mmap; r != nil; r = r->next){
363                 if(r->segname == nil) continue;
364                 r->segoff = p - gmem;
365                 r->v = p;
366                 p += r->end - r->start;
367                 r->ve = p;
368         }
369         vgamem = p;
370         vgamemoff = p - gmem;
371         regptr(0xa0000)->segoff = vgamemoff;
372         regptr(0xa0000)->v = vgamem;
373         p += 256*1024;
374         regptr(0xa0000)->ve = p;
375         tmp = p;
376         tmpoff = p - gmem;
377
378         for(r = mmap; r != nil; r = r->next)
379                 modregion(r);
380
381 }
382
383 void
384 postexc(char *name, vlong code)
385 {
386         if(code >= 0){
387                 if(ctl("exc %s,%#ux", name, (u32int)code) < 0)
388                         sysfatal("ctl(postexc): %r");
389         }else
390                 if(ctl("exc %s", name) < 0)
391                         sysfatal("ctl(postexc): %r");
392 }
393
394 void
395 launch(void)
396 {
397         char *s;
398
399         s = rcflush(1);
400         if(ctl("go %s", s == nil ? "" : s) < 0)
401                 sysfatal("go %s: %r", s == nil ? "" : s);
402         getexit++;
403 }
404
405 static void
406 waitproc(void *)
407 {
408         static char buf[512];
409         char *p;
410         int rc;
411
412         threadsetname("waitexit");
413         for(;;){
414                 rc = read(waitfd, buf, sizeof(buf) - 1);
415                 if(rc < 0)
416                         sysfatal("read: %r");
417                 buf[rc] = 0;
418                 p = strchr(buf, '\n');
419                 if(p != nil) *p = 0;
420                 sendp(waitch, strdup(buf));
421         }
422 }
423
424 vlong timerevent = -1;
425 Lock timerlock;
426 int timerid;
427
428 static void
429 sleeperproc(void *)
430 {
431         vlong then, now;
432
433         timerid = threadid();
434         timerevent = nsec() + SleeperPoll;
435         unlock(&timerlock);
436         threadsetname("sleeper");
437         for(;;){
438                 lock(&timerlock);
439                 then = timerevent;
440                 now = nsec();
441                 if(then <= now) timerevent = now + SleeperPoll;
442                 unlock(&timerlock);
443                 if(then - now >= MinSleep){
444                         sleep((then - now) / MSEC);
445                         continue;
446                 }
447                 while(nsec() < then)
448                         ;
449                 sendul(sleepch, 0);
450         }
451 }
452
453 static void
454 runloop(void)
455 {
456         char *waitmsg;
457         ulong ul;
458         VmxNotif notif;
459
460         lock(&timerlock);
461         proccreate(waitproc, nil, 4096);
462         proccreate(sleeperproc, nil, 4096);
463         launch();
464         for(;;){
465                 enum {
466                         WAIT,
467                         SLEEP,
468                         NOTIF,
469                 };
470                 Alt a[] = {
471                         [WAIT] {waitch, &waitmsg, CHANRCV},
472                         [SLEEP] {sleepch, &ul, CHANRCV},
473                         [NOTIF] {notifch, &notif, CHANRCV},
474                         {nil, nil, CHANEND}
475                 };
476                 switch(alt(a)){
477                 case WAIT:
478                         getexit--;
479                         processexit(waitmsg);
480                         free(waitmsg);
481                         break;
482                 case SLEEP:
483                         pitadvance();
484                         rtcadvance();
485                         break;
486                 case NOTIF:
487                         notif.f(notif.arg);
488                         break;
489                 }
490                 if(getexit == 0 && state == VMRUNNING)
491                         launch();
492         }
493 }
494
495 static int mainid;
496
497 void
498 sendnotif(void (*f)(void *), void *arg)
499 {
500         VmxNotif notif = {f, arg};
501         
502         if(threadid() == mainid)
503                 f(arg);
504         else
505                 send(notifch, &notif);
506 }
507
508 extern void vgainit(void);
509 extern void pciinit(void);
510 extern void pcibusmap(void);
511 extern void cpuidinit(void);
512 extern void vgafbparse(char *);
513 extern void init9p(char *);
514
515 int cmdlinen;
516 char **cmdlinev;
517 int bootmodn;
518 char **bootmod;
519
520 static uvlong
521 siparse(char *s)
522 {
523         uvlong l;
524         char *p;
525         
526         l = strtoull(s, &p, 0);
527         switch(*p){
528         case 'k': case 'K': p++; l *= 1<<10; break;
529         case 'm': case 'M': p++; l *= 1<<20; break;
530         case 'g': case 'G': p++; l *= 1<<30; break;
531         }
532         if(*p != 0) sysfatal("invalid argument: %s", s);
533         return l;
534 }
535
536 static void
537 setiodebug(char *s)
538 {
539         char *p;
540         int n, m, neg;
541         extern u32int iodebug[32];
542         
543         do{
544                 if(neg = *s == '!')
545                         s++;
546                 n = strtoul(s, &p, 0);
547                 if(s == p)
548 no:                     sysfatal("invalid iodebug argument (error at %#q)", s);
549                 if(n >= sizeof(iodebug)*8)
550 range:                  sysfatal("out of iodebug range (0-%#ux)", sizeof(iodebug)*8-1);
551                 s = p + 1;
552                 if(*p == '-'){
553                         m = strtoul(s, &p, 0);
554                         if(m >= sizeof(iodebug)*8)
555                                 goto range;
556                         if(s == p || m < n) goto no;
557                         s = p + 1;
558                 }else
559                         m = n;
560                 for(; n <= m; n++)
561                         if(neg)
562                                 iodebug[n>>5] &= ~(1<<(n&31));
563                         else
564                                 iodebug[n>>5] |= 1<<(n&31);
565         }while(*p == ',');
566         if(*p != 0) goto no;
567 }
568
569 static void
570 usage(void)
571 {
572         char *blanks, *p;
573         
574         blanks = strdup(argv0);
575         for(p = blanks; *p != 0; p++)
576                 *p = ' ';
577         fprint(2, "usage: %s [ -M mem ] [ -c com1rd[,com1wr] ] [ -C com2rd[,com2r] ] [ -n nic ]\n", argv0);
578         fprint(2, "       %s [ -d blockfile ] [ -m module ] [ -v vga ] [ -9 srv ] kernel [ args ... ]\n", blanks);
579         threadexitsall("usage");
580 }
581
582 void (*kconfig)(void);
583
584 void
585 threadmain(int argc, char **argv)
586 {
587         static int (*edev[16])(char *);
588         static char *edevt[nelem(edev)];
589         static char *edevaux[nelem(edev)];
590         static int edevn;
591         static uvlong gmemsz = 64*1024*1024;
592         static char *srvname;
593         extern uintptr fbsz, fbaddr;
594         int i;
595
596         quotefmtinstall();
597         mainid = threadid();
598         cpuidinit();
599         waitch = chancreate(sizeof(char *), 32);
600         sleepch = chancreate(sizeof(ulong), 32);
601         notifch = chancreate(sizeof(VmxNotif), 16);
602         
603         ARGBEGIN {
604         case 'm':
605                 bootmod = realloc(bootmod, (bootmodn + 1) * sizeof(char *));
606                 bootmod[bootmodn++] = strdup(EARGF(usage()));
607                 break;
608         case 's':
609                 segname = strdup(EARGF(usage()));
610                 segrclose = 0;
611                 break;
612         case 'c':
613                 uartinit(0, EARGF(usage()));
614                 break;
615         case 'C':
616                 uartinit(1, EARGF(usage()));
617                 break;
618         case 'n':
619                 assert(edevn < nelem(edev));
620                 edev[edevn] = mkvionet;
621                 edevt[edevn] = "virtio network";
622                 edevaux[edevn++] = strdup(EARGF(usage()));
623                 break;
624         case 'd':
625                 assert(edevn < nelem(edev));
626                 edevaux[edevn] = strdup(EARGF(usage()));
627                 if(strncmp(edevaux[edevn], "ide:", 4) == 0){
628                         edevaux[edevn] += 4;
629                         edev[edevn] = mkideblk;
630                         edevt[edevn] = "ide block";
631                 }else{
632                         edev[edevn] = mkvioblk;
633                         edevt[edevn] = "virtio block";
634                 }
635                 edevn++;
636                 break;
637         case 'M':
638                 gmemsz = siparse(EARGF(usage()));
639                 if(gmemsz != (uintptr) gmemsz) sysfatal("too much memory for address space");
640                 break;
641         case 'v':
642                 vgafbparse(EARGF(usage()));
643                 break;
644         case '9':
645                 if(srvname != nil) usage();
646                 srvname = EARGF(usage());
647                 break;
648         case L'ι':
649                 setiodebug(EARGF(usage()));
650                 break;
651         default:
652                 usage();
653         } ARGEND;
654         if(argc < 1) usage();
655         cmdlinen = argc - 1;
656         cmdlinev = argv + 1;
657         
658         if(gmemsz < 1<<20) sysfatal("640 KB of RAM is not enough for everyone");
659         mkregion(0, 0xa0000, REGALLOC|REGFREE|REGRWX);
660         mkregion(0xa0000, 0xc0000, REGALLOC|REGRWX);
661         mkregion(0xc0000, 0x100000, REGALLOC|REGRES|REGRWX);
662         if(fbsz != 0 && fbaddr < gmemsz){
663                 mkregion(0x100000, fbaddr, REGALLOC|REGFREE|REGRWX);
664                 mkregion(fbaddr + fbsz, gmemsz, REGALLOC|REGFREE|REGRWX);
665         }else
666                 mkregion(0x100000, gmemsz, REGALLOC|REGFREE|REGRWX);
667         if(fbsz != 0){
668                 if(fbaddr < 1<<20) sysfatal("framebuffer must not be within first 1 MB");
669                 if(fbaddr != (u32int) fbaddr || (u32int)(fbaddr+fbsz) < fbaddr) sysfatal("framebuffer must be within first 4 GB");
670                 mkregion(fbaddr, fbaddr+fbsz, REGALLOC|REGRWX);
671         }
672         vmxsetup();
673         mksegment(segname);
674         loadkernel(argv[0]);
675         pciinit();
676
677         vgainit();
678         for(i = 0; i < edevn; i++)
679                 if(edev[i](edevaux[i]) < 0)
680                         sysfatal("%s: %r", edevt[i]);
681
682         pcibusmap();
683         
684         if(srvname != nil) init9p(srvname);
685         if(kconfig != nil) kconfig();
686         runloop();
687         exits(nil);
688 }