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