diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /os/boot/pc/boot.c | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'os/boot/pc/boot.c')
| -rw-r--r-- | os/boot/pc/boot.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/os/boot/pc/boot.c b/os/boot/pc/boot.c new file mode 100644 index 00000000..0659edbd --- /dev/null +++ b/os/boot/pc/boot.c @@ -0,0 +1,451 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "../../../utils/libmach/elf.h" + +static uchar elfident[7] = { + '\177', 'E', 'L', 'F', '\1', '\1', '\1' +}; +static Ehdr ehdr, rehdr; +static Phdr *phdr; +static int curphdr; +static ulong curoff; +static ulong elftotal; +static long (*swal)(long); +static ushort (*swab)(ushort); + +/* + * big-endian short + */ +ushort +beswab(ushort s) +{ + uchar *p; + + p = (uchar*)&s; + return (p[0]<<8) | p[1]; +} + +/* + * big-endian long + */ +long +beswal(long l) +{ + uchar *p; + + p = (uchar*)&l; + return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; +} + +/* + * little-endian short + */ +ushort +leswab(ushort s) +{ + uchar *p; + + p = (uchar*)&s; + return (p[1]<<8) | p[0]; +} + +/* + * little-endian long + */ +long +leswal(long l) +{ + uchar *p; + + p = (uchar*)&l; + return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0]; +} + +/* + * Convert header to canonical form + */ +static void +hswal(long *lp, int n, long (*swap) (long)) +{ + while (n--) { + *lp = (*swap) (*lp); + lp++; + } +} + +static int +readehdr(Boot *b) +{ + int i; + + /* bitswap the header according to the DATA format */ + if(ehdr.ident[CLASS] != ELFCLASS32) { + print("bad ELF class - not 32 bit\n"); + return 0; + } + if(ehdr.ident[DATA] == ELFDATA2LSB) { + swab = leswab; + swal = leswal; + } else if(ehdr.ident[DATA] == ELFDATA2MSB) { + swab = beswab; + swal = beswal; + } else { + print("bad ELF encoding - not big or little endian\n"); + return 0; + } + memmove(&rehdr, &ehdr, sizeof(Ehdr)); + + ehdr.type = swab(ehdr.type); + ehdr.machine = swab(ehdr.machine); + ehdr.version = swal(ehdr.version); + ehdr.elfentry = swal(ehdr.elfentry); + ehdr.phoff = swal(ehdr.phoff); + ehdr.shoff = swal(ehdr.shoff); + ehdr.flags = swal(ehdr.flags); + ehdr.ehsize = swab(ehdr.ehsize); + ehdr.phentsize = swab(ehdr.phentsize); + ehdr.phnum = swab(ehdr.phnum); + ehdr.shentsize = swab(ehdr.shentsize); + ehdr.shnum = swab(ehdr.shnum); + ehdr.shstrndx = swab(ehdr.shstrndx); + if(ehdr.type != EXEC || ehdr.version != CURRENT) + return 0; + if(ehdr.phentsize != sizeof(Phdr)) + return 0; + + if(debug) + print("readehdr OK entry 0x%lux\n", ehdr.elfentry); + + curoff = sizeof(Ehdr); + i = ehdr.phoff+ehdr.phentsize*ehdr.phnum - curoff; + b->state = READPHDR; + b->bp = (char*)malloc(i); + b->wp = b->bp; + b->ep = b->wp + i; + phdr = (Phdr*)(b->bp + ehdr.phoff-sizeof(Ehdr)); + if(debug) + print("phdr..."); + + return 1; +} + +static int +nextphdr(Boot *b) +{ + Phdr *php; + ulong entry, offset; + char *paddr; + + if(debug) + print("readedata %d\n", curphdr); + + for(; curphdr < ehdr.phnum; curphdr++){ + php = phdr+curphdr; + if(php->type != LOAD) + continue; + offset = php->offset; + paddr = (char*)PADDR(php->paddr); + if(offset < curoff){ + /* + * Can't (be bothered to) rewind the + * input, it might be from tftp. If we + * did then we could boot FreeBSD kernels + * too maybe. + */ + return 0; + } + if(php->offset > curoff){ + b->state = READEPAD; + b->bp = (char*)malloc(offset - curoff); + b->wp = b->bp; + b->ep = b->wp + offset - curoff; + if(debug) + print("nextphdr %lud...\n", offset - curoff); + return 1; + } + b->state = READEDATA; + b->bp = paddr; + b->wp = b->bp; + b->ep = b->wp+php->filesz; + print("%ud+", php->filesz); + elftotal += php->filesz; + if(debug) + print("nextphdr %ud@0x%p\n", php->filesz, paddr); + + return 1; + } + + if(curphdr != 0){ + print("=%lud\n", elftotal); + b->state = TRYBOOT; + entry = ehdr.elfentry & ~0xF0000000; + PLLONG(b->exec.entry, entry); + return 1; + } + + return 0; +} + +static int +readepad(Boot *b) +{ + Phdr *php; + + php = phdr+curphdr; + if(debug) + print("readepad %d\n", curphdr); + curoff = php->offset; + + return nextphdr(b); +} + +static int +readedata(Boot *b) +{ + Phdr *php; + + php = phdr+curphdr; + if(debug) + print("readedata %d\n", curphdr); + if(php->filesz < php->memsz){ + print("%lud", php->memsz-php->filesz); + elftotal += php->memsz-php->filesz; + memset((char*)(PADDR(php->paddr)+php->filesz), 0, php->memsz-php->filesz); + } + curoff = php->offset+php->filesz; + curphdr++; + + return nextphdr(b); +} + +static int +readphdr(Boot *b) +{ + Phdr *php; + + php = phdr; + hswal((long*)php, ehdr.phentsize*ehdr.phnum/sizeof(long), swal); + if(debug) + print("phdr curoff %lud vaddr 0x%lux paddr 0x%lux\n", + curoff, php->vaddr, php->paddr); + + curoff = ehdr.phoff+ehdr.phentsize*ehdr.phnum; + curphdr = 0; + + return nextphdr(b); +} + +static int +addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf) +{ + int n; + + n = edbuf - *dbuf; + if(n <= 0) + return 0; + if(n > esbuf - *sbuf) + n = esbuf - *sbuf; + if(n <= 0) + return -1; + + memmove(*dbuf, *sbuf, n); + *sbuf += n; + *dbuf += n; + return edbuf - *dbuf; +} + +int +bootpass(Boot *b, void *vbuf, int nbuf) +{ + char *buf, *ebuf; + Exec *ep; + ulong entry, data, text, bss; + + if(b->state == FAILED) + return FAIL; + + if(nbuf == 0) + goto Endofinput; + + buf = vbuf; + ebuf = buf+nbuf; + while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) { + switch(b->state) { + case INITKERNEL: + b->state = READEXEC; + b->bp = (char*)&b->exec; + b->wp = b->bp; + b->ep = b->bp+sizeof(Exec); + break; + case READEXEC: + ep = &b->exec; + if(GLLONG(ep->magic) == I_MAGIC) { + b->state = READ9TEXT; + b->bp = (char*)PADDR(GLLONG(ep->entry)); + b->wp = b->bp; + b->ep = b->wp+GLLONG(ep->text); + print("%lud", GLLONG(ep->text)); + break; + } + + /* check for gzipped kernel */ + if(b->bp[0] == 0x1F && (uchar)b->bp[1] == 0x8B && b->bp[2] == 0x08) { + b->state = READGZIP; + b->bp = (char*)malloc(1440*1024); + b->wp = b->bp; + b->ep = b->wp + 1440*1024; + memmove(b->bp, &b->exec, sizeof(Exec)); + b->wp += sizeof(Exec); + print("gz..."); + break; + } + + /* + * Check for ELF. + */ + if(memcmp(b->bp, elfident, 4) == 0){ + b->state = READEHDR; + b->bp = (char*)&ehdr; + b->wp = b->bp; + b->ep = b->wp + sizeof(Ehdr); + memmove(b->bp, &b->exec, sizeof(Exec)); + b->wp += sizeof(Exec); + print("elf..."); + break; + } + + print("bad kernel format\n"); + b->state = FAILED; + return FAIL; + + case READ9TEXT: + ep = &b->exec; + b->state = READ9DATA; + b->bp = (char*)PGROUND(PADDR(GLLONG(ep->entry))+GLLONG(ep->text)); + b->wp = b->bp; + b->ep = b->wp + GLLONG(ep->data); + print("+%ld", GLLONG(ep->data)); + break; + + case READ9DATA: + ep = &b->exec; + bss = GLLONG(ep->bss); + print("+%ld=%ld\n", + bss, GLLONG(ep->text)+GLLONG(ep->data)+bss); + b->state = TRYBOOT; + return ENOUGH; + + case READEHDR: + if(!readehdr(b)){ + print("readehdr failed\n"); + b->state = FAILED; + return FAIL; + } + break; + + case READPHDR: + if(!readphdr(b)){ + b->state = FAILED; + return FAIL; + } + break; + + case READEPAD: + if(!readepad(b)){ + b->state = FAILED; + return FAIL; + } + break; + + case READEDATA: + if(!readedata(b)){ + b->state = FAILED; + return FAIL; + } + if(b->state == TRYBOOT) + return ENOUGH; + break; + + case TRYBOOT: + case READGZIP: + return ENOUGH; + + case READ9LOAD: + case INIT9LOAD: + panic("9load"); + + default: + panic("bootstate"); + } + } + return MORE; + + +Endofinput: + /* end of input */ + switch(b->state) { + case INITKERNEL: + case READEXEC: + case READ9TEXT: + case READ9DATA: + case READEHDR: + case READPHDR: + case READEPAD: + case READEDATA: + print("premature EOF\n"); + b->state = FAILED; + return FAIL; + + case TRYBOOT: + entry = GLLONG(b->exec.entry); + print("entry: 0x%lux\n", entry); + warp9(PADDR(entry)); + b->state = FAILED; + return FAIL; + + case READGZIP: + ep = &b->exec; + if(b->bp[0] != 0x1F || (uchar)b->bp[1] != 0x8B || b->bp[2] != 0x08) + print("lost magic\n"); + + print("%ld => ", b->wp - b->bp); + if(gunzip((uchar*)ep, sizeof(*ep), (uchar*)b->bp, b->wp - b->bp) < sizeof(*ep)) { + print("badly compressed kernel\n"); + return FAIL; + } + + entry = GLLONG(ep->entry); + text = GLLONG(ep->text); + data = GLLONG(ep->data); + bss = GLLONG(ep->bss); + print("%lud+%lud+%lud=%lud\n", text, data, bss, text+data+bss); + + if(gunzip((uchar*)PADDR(entry)-sizeof(Exec), sizeof(Exec)+text+data, + (uchar*)b->bp, b->wp-b->bp) < sizeof(Exec)+text+data) { + print("error uncompressing kernel\n"); + return FAIL; + } + + /* relocate data to start at page boundary */ + memmove((void*)PGROUND(PADDR(entry+text)), (void*)(PADDR(entry+text)), data); + + print("entry: %lux\n", entry); + warp9(PADDR(entry)); + b->state = FAILED; + return FAIL; + + case INIT9LOAD: + case READ9LOAD: + panic("end 9load"); + + default: + panic("bootdone"); + } + b->state = FAILED; + return FAIL; +} |
