]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/ksetup.c
show line numbers in dtracy type errors
[plan9front.git] / sys / src / cmd / vmx / ksetup.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <libsec.h>
6 #include "dat.h"
7 #include "fns.h"
8 #include "x86.h"
9
10 static uchar hdr[8192];
11 static int fd;
12
13 extern int bootmodn;
14 extern char **bootmod;
15 extern int cmdlinen;
16 extern char **cmdlinev;
17 extern VgaMode *curmode, textmode;
18 extern uintptr fbaddr, fbsz;
19
20 static int elf64;
21
22 static int
23 biostype(Region *r)
24 {
25         return r->type >> 8 & 0xff;
26 }
27
28 static void *
29 pack(void *v, char *fmt, ...)
30 {
31         uchar *p;
32         va_list va;
33         
34         p = v;
35         va_start(va, fmt);
36         for(; *fmt != 0; fmt++)
37                 switch(*fmt){
38                 case '.': p++; break;
39                 case 's': PUT16(p, 0, va_arg(va, int)); p += 2; break;
40                 case 'i': PUT32(p, 0, va_arg(va, u32int)); p += 4; break;
41                 case 'v': PUT64(p, 0, va_arg(va, u64int)); p += 8; break;
42                 case 'z': if(elf64) {PUT64(p, 0, va_arg(va, uintptr)); p += 8;} else {PUT32(p, 0, va_arg(va, uintptr)); p += 4;} break;
43                 default: sysfatal("pack: unknown fmt character %c", *fmt);
44                 }
45         va_end(va);
46         return p;
47 }
48
49 static int
50 putmmap(uchar *p0)
51 {
52         uchar *p;
53         Region *r;
54         int t;
55         
56         p = p0;
57         for(r = mmap; r != nil; r = r->next){
58                 t = biostype(r);
59                 if(t == 0) continue;
60                 if(gavail(p) < 24) sysfatal("out of guest memory");
61                 p = pack(p, "ivvi", 20, (uvlong) r->start, (uvlong)(r->end - r->start), t);
62         }
63         return (uchar *) p - p0;
64 }
65
66 static int
67 putcmdline(uchar *p0)
68 {
69         int i;
70         char *p, *e;
71         
72         if(cmdlinen == 0) return 0;
73         p = (char*)p0;
74         e = gend(p0);
75         if(p >= e) return 0;
76         for(i = 0; i < cmdlinen; i++){
77                 p = strecpy(p, e, cmdlinev[i]);
78                 if(i != cmdlinen - 1) *p++ = ' ';
79         }
80         return p - (char*)p0 + 1;
81 }
82
83 static int
84 putmods(uchar *p0)
85 {
86         int i, fd, rc;
87         u32int *p;
88         uchar *q;
89         char dummy;
90
91         if(bootmodn == 0) return 0;
92         p = (u32int*)p0;
93         q = (uchar*)(p + 4 * bootmodn);
94         for(i = 0; i < bootmodn; i++){
95                 q = gptr(-(-gpa(q) & -BY2PG), 1);
96                 if(q == nil) sysfatal("out of guest memory");
97                 fd = open(bootmod[i], OREAD);
98                 if(fd == -1) sysfatal("module open: %r");
99                 p[0] = gpa(q);
100                 rc = readn(fd, q, gavail(q));
101                 if(rc < 0) sysfatal("module read: %r");
102                 if(read(fd, &dummy, 1) == 1) sysfatal("out of guest memory");
103                 close(fd);
104                 q += rc;
105                 p[1] = gpa(q);
106                 p[2] = 0;
107                 p[3] = 0;
108                 p += 4;
109         }
110         bootmodn = ((uchar*)p - p0) / 16;
111         return q - p0;
112 }
113
114 static int
115 trymultiboot(void)
116 {
117         u32int *p, flags;
118         u32int header, load, loadend, bssend, entry;
119         u32int filestart;
120         uchar *gp;
121         uchar *modp;
122         int len;
123         int rc;
124
125         for(p = (u32int*)hdr; p < (u32int*)hdr + sizeof(hdr)/4; p++)
126                 if(*p == 0x1badb002)
127                         break;
128         if(p == (u32int*)hdr + sizeof(hdr)/4)
129                 return 0;
130         if((u32int)(p[0] + p[1] + p[2]) != 0)
131                 sysfatal("invalid multiboot checksum");
132         flags = p[1];
133         if((flags & 1<<16) == 0)
134                 sysfatal("no size info in multiboot header");
135         header = p[3];
136         load = p[4];
137         loadend = p[5];
138         bssend = p[6];
139         entry = p[7];
140         filestart = (uchar*)p - hdr - (header - load);
141         gp = gptr(load, bssend != 0 ? bssend - load : loadend != 0 ? loadend - load : BY2PG);
142         if(gp == nil)
143                 sysfatal("kernel image out of bounds");
144         seek(fd, filestart, 0);
145         if(loadend == 0){
146                 rc = readn(fd, gp, gavail(gp));
147                 if(rc <= 0) sysfatal("readn: %r");
148                 loadend = load + rc;
149         }else{
150                 rc = readn(fd, gp, loadend - load);
151                 if(rc < 0) sysfatal("readn: %r");
152                 if(rc < loadend - load) sysfatal("short kernel image");
153         }
154         if(bssend == 0) bssend = loadend;
155         bssend = -(-bssend & -BY2PG);
156         p = gptr(bssend, 128);
157         if(p == nil) sysfatal("no space for multiboot structure");
158         memset(p, 0, 128);
159         p[0] = 1<<0;
160         p[1] = gavail(gptr(0, 0)) >> 10;
161         if(p[1] > 640) p[1] = 640;
162         p[2] = gavail(gptr(1048576, 0)) >> 10;  
163         modp = gptr(bssend + 128, 1);
164         if(modp == nil) sysfatal("out of guest memory");
165         len = putmmap(modp);
166         if(len != 0){
167                 p[0] |= 1<<6;
168                 p[11] = len;
169                 p[12] = gpa(modp);
170                 modp += len;
171         }
172         len = putcmdline(modp);
173         if(len != 0){
174                 p[0] |= 1<<2;
175                 p[4] = gpa(modp);
176                 modp += len + 7 & -8;
177         }
178         len = putmods(modp);
179         if(len != 0){
180                 p[0] |= 1<<3;
181                 p[5] = bootmodn;
182                 p[6] = gpa(modp);
183                 modp += len + 7 & -8;
184         }
185
186         if(curmode != nil && curmode != &textmode){
187                 int i, o, n;
188                 u16int r, g, b;
189
190                 o = 0;
191                 r = g = b = 0;
192                 for(i = 0; i < 4; i++){
193                         n = curmode->chan >> 8*i & 0xf;
194                         if(n == 0) continue;
195                         switch(curmode->chan >> 4 + 8*i & 0xf){
196                         case CRed:      r = o | n<<8; break;
197                         case CGreen:    g = o | n<<8; break;
198                         case CBlue:     b = o | n<<8; break;
199                         }
200                         o += n;
201                 }
202                 p[0] |= 1<<12;
203                 pack(&p[22], "viiiisss", (u64int)fbaddr,
204                         curmode->hbytes, curmode->w, curmode->h,
205                         chantodepth(curmode->chan) | 1<<8, r, g, b);
206         }
207
208         USED(modp);
209         rset(RPC, entry);
210         rset(RAX, 0x2badb002);
211         rset(RBX, bssend);
212         return 1;
213 }
214
215 typedef struct ELFHeader ELFHeader;
216 struct ELFHeader {
217         uintptr entry, phoff, shoff;
218         u32int flags;
219         u16int ehsize;
220         u16int phentsize, phnum;
221         u16int shentsize, shnum, shstrndx;
222 };
223 typedef struct ELFPHeader ELFPHeader;
224 struct ELFPHeader {
225         u32int type, flags;
226         uintptr offset, vaddr, paddr, filesz, memsz, align;
227 };
228 enum {
229         PT_NULL, PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_NOTE,
230         PT_SHLIB, PT_PHDR, PT_TLS,
231         PT_GNU_EH_FRAME = 0x6474e550,
232         PT_GNU_RELRO = 0x6474e552,
233         PT_OPENBSD_RANDOMIZE = 0x65a3dbe6,
234 };
235 typedef struct ELFSHeader ELFSHeader;
236 struct ELFSHeader {
237         u32int iname, type;
238         uintptr flags, addr, offset, size;
239         u32int link, info;
240         uintptr addralign, entsize;
241         char *name;
242 };
243 enum {
244         SHT_SYMTAB = 2,
245         SHT_STRTAB = 3,
246         SHF_ALLOC = 2,
247 };
248 typedef struct ELFSymbol ELFSymbol;
249 struct ELFSymbol {
250         u32int iname;
251         uintptr addr, size;
252         u8int info, other;
253         u16int shndx;
254         char *name;
255 };
256 static ELFHeader eh;
257 static ELFPHeader *ph;
258 static ELFSHeader *sh;
259 static ELFSymbol *sym;
260 static int nsym;
261 static uintptr elfmax;
262
263 static u64int
264 elff(uchar **p, uchar *e, int sz)
265 {
266         u64int rc;
267
268         if(sz == -1)
269                 sz = elf64 ? 8 : 4;
270         if(*p + sz > e){
271                 fprint(2, "out of bounds: %#p > %#p", *p + sz, e);
272                 return 0;
273         }
274         switch(sz){
275         case 1: rc = GET8(*p, 0); break;
276         case 2: rc = GET16(*p, 0); break;
277         case 3: case 4: rc = GET32(*p, 0); break;
278         default: rc = GET64(*p, 0); break;
279         }
280         *p += sz;
281         return rc;
282 }
283
284 static void
285 elfheader(ELFHeader *eh, uchar *p, uchar *e)
286 {
287         eh->entry = elff(&p, e, -1);
288         eh->phoff = elff(&p, e, -1);
289         eh->shoff = elff(&p, e, -1);
290         eh->flags = elff(&p, e, 4);
291         eh->ehsize = elff(&p, e, 2);
292         eh->phentsize = elff(&p, e, 2);
293         eh->phnum = elff(&p, e, 2);
294         eh->shentsize = elff(&p, e, 2);
295         eh->shnum = elff(&p, e, 2);
296         eh->shstrndx = elff(&p, e, 2);
297 }
298
299 static void
300 elfpheader(ELFPHeader *ph, uchar *p, uchar *e)
301 {
302         ph->type = elff(&p, e, 4);
303         if(elf64) ph->flags = elff(&p, e, 4);
304         ph->offset = elff(&p, e, -1);
305         ph->vaddr = elff(&p, e, -1);
306         ph->paddr = elff(&p, e, -1);
307         ph->filesz = elff(&p, e, -1);
308         ph->memsz = elff(&p, e, -1);
309         if(!elf64) ph->flags = elff(&p, e, 4);
310         ph->align = elff(&p, e, -1);
311 }
312
313 static void
314 elfsheader(ELFSHeader *sh, uchar *p, uchar *e)
315 {
316         sh->iname = elff(&p, e, 4);
317         sh->type = elff(&p, e, 4);
318         sh->flags = elff(&p, e, -1);
319         sh->addr = elff(&p, e, -1);
320         sh->offset = elff(&p, e, -1);
321         sh->size = elff(&p, e, -1);
322         sh->link = elff(&p, e, 4);
323         sh->info = elff(&p, e, 4);
324         sh->addralign = elff(&p, e, -1);
325         sh->entsize = elff(&p, e, -1);
326 }
327
328 static void
329 elfsymbol(ELFSymbol *s, uchar *p, uchar *e)
330 {
331         s->iname = elff(&p, e, 4);
332         if(elf64){
333                 s->info = elff(&p, e, 1);
334                 s->other = elff(&p, e, 1);
335                 s->shndx = elff(&p, e, 2);
336                 s->addr = elff(&p, e, -1);
337                 s->size = elff(&p, e, -1);
338         }else{
339                 s->addr = elff(&p, e, -1);
340                 s->size = elff(&p, e, -1);
341                 s->info = elff(&p, e, 1);
342                 s->other = elff(&p, e, 1);
343                 s->shndx = elff(&p, e, 2);
344         }
345 }
346
347 static void
348 epreadn(void *buf, ulong sz, vlong off, char *fn)
349 {
350         seek(fd, off, 0);
351         werrstr("eof");
352         if(readn(fd, buf, sz) < sz)
353                 sysfatal("%s: read: %r", fn);
354 }
355
356 static int
357 elfheaders(void)
358 {
359         uchar *buf;
360         int i;
361         ELFSHeader *s;
362
363         if(GET32(hdr, 0) != 0x464c457f) return 0;
364         if(hdr[5] != 1 || hdr[6] != 1 || hdr[0x14] != 1) return 0;
365         switch(hdr[4]){
366         case 1: elf64 = 0; break;
367         case 2: elf64 = 1; if(sizeof(uintptr) == 4) sysfatal("64-bit binaries not supported on 32-bit host"); break;
368         default: return 0;
369         }
370         elfheader(&eh, hdr + 0x18, hdr + sizeof(hdr));
371         buf = emalloc(eh.phentsize > eh.shentsize ? eh.phentsize : eh.shentsize);
372         ph = emalloc(sizeof(ELFPHeader) * eh.phnum);
373         for(i = 0; i < eh.phnum; i++){
374                 epreadn(buf, eh.phentsize, eh.phoff + i * eh.phentsize, "elfheaders");
375                 elfpheader(&ph[i], buf, buf + eh.phentsize);
376         }
377         sh = emalloc(sizeof(ELFSHeader) * eh.shnum);
378         for(i = 0; i < eh.shnum; i++){
379                 epreadn(buf, eh.shentsize, eh.shoff + i * eh.shentsize, "elfheaders");
380                 elfsheader(&sh[i], buf, buf + eh.shentsize);
381         }
382         free(buf);
383         
384         if(eh.shstrndx != 0 && eh.shstrndx < eh.shnum){
385                 s = &sh[eh.shstrndx];
386                 if(s->type != SHT_STRTAB)
387                         sysfatal("elfheaders: section string table is not a string table");
388                 buf = emalloc(s->size + 1);
389                 epreadn(buf, s->size, s->offset, "elfheaders");
390                 for(i = 0; i < eh.shnum; i++){
391                         if(sh[i].iname < s->size)
392                                 sh[i].name = (char *) &buf[sh[i].iname];
393                 }
394         }
395         
396         return 1;
397 }
398
399 static void
400 elfdata(void)
401 {
402         int i;
403         void *v;
404
405         for(i = 0; i < eh.phnum; i++){
406                 switch(ph[i].type){
407                 case PT_NULL:
408                 case PT_GNU_RELRO:
409                 case PT_NOTE:
410                         continue;
411                 case PT_DYNAMIC:
412                 case PT_INTERP:
413                         sysfatal("elf: dynamically linked");
414                 default:
415                         sysfatal("elf: unknown program header type %#ux", (int)ph[i].type);
416                 case PT_LOAD:
417                 case PT_PHDR:
418                 case PT_OPENBSD_RANDOMIZE:
419                         break;
420                 }
421                 v = gptr(ph[i].paddr, ph[i].memsz);
422                 if(v == nil)
423                         sysfatal("invalid address %#p (length=%#p) in elf", (void*)ph[i].paddr, (void*)ph[i].memsz);
424                 if(ph[i].type == PT_OPENBSD_RANDOMIZE)
425                         genrandom(v, ph[i].memsz);
426                 else{
427                         if(ph[i].filesz > ph[i].memsz)
428                                 sysfatal("elf: header entry shorter in memory than in the file (%#p < %#p)", (void*)ph[i].memsz, (void*)ph[i].filesz);
429                         if(ph[i].filesz != 0)
430                                 epreadn(v, ph[i].filesz, ph[i].offset, "elfdata");
431                         if(ph[i].filesz < ph[i].memsz)
432                                 memset((uchar*)v + ph[i].filesz, 0, ph[i].memsz - ph[i].filesz);
433                 }
434                 if(ph[i].paddr + ph[i].memsz > elfmax)
435                         elfmax = ph[i].paddr + ph[i].memsz;
436         }
437 }
438
439 static int
440 elfsymbols(void)
441 {
442         ELFSHeader *s, *sy, *st;
443         char *str;
444         uchar *buf, *p;
445         int i;
446         
447         sy = nil;
448         st = nil;
449         for(s = sh; s < sh + eh.shnum; s++){
450                 if(s->type == SHT_SYMTAB && s->name != nil && strcmp(s->name, ".symtab") == 0) 
451                         sy = s;
452                 if(s->type == SHT_STRTAB && s->name != nil && strcmp(s->name, ".strtab") == 0)
453                         st = s;
454         }
455         if(sy == nil || st == nil)
456                 return 0;
457         if(sy->entsize == 0 || (sy->size % sy->entsize) != 0)
458                 sysfatal("symbol section: invalid headers");
459         str = emalloc(st->size);
460         epreadn(str, st->size, st->offset, "elfsymbols");
461         buf = emalloc(sy->size);
462         epreadn(buf, sy->size, sy->offset, "elfsymbols");
463         nsym = sy->size / sy->entsize;
464         sym = emalloc(sizeof(ELFSymbol) * nsym);
465         for(i = 0; i < nsym; i++){
466                 p = buf + i * sy->entsize;
467                 elfsymbol(sym + i, p, p + sy->entsize);
468                 if(sym[i].iname < st->size)
469                         sym[i].name = &str[sym[i].iname];
470         }
471         free(buf);
472         return 1;
473 }
474
475 static ELFSymbol *
476 elfsym(char *n)
477 {
478         ELFSymbol *s;
479         
480         for(s = sym; s < sym + nsym; s++)
481                 if(s->name != nil && strcmp(s->name, n) == 0)
482                         return s;
483         return nil;
484 }
485
486 static void *
487 symaddr(ELFSymbol *s)
488 {
489         ELFPHeader *p;
490
491         if(s == nil) return nil;
492         for(p = ph; p < ph + eh.phnum; p++)
493                 if(s->addr >= p->vaddr && s->addr < p->vaddr + p->memsz)
494                         return gptr(p->paddr + (s->addr - p->vaddr), s->size);
495         return nil;
496 }
497
498 static uchar *obsdarg, *obsdarg0, *obsdargnext;
499 static int obsdarglen;
500 static int obsdconsdev = 12 << 8, obsddbcons = -1, obsdbootdev;
501
502 enum {
503         BOOTARG_MEMMAP,
504         BOOTARG_DISKINFO,
505         BOOTARG_APMINFO,
506         BOOTARG_CKSUMLEN,
507         BOOTARG_PCIINFO,
508         BOOTARG_CONSDEV,
509         BOOTARG_SMPINFO,
510         BOOTARG_BOOTMAC,
511         BOOTARG_DDB,
512         BOOTARG_BOOTDUID,
513         BOOTARG_BOOTSR,
514         BOOTARG_EFIINFO,
515         BOOTARG_END = -1,
516 };
517
518 static void
519 obsdelfload(void)
520 {
521         void *v, *w, *hdrfix;
522         int shentsize;
523         int saddr;
524         ELFSHeader *s;
525         uintptr off;
526         
527         saddr = elf64 ? 8 : 4;
528         elfmax = -(-elfmax & -saddr);
529         v = gptr(elfmax, eh.ehsize);
530         if(v == nil)
531 space:          sysfatal("out of space for kernel");
532         epreadn(v, eh.ehsize, 0, "obsdelfload");
533         elfmax += -(-eh.ehsize & -saddr);
534         hdrfix = (uchar*)v + (elf64 ? 0x20 : 0x1c);
535         
536         shentsize = 40 + 24*elf64;
537         v = gptr(elfmax, shentsize * eh.shnum);
538         if(v == nil) goto space;
539         off = shentsize * eh.shnum;
540         elfmax += off;
541         off += -(-eh.ehsize & -saddr);
542         for(s = sh; s < sh + eh.shnum; s++)
543                 if(s->type == SHT_SYMTAB || s->type == SHT_STRTAB ||
544                 s->name != nil && (strcmp(s->name, ".debug_line") == 0 || strcmp(s->name, ".SUNW_ctf") == 0)){
545                         w = gptr(elfmax, s->size);
546                         if(w == nil) goto space;
547                         epreadn(w, s->size, s->offset, "obsdelfload");
548                         v = pack(v, "iizzzziizz",
549                                 s->iname, s->type, s->flags | SHF_ALLOC, (uintptr)0,
550                                 off, s->size, s->link, s->info, s->addralign, s->entsize);
551                         elfmax += -(-s->size & -saddr);
552                         off += -(-s->size & -saddr);
553                 }else{
554                         memset(v, 0, shentsize);
555                         v = (uchar*)v + shentsize;
556                 }
557         pack(hdrfix, "zz......sss", (uintptr)0, -(-eh.ehsize & -saddr), 0, 0, shentsize);
558 }
559
560 #define obsdpack(...) (obsdarg = pack(obsdarg, __VA_ARGS__))
561
562 static void
563 obsdstart(int type)
564 {
565         obsdarg0 = obsdarg;
566         PUT32(obsdarg, 0, type);
567         PUT32(obsdarg, 8, 0); /* next */
568         obsdarg += 12;
569 }
570
571 static void
572 obsdend(void)
573 {
574         if(obsdarg == obsdarg0 + 12) obsdarg += 4;
575         PUT32(obsdarg0, 4, obsdarg - obsdarg0); /* size */
576         obsdarglen += obsdarg - obsdarg0;
577         PUT32(obsdargnext, 0, gpa(obsdarg0));
578         obsdargnext = obsdarg0 + 8;
579         obsdarg0 = nil; 
580 }
581
582 static void
583 obsdfb(void)
584 {
585         int i, s, p;
586         u32int r, g, b, a, m;
587
588         if(curmode == nil || curmode == &textmode) return;
589         p = r = g = b = a = 0;
590         for(i = 0; i < 4; i++){
591                 s = curmode->chan >> 8 * i & 0xf;
592                 if(s == 0) continue;
593                 m = (1<<s)-1 << p;
594                 p += s;
595                 switch(curmode->chan >> 4 + 8 * i & 0xf){
596                 case CRed: r |= m; break;
597                 case CGreen: g |= m; break;
598                 case CBlue: b |= m; break;
599                 case CAlpha: case CIgnore: a |= m; break;
600                 default: return;
601                 }
602         }
603         obsdstart(BOOTARG_EFIINFO);
604         obsdpack("vvvviiiiiii", 0ULL, 0ULL, (uvlong)fbaddr, (uvlong)fbsz, curmode->h, curmode->w, curmode->w, r, g, b, a);
605         obsdend();
606 }
607
608 static void
609 obsdargs(void)
610 {
611         Region *r;
612         int t;
613
614         obsdstart(BOOTARG_MEMMAP);
615         for(r = mmap; r != nil; r = r->next){
616                 t = biostype(r);
617                 if(t == 0) continue;
618                 obsdpack("vvi", (uvlong)r->start, (uvlong)(r->end - r->start), t);
619         }
620         obsdpack("vvi", 0ULL, 0ULL, 0);
621         obsdend();
622         obsdstart(BOOTARG_CONSDEV); obsdpack("iiii", obsdconsdev, -1, -1, 0); obsdend();
623         if(obsddbcons != -1){
624                 obsdstart(BOOTARG_DDB); obsdpack("i", obsddbcons); obsdend();
625         }
626         obsdfb();
627         obsdstart(BOOTARG_END); obsdend();
628 }
629
630 static int
631 obsdcmdline(int argc, char **argv)
632 {
633         char *p;
634         char *q, *r;
635         int howto;
636         
637         howto = 0;
638         while(argc-- > 0){
639                 p = *argv++;
640                 if(*p == '-'){
641                         while(*++p != 0)
642                                 switch(*p){
643                                 case 'a': howto |= 0x0001; break; /* RB_ASKNAME */
644                                 case 's': howto |= 0x0002; break; /* RB_SINGLE */
645                                 case 'd': howto |= 0x0040; break; /* RB_DDB */
646                                 case 'c': howto |= 0x0400; break; /* RB_CONFIG */
647                                 default: goto usage;
648                                 }
649                         continue;
650                 }
651                 q = strchr(p, '=');
652                 if(q == nil) goto usage;
653                 *q++ = 0;
654                 if(strcmp(p, "device") == 0){
655                         obsdbootdev = 0;
656                         switch(*q){
657                         case 'w': break;
658                         case 'f': obsdbootdev = 2; break;
659                         case 's': obsdbootdev = 4; break;
660                         case 'c': obsdbootdev = 6; break;
661                         case 'r': obsdbootdev = 17; break;
662                         case 'v': obsdbootdev = 14; if(*++q != 'n') goto nodev; break;
663                         default: nodev: sysfatal("invalid device");
664                         }
665                         if(*++q != 'd') goto nodev;
666                         obsdbootdev |= strtoul(++q, &r, 10) << 16;
667                         if(r == q || (obsdbootdev & 0xfff00000) != 0) goto nodev;
668                         if(*r < 'a' || *r > 'p') goto nodev;
669                         obsdbootdev |= *r - 'a' << 8;
670                         if(*++r != 0) goto nodev;
671                         obsdbootdev |= 0xa0000000;
672                 }else if(strcmp(p, "tty") == 0){
673                         if(strcmp(q, "com0") == 0)
674                                 obsdconsdev = 8 << 8;
675                         else if(strcmp(q, "com1") == 0)
676                                 obsdconsdev = 8 << 8 | 1;
677                         else if(strcmp(q, "pc0") == 0)
678                                 obsdconsdev = 12 << 8;
679                         else
680                                 sysfatal("tty must be one of com0, com1, pc0");
681                 }else if(strcmp(p, "db_console") == 0){
682                         if(strcmp(q, "on") == 0)
683                                 obsddbcons = 1;
684                         else if(strcmp(q, "off") == 0)
685                                 obsddbcons = 0;
686                         else
687                                 sysfatal("db_console must be one of on, off");
688                 }else goto usage;
689         }
690         return howto;
691 usage:
692         fprint(2, "openbsd cmdline usage: kernel [-asdc] [var=value ...]\nsupported vars: device tty db_console\n");
693         threadexitsall("usage");
694         return 0;
695 }
696
697 static int
698 obsdload(void)
699 {
700         int sp;
701         int howto;
702         uchar *v;
703         
704         obsdelfload();
705         sp = 0xfffc;
706         sp -= 36;
707         v = gptr(sp, 36);
708         howto = obsdcmdline(cmdlinen, cmdlinev);
709         assert(v != nil);
710         PUT32(v, 4, howto); /* howto */
711         PUT32(v, 8, obsdbootdev); /* bootdev */
712         PUT32(v, 12, 0xa); /* bootapiver */
713         PUT32(v, 16, elfmax); /* esym */
714         PUT32(v, 20, 0); /* extmem */
715         PUT32(v, 24, 0); /* cnvmem */
716         PUT32(v, 32, 0); /* bootargv */
717         obsdarg = gptr(0x10000, 4096);
718         assert(obsdarg != nil);
719         obsdargnext = &v[32]; /* bootargv */
720         obsdargs();
721         assert(obsdarg0 == nil);
722         PUT32(v, 28, obsdarglen); /* bootargc */
723         rset(RSP, sp);
724         rset(RPC, (u32int)eh.entry & 0x0fffffff);
725         return 1;
726 }
727
728 static int
729 tryelf(void)
730 {
731         char *s;
732
733         if(!elfheaders()) return 0;
734         elfdata();
735         if(!elfsymbols()) return 0;
736         s = symaddr(elfsym("ostype"));
737         if(s != nil && strcmp(s, "OpenBSD") == 0)
738                 return obsdload();
739         return 0;
740 }
741
742 static void
743 linuxbootmod(char *fn, void *zp, u32int kend)
744 {
745         u32int addr;
746         uintptr memend;
747         int fd;
748         vlong sz;
749         void *v;
750         int rc;
751         
752         fd = open(fn, OREAD);
753         if(fd < 0) sysfatal("linux: initrd: open: %r");
754         sz = seek(fd, 0, 2);
755         if(sz < 0) sysfatal("linux: initrd: seek: %r");
756         if(sz == 0) sysfatal("linux: empty initrd");
757         addr = GET32(zp, 0x22c);
758         memend = (1<<20) + gavail(gptr(1<<20, 0));
759         if(addr >= memend) addr = memend - 1;
760         if((addr - (sz - 1) & -4) < kend) sysfatal("linux: no room for initrd");
761         addr = addr - (sz - 1) & -4;
762         v = gptr(addr, sz);
763         if(v == nil) sysfatal("linux: initrd: gptr failed");
764         seek(fd, 0, 0);
765         rc = readn(fd, v, sz);
766         if(rc < 0) sysfatal("linux: initrd: read: %r");
767         if(rc < sz) sysfatal("linux: initrd: short read");
768         close(fd);
769         PUT32(zp, 0x218, addr);
770         PUT32(zp, 0x21C, sz);
771 }
772
773 static void
774 linuxscreeninfo(void *zp)
775 {
776         extern VgaMode *curmode, textmode;
777         extern uintptr fbaddr, fbsz;
778         uintptr extmem;
779         int i, p, s;
780         
781         extmem = gavail(gptr(1<<20, 0)) >> 10;
782         if(extmem >= 65535) extmem = 65535;
783         PUT16(zp, 0x02, extmem);
784         
785         if(curmode == nil) return;
786         if(curmode == &textmode){
787                 PUT8(zp, 0x06, 3); /* mode 3 */
788                 PUT8(zp, 0x07, 80); /* 80 cols */
789                 PUT8(zp, 0x0e, 25); /* 25 rows */
790                 PUT8(zp, 0x0f, 0x22); /* VGA */
791                 PUT16(zp, 0x10, 16); /* characters are 16 pixels high */
792         }else{
793                 PUT8(zp, 0x0f, 0x23); /* VESA linear framebuffer */
794                 PUT16(zp, 0x12, curmode->w);
795                 PUT16(zp, 0x14, curmode->h);
796                 PUT16(zp, 0x16, chantodepth(curmode->chan));
797                 PUT32(zp, 0x18, fbaddr);
798                 PUT32(zp, 0x1C, fbsz);
799                 PUT16(zp, 0x24, curmode->hbytes);
800                 for(i = 0, p = 0; i < 4; i++){
801                         s = curmode->chan >> 8 * i & 15;
802                         if(s == 0) continue;
803                         switch(curmode->chan >> 8 * i + 4 & 15){
804                         case CRed: PUT16(zp, 0x26, s | p << 8); break;
805                         case CGreen: PUT16(zp, 0x28, s | p << 8); break;
806                         case CBlue: PUT16(zp, 0x2a, s | p << 8); break;
807                         case CAlpha: case CIgnore:  PUT16(zp, 0x2c, s | p << 8); ; break;
808                         }
809                         p += s;
810                 }
811                 PUT16(zp, 0x34, 1<<0|1<<1|1<<3|1<<4|1<<5|1<<6|1<<7); /* attributes */
812         }
813 }
814
815 static void
816 linuxgdt(void *v)
817 {
818         u32int base;
819         
820         base = gpa(v);
821         rset("gdtrbase", base);
822         v = pack(v, "vvvv", 0, 0,
823                 GDTBASE(0) | GDTLIM(-1) | GDTRX | GDTG | GDTP | GDT32,
824                 GDTBASE(0) | GDTLIM(-1) | GDTRW | GDTG | GDTP | GDT32
825         );
826         rset("gdtrlimit", gpa(v) - base - 1);
827         rset("cs", 0x10);
828         rset("ds", 0x18);
829         rset("es", 0x18);
830         rset("ss", 0x18);
831 }
832
833 static void
834 linuxe820(uchar *zp)
835 {
836         Region *r;
837         uchar *v;
838         int t;
839         int n;
840         
841         v = zp + 0x2d0;
842         n = 1;
843         for(r = mmap; r != nil; r = r->next){
844                 t = biostype(r);
845                 if(t == 0) continue;
846                 v = pack(v, "vvi", r->start, r->end - r->start, t);
847                 n++;
848         }
849         PUT8(zp, 0x1e8, n);
850 }
851
852 static int
853 trylinux(void)
854 {
855         char buf[1024];
856         u8int loadflags;
857         u16int version;
858         uchar *zp;
859         void *v;
860         u32int ncmdline, cmdlinemax, syssize, setupsects;
861         
862         seek(fd, 0, 0);
863         if(readn(fd, buf, sizeof(buf)) < 1024) return 0;
864         if(GET16(buf, 0x1FE) != 0xAA55 || GET32(buf, 0x202) != 0x53726448) return 0;
865         version = GET16(buf, 0x206);
866         if(version < 0x206){
867                 vmerror("linux: kernel too old (boot protocol version %d.%.2d, needs to be 2.06 or newer)", version >> 8, version & 0xff);
868                 return 0;
869         }
870         loadflags = GET8(buf, 0x211);
871         if((loadflags & 1) == 0){
872                 vmerror("linux: zImage is not supported");
873                 return 0;
874         }
875         zp = gptr(0x1000, 0x1000);
876         if(zp == nil) sysfatal("linux: gptr for zeropage failed");
877         rset(RSI, 0x1000);
878         memset(zp, 0, 0x1000);
879         memmove(zp + 0x1f1, buf + 0x1f1, 0x202 + GET8(buf, 0x201) - 0x1f1);
880         setupsects = GET8(zp, 0x1F1);
881         if(setupsects == 0) setupsects = 4;
882         syssize = GET32(zp, 0x1F4);
883         cmdlinemax = GET32(zp, 0x238);
884         
885         v = gptr(1<<20, syssize << 4);
886         if(v == nil) sysfatal("linux: not enough room for kernel");
887         epreadn(v, syssize << 4, (setupsects + 1) * 512, "trylinux");
888         
889         v = gptr(0x20000, 1);
890         if(v == nil) sysfatal("linux: gptr for cmdline failed");
891         ncmdline = putcmdline(v);
892         if(ncmdline == 0)
893                 *(uchar*)v = 0;
894         else
895                 if(ncmdline - 1 > cmdlinemax) sysfatal("linux: cmdline too long (%d > %d)", ncmdline, cmdlinemax);
896         PUT32(zp, 0x228, 0x20000);
897         
898         switch(bootmodn){
899         case 0: break;
900         default:
901                 vmerror("linux: ignoring extra boot modules (only one supported)");
902                 /* wet floor */
903         case 1:
904                 linuxbootmod(*bootmod, zp, (1<<20) + (syssize << 4));
905         }
906         
907         linuxscreeninfo(zp);
908         v = gptr(0x3000, 256);
909         if(v == nil) sysfatal("linux: gptr for gdt failed");
910         linuxgdt(v);
911         
912         linuxe820(zp);
913         
914         PUT16(zp, 0x1FA, 0xffff);
915         PUT8(zp, 0x210, 0xFF); /* bootloader ID */
916         PUT8(zp, 0x211, loadflags | 0x80); /* kernel can use heap */
917
918         PUT32(zp, 0x224, 0xfe00); /* kernel can use full segment */
919         rset(RPC, GET32(zp, 0x214));
920         rset(RBP, 0);
921         rset(RDI, 0);
922         rset(RBX, 0);
923         return 1;
924 }
925
926
927 void
928 loadkernel(char *fn)
929 {
930         fd = open(fn, OREAD);
931         if(fd < 0) sysfatal("open: %r");
932         if(readn(fd, hdr, sizeof(hdr)) <= 0)
933                 sysfatal("readn: %r");
934         if(trymultiboot())
935                 goto done;
936         if(tryelf())
937                 goto done;
938         if(trylinux())
939                 goto done;
940         sysfatal("%s: unknown format", fn);
941 done:
942         close(fd);
943 }