diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/boot/pc/dosboot.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/boot/pc/dosboot.c')
-rwxr-xr-x | sys/src/boot/pc/dosboot.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/sys/src/boot/pc/dosboot.c b/sys/src/boot/pc/dosboot.c new file mode 100755 index 000000000..9a0f5bc4f --- /dev/null +++ b/sys/src/boot/pc/dosboot.c @@ -0,0 +1,584 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "fs.h" + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; +/* fat 32 */ + uchar bigfatsize[4]; + uchar extflags[2]; + uchar fsversion[2]; + uchar rootdirstartclust[4]; + uchar fsinfosect[2]; + uchar backupbootsect[2]; +/* ??? + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +*/ +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar lowercase; + uchar hundredth; + uchar ctime[2]; + uchar cdate[2]; + uchar adate[2]; + uchar highstart[2]; + uchar mtime[2]; + uchar mdate[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DOSRONLY 0x01 +#define DOSHIDDEN 0x02 +#define DOSSYSTEM 0x04 +#define DOSVLABEL 0x08 +#define DOSDIR 0x10 +#define DOSARCH 0x20 + +/* + * predeclared + */ +static void bootdump(Dosboot*); +static void setname(Dosfile*, char*); + +/* + * debugging + */ +#define chatty 0 +#define chat if(chatty)print + +/* + * block io buffers + */ +enum +{ + Nbio= 16, +}; +typedef struct Clustbuf Clustbuf; +struct Clustbuf +{ + int age; + long sector; + uchar *iobuf; + Dos *dos; + int size; +}; +Clustbuf bio[Nbio]; + +/* + * get an io block from an io buffer + */ +Clustbuf* +getclust(Dos *dos, long sector) +{ + Fs *fs; + Clustbuf *p, *oldest; + int size; + + chat("getclust @ %ld\n", sector); + + /* + * if we have it, just return it + */ + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + p->age = m->ticks; + chat("getclust %ld in cache\n", sector); + return p; + } + } + + /* + * otherwise, reuse the oldest entry + */ + oldest = bio; + for(p = &bio[1]; p < &bio[Nbio]; p++){ + if(p->age <= oldest->age) + oldest = p; + } + p = oldest; + + /* + * make sure the buffer is big enough + */ + size = dos->clustsize*dos->sectsize; + if(p->iobuf==0 || p->size < size) + p->iobuf = ialloc(size, 0); + p->size = size; + + /* + * read in the cluster + */ + fs = (Fs*)dos; /* assume dos is embedded at start of an Fs */ + chat("getclust addr %lud %p %p %p\n", (ulong)((sector+dos->start)*(vlong)dos->sectsize), + fs, fs->diskseek, fs->diskread); + if(fs->diskseek(fs, (sector+dos->start)*(vlong)dos->sectsize) < 0){ + chat("can't seek block\n"); + return 0; + } + if(fs->diskread(fs, p->iobuf, size) != size){ + chat("can't read block\n"); + return 0; + } + + p->age = m->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %ld read\n", sector); + return p; +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + case 32: + k = 4*n; break; + default: + return -1; + } + if(k >= dos->fatsize*dos->sectsize) + panic("getfat"); + + if (dos->sectsize == 0 || dos->clustsize == 0) + panic("fatwalk: zero sector or cluster size"); + sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; + o = k%(dos->sectsize*dos->clustsize); + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o++]<<8; + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k = -1; + } + else if (dos->fatbits == 32){ + if(o >= dos->sectsize*dos->clustsize){ + p = getclust(dos, sect+dos->clustsize); + o = 0; + } + k |= p->iobuf[o++]<<16; + k |= p->iobuf[o]<<24; + if (k >= 0xfffffff8) + k = -1; + } + else + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %lud\n", n, k); + return k; +} + +/* + * map a file's logical cluster address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, long ltarget) +{ + Dos *dos = fp->dos; + long l; + long p; + + chat("fileaddr %8.8s %ld\n", fp->name, ltarget); + /* + * root directory is contiguous and easy (unless FAT32) + */ + if(fp->pstart == 0 && dos->rootsize != 0) { + if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) + return -1; + l = dos->rootaddr + ltarget*dos->clustsize; + chat("fileaddr %ld -> %ld\n", ltarget, l); + return l; + } + + /* + * anything else requires a walk through the fat + */ + if(ltarget >= fp->lcurrent && fp->pcurrent){ + /* start at the currrent point */ + l = fp->lcurrent; + p = fp->pcurrent; + } else { + /* go back to the beginning */ + l = 0; + p = fp->pstart; + } + while(l != ltarget){ + /* walk the fat */ + p = fatwalk(dos, p); + if(p < 0) + return -1; + l++; + } + fp->lcurrent = l; + fp->pcurrent = p; + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + l = dos->dataaddr + (p-2)*dos->clustsize; + chat("fileaddr %ld -> %ld\n", ltarget, l); + return l; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + long addr; + long rv; + int i; + int off; + Clustbuf *p; + uchar *from, *to; + + if((fp->attr & DOSDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + + to = a; + for(rv = 0; rv < n; rv+=i){ + /* + * read the cluster + */ + addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + + /* + * copy the bytes we need + */ + off = fp->offset % fp->dos->clustbytes; + from = &p->iobuf[off]; + i = n - rv; + if(i > fp->dos->clustbytes - off) + i = fp->dos->clustbytes - off; + memmove(to, from, i); + to += i; + fp->offset += i; + } + + return rv; +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +int +doswalk(File *f, char *name) +{ + Dosdir d; + long n; + Dosfile *file; + + chat("doswalk %s\n", name); + + file = &f->dos; + + if((file->attr & DOSDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(file, name); + + file->offset = 0; /* start at the beginning */ + while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ + chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext); + if(memcmp(file->name, d.name, sizeof(d.name)) != 0) + continue; + if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) + continue; + if(d.attr & DOSVLABEL){ + chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext); + continue; + } + file->attr = d.attr; + file->pstart = GSHORT(d.start); + if (file->dos->fatbits == 32) + file->pstart |= GSHORT(d.highstart) << 16; + file->length = GLONG(d.length); + file->pcurrent = 0; + file->lcurrent = 0; + file->offset = 0; + return 1; + } + return n >= 0 ? 0 : -1; +} + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read in a segment + */ +long +dosreadseg(File *f, void *va, long len) +{ + char *a; + long n, sofar; + Dosfile *fp; + + fp = &f->dos; + a = va; + for(sofar = 0; sofar < len; sofar += n){ + n = 8*1024; + if(len - sofar < n) + n = len - sofar; + n = dosread(fp, a + sofar, n); + if(n <= 0) + break; + print("."); + } + return sofar; +} + +int +dosinit(Fs *fs) +{ + Clustbuf *p; + Dosboot *b; + int i; + Dos *dos; + Dosfile *root; + +chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread); + + dos = &fs->dos; + /* defaults till we know better */ + dos->sectsize = 512; + dos->clustsize = 1; + + /* get first sector */ + p = getclust(dos, 0); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + +chat("dosinit0a\n"); + + p->dos = 0; /* don't cache this block */ + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + chat("no dos file system %x %x %x %x\n", + b->magic[0], b->magic[1], b->magic[2], b->magic[3]); + return -1; + } + + if(chatty) + bootdump(b); + + if(b->clustsize == 0) { +unreasonable: + if(chatty){ + print("unreasonable FAT BPB: "); + for(i=0; i<3+8+2+1; i++) + print(" %.2ux", p->iobuf[i]); + print("\n"); + } + return -1; + } + +chat("dosinit1\n"); + + /* + * Determine the systems' wondrous properties. + * There are heuristics here, but there's no real way + * of knowing if this is a reasonable FAT. + */ + dos->fatbits = 0; + dos->sectsize = GSHORT(b->sectsize); + if(dos->sectsize & 0xFF) + goto unreasonable; + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectsize*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->fatsize = GSHORT(b->fatsize); + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + if(dos->fatsize == 0) { + chat("fat32\n"); + dos->rootsize = 0; + dos->fatsize = GLONG(b->bigfatsize); + dos->fatbits = 32; + } + dos->fataddr = dos->nresrv; + if (dos->rootsize == 0) { + dos->rootaddr = 0; + dos->rootclust = GLONG(b->rootdirstartclust); + dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize; + } else { + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; + i = i/dos->sectsize; + dos->dataaddr = dos->rootaddr + i; + } + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatbits != 32) { + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + } + dos->freeptr = 2; + + if(dos->clustbytes < 512 || dos->clustbytes > 64*1024) + goto unreasonable; + +chat("dosinit2\n"); + + /* + * set up the root + */ + + fs->root.fs = fs; + root = &fs->root.dos; + root->dos = dos; + root->pstart = dos->rootsize == 0 ? dos->rootclust : 0; + root->pcurrent = root->lcurrent = 0; + root->offset = 0; + root->attr = DOSDIR; + root->length = dos->rootsize*sizeof(Dosdir); + +chat("dosinit3\n"); + + fs->read = dosreadseg; + fs->walk = doswalk; + return 0; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", (char*)b->version); + print("sectsize: %d ", GSHORT(b->sectsize)); + print("allocsize: %d ", b->clustsize); + print("nresrv: %d ", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d ", GSHORT(b->rootsize)); + print("volsize: %d ", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d ", GSHORT(b->fatsize)); + print("trksize: %d ", GSHORT(b->trksize)); + print("nheads: %d ", GSHORT(b->nheads)); + print("nhidden: %d ", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); +/* + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", b->label); +*/ +} + + +/* + * set up a dos file name + */ +static void +setname(Dosfile *fp, char *from) +{ + char *to; + + to = fp->name; + for(; *from && to-fp->name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to - fp->name < 8) + *to++ = ' '; + + /* from might be 12345678.123: don't save the '.' in ext */ + if(*from == '.') + from++; + + to = fp->ext; + for(; *from && to-fp->ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + while(to-fp->ext < 3) + *to++ = ' '; + + chat("name is %8.8s.%3.3s\n", fp->name, fp->ext); +} |