diff --git a/sys/src/boot/pc/fat.c b/sys/src/boot/pc/fat.c index 285a08594..a35d4b043 100644 --- a/sys/src/boot/pc/fat.c +++ b/sys/src/boot/pc/fat.c @@ -1,20 +1,42 @@ #include #include "fns.h" +#define GETSHORT(p) (*(ushort *)(p)) +#define GETLONG(p) (*(uint *)(p)) + enum { Sectsz = 0x200, Dirsz = 0x20, Maxpath = 64, + Fat12 = 1, + Fat16 = 2, + Fat32 = 4, }; typedef struct Extend Extend; typedef struct Dir Dir; typedef struct Pbs Pbs; +typedef struct Fat Fat; + +struct Fat +{ + ulong ver; + int drive; + ulong clustsize; + ulong eofmark; + ulong partlba; + ulong fatlba; + ulong dirstart; /* LBA for FAT16, cluster for FAT32 */ + ulong dirents; + ulong datalba; +}; struct Extend { - int drive; + Fat *fat; ulong lba; + ulong clust; + ulong lbaoff; ulong len; uchar *rp; uchar *ep; @@ -53,12 +75,34 @@ struct Pbs uchar nheads[2]; uchar nhidden[4]; uchar bigvolsize[4]; - uchar driveno; - uchar reserved0; - uchar bootsig; - uchar volid[4]; - uchar label[11]; - uchar type[8]; + union + { + struct + { + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; + } fat16; + struct + { + uchar fatsize[4]; + uchar flags[2]; + uchar ver[2]; + uchar rootclust[4]; + uchar fsinfo[2]; + uchar bootbak[2]; + uchar reserved0[12]; + uchar driveno; + uchar reserved1; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; + } fat32; + }; }; int readsect(ulong drive, ulong lba, void *buf); @@ -68,14 +112,47 @@ unload(void) { } +static ulong +readnext(Extend *ex, ulong clust) +{ + Fat *fat = ex->fat; + uint b = fat->ver; + ulong sect, off; + + sect = clust * b / Sectsz; + off = clust * b % Sectsz; + if(readsect(fat->drive, fat->fatlba + sect, ex->buf)) + memset(ex->buf, 0xff, 4); + switch(fat->ver){ + case Fat16: + return GETSHORT(&ex->buf[off]); + case Fat32: + return GETLONG(&ex->buf[off])& 0x0fffffff; + } + return 0; +} + int read(void *f, void *data, int len) { Extend *ex = f; + Fat *fat = ex->fat; - if(ex->len > 0 && ex->rp >= ex->ep) - if(readsect(ex->drive, ex->lba++, ex->rp = ex->buf)) + if(ex->len > 0 && ex->rp >= ex->ep){ + if(ex->clust != ~0U){ + if(ex->lbaoff % fat->clustsize == 0){ + if((ex->clust >> 4) == fat->eofmark) + return -1; + ex->lbaoff = (ex->clust - 2) * fat->clustsize; + putc('.'); + ex->clust = readnext(ex, ex->clust); + ex->lba = ex->lbaoff + fat->datalba; + } + ex->lbaoff++; + } + if(readsect(fat->drive, ex->lba++, ex->rp = ex->buf)) return -1; + } if(ex->len < len) len = ex->len; if(len > (ex->ep - ex->rp)) @@ -87,42 +164,21 @@ read(void *f, void *data, int len) } void -close(void *f) +open(Fat *fat, void *f, ulong lba) { Extend *ex = f; - ex->drive = 0; - ex->lba = 0; + ex->fat = fat; + ex->lba = lba; ex->len = 0; + ex->lbaoff = 0; + ex->clust = ~0U; ex->rp = ex->ep = ex->buf + Sectsz; } -static ulong -rootlba(Extend *fat) +void +close(void *) { - ulong lba; - Pbs *p = (Pbs*)fat->buf; - - lba = fat->lba; - lba += *((ushort*)p->nreserv); - lba += *((ushort*)p->fatsize) * p->nfats; - return lba; -} - -static ulong -dirlba(Extend *fat, Dir *d) -{ - ulong clust; - ulong dirs; - ulong lba; - Pbs *p = (Pbs*)fat->buf; - - lba = rootlba(fat); - dirs = *((ushort*)p->rootsize); - lba += (dirs * Dirsz + Sectsz-1) / Sectsz; - clust = *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo); - lba += (clust - 2) * p->clustsize; - return lba; } static int @@ -155,18 +211,27 @@ dirname(Dir *d, char buf[Maxpath]) return x - buf; } +static ulong +dirclust(Dir *d) +{ + return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo); +} + static int -fatwalk(Extend *ex, Extend *fat, char *path) +fatwalk(Extend *ex, Fat *fat, char *path) { char name[Maxpath], *end; - Pbs *pbs = (Pbs*)fat->buf; int i, j; Dir d; - close(ex); - ex->drive = fat->drive; - ex->lba = rootlba(fat); - ex->len = *((ushort*)pbs->rootsize) * Dirsz; + if(fat->ver == Fat32){ + open(fat, ex, 0); + ex->clust = fat->dirstart; + ex->len = ~0U; + }else{ + open(fat, ex, fat->dirstart); + ex->len = fat->dirents * Dirsz; + } for(;;){ if(readn(ex, &d, Dirsz) != Dirsz) break; @@ -178,25 +243,81 @@ fatwalk(Extend *ex, Extend *fat, char *path) end = path + strlen(path); j = end - path; if(i == j && memcmp(name, path, j) == 0){ - ex->rp = ex->ep; - ex->lba = dirlba(fat, &d); + open(fat, ex, 0); + ex->clust = dirclust(&d); ex->len = *((ulong*)d.len); if(*end == 0) return 0; else if(d.attr & 0x10){ - ex->len = pbs->clustsize * Sectsz; + ex->len = fat->clustsize * Sectsz; path = end; continue; } break; } } - close(ex); return -1; } static int -findfat(Extend *fat, int drive) +conffat(Fat *fat, void *buf) +{ + Pbs *p = buf; + uint fatsize, volsize, datasize, reserved; + uint ver, dirsize, dirents, clusters; + + /* sanity check */ + if(GETSHORT(p->sectsize) != Sectsz){ + print("sectsize != 512\r\n"); + halt(); + } + + /* load values from fat */ + fatsize = GETSHORT(p->fatsize); + if(fatsize == 0) + fatsize = GETLONG(p->fat32.fatsize); + volsize = GETSHORT(p->volsize); + if(volsize == 0) + volsize = GETLONG(p->bigvolsize); + reserved = GETSHORT(p->nreserv); + dirents = GETSHORT(p->rootsize); + dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz; + datasize = volsize - (reserved + fatsize * p->nfats + dirsize); + clusters = datasize / p->clustsize; + + /* determine fat type */ + if(clusters < 4085) + ver = Fat12; + else if(clusters < 65525) + ver = Fat16; + else + ver = Fat32; + + /* another check */ + if(ver == Fat12){ + print("TODO: implement FAT12\r\n"); + halt(); + } + + /* fill FAT descriptor */ + fat->ver = ver; + fat->dirents = dirents; + fat->clustsize = p->clustsize; + fat->fatlba = fat->partlba + reserved; + fat->dirstart = fat->fatlba + fatsize * p->nfats; + if(ver == Fat32){ + fat->datalba = fat->dirstart; + fat->dirstart = GETLONG(p->fat32.rootclust); + fat->eofmark = 0xffffff; + }else{ + fat->datalba = fat->dirstart + dirsize; + fat->eofmark = 0xfff; + } + return 0; +} + +static int +findfat(Fat *fat, int drive) { struct { uchar status; @@ -206,20 +327,22 @@ findfat(Extend *fat, int drive) uchar lba[4]; uchar len[4]; } *p; + uchar buf[Sectsz]; int i; - if(readsect(drive, 0, fat->buf)) + if(readsect(drive, 0, buf)) return -1; - if(fat->buf[0x1fe] != 0x55 || fat->buf[0x1ff] != 0xAA) + if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA) return -1; - p = (void*)&fat->buf[0x1be]; + p = (void*)&buf[0x1be]; for(i=0; i<4; i++){ if(p[i].status != 0x80) continue; - close(fat); fat->drive = drive; - fat->lba = *((ulong*)p[i].lba); - if(readsect(drive, fat->lba, fat->buf)) + fat->partlba = *((ulong*)p[i].lba); + if(readsect(drive, fat->partlba, buf)) + continue; + if(conffat(fat, buf)) continue; return 0; } @@ -231,12 +354,14 @@ start(void *sp) { char path[Maxpath], *kern; int drive; - Extend fat, ex; + Extend ex; + Fat fat; void *f; /* drive passed in DL */ drive = ((ushort*)sp)[5] & 0xFF; + print("9bootfat\r\n"); if(findfat(&fat, drive)){ print("no fat\r\n"); halt(); diff --git a/sys/src/boot/pc/pbs.s b/sys/src/boot/pc/pbs.s index a4c1fc191..6b11eded9 100644 --- a/sys/src/boot/pc/pbs.s +++ b/sys/src/boot/pc/pbs.s @@ -4,7 +4,7 @@ #define RELOC 0x7c00 TEXT _magic(SB), $0 - BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */ + BYTE $0xEB; BYTE $0x58; /* jmp .+ 0x58 (_start0x5A) */ BYTE $0x90 /* nop */ TEXT _version(SB), $0 BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; @@ -35,9 +35,26 @@ TEXT _nhiddenhi(SB), $0 BYTE $0x00; BYTE $0x00; TEXT _bigvolsize(SB), $0 BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; +/* FAT32 structure, starting @0x24 */ +TEXT _fatsz32(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _extflags(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _fsver(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _rootclust(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 +TEXT _fsinfo(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _bkboot(SB), $0 + BYTE $0x00; BYTE $0x00 +TEXT _reserved0(SB), $0 + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 TEXT _driveno(SB), $0 BYTE $0x00 -TEXT _reserved0(SB), $0 +TEXT _reserved1(SB), $0 BYTE $0x00 TEXT _bootsig(SB), $0 BYTE $0x00 @@ -49,9 +66,9 @@ TEXT _label(SB), $0 BYTE $0x00; BYTE $0x00; BYTE $0x00 TEXT _type(SB), $0 BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; - BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00; + BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00 -_start0x3E: +_start0x5A: CLI CLR(rAX) MTSR(rAX, rSS) /* 0000 -> rSS */ diff --git a/sys/src/cmd/disk/format.c b/sys/src/cmd/disk/format.c index 2ada3abfa..f74b76a21 100644 --- a/sys/src/cmd/disk/format.c +++ b/sys/src/cmd/disk/format.c @@ -442,9 +442,9 @@ dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int memmove(b->version, "Plan9.00", sizeof(b->version)); /* - * Add bootstrapping code; assume it starts - * at 0x3E (the destination of the jump we just - * wrote to b->magic). + * Add bootstrapping code; offset is + * determined from short jump (0xEB 0x??) + * instruction. */ if(dopbs) { pbsbuf = malloc(secsize); @@ -466,11 +466,15 @@ dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int memmove(pbsbuf, bootprog, sizeof(bootprog)); npbs = nbootprog; } - if(npbs <= 0x3E) + n = buf[1] + 2; + if(npbs <= 0x3 || npbs < n) fprint(2, "warning: pbs too small\n"); - else - memmove(buf+0x3E, pbsbuf+0x3E, npbs-0x3E); - + else if(buf[0] != 0xEB) + fprint(2, "warning: pbs doesn't start with short jump\n"); + else{ + memmove(buf, pbsbuf, 3); + memmove(buf+n, pbsbuf+n, npbs-n); + } free(pbsbuf); } @@ -558,6 +562,11 @@ if(chatty) print("driveno = %ux\n", b->driveno); b->bootsig = 0x29; x = disk->offset + b->nfats*fatsecs + nresrv; PUTLONG(b->volid, x); + /* + * FAT32 9boot PBS requires volid at this + * offset even for FAT16/FAT12 partitions. + */ + PUTLONG(b->volid+28, x); if(chatty) print("volid = %lux %lux\n", x, GETLONG(b->volid)); memmove(b->label, label, sizeof(b->label)); sprint(r, "FAT%d ", fatbits); diff --git a/sys/src/cmd/unix/mbrfix.c b/sys/src/cmd/unix/mbrfix.c new file mode 100644 index 000000000..2b32de5cb --- /dev/null +++ b/sys/src/cmd/unix/mbrfix.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long long vlong; + + +enum { + Sectsz = 0x200, + Psectsz = 11, + Pclustsc = 13, + Presvd = 14, + Pnumfat = 16, + Pfatsz = 22, + Pfatsz32 = 36, + Pvolid = 67, +}; + +int +readn(int f, void *av, int n) +{ + char *a; + int m, t; + a = av; + t = 0; + while(t < n){ + m = read(f, a+t, n-t); + if(m <= 0){ + if(t == 0) + return m; + break; + } + t += m; + } + return t; +} + +void +sysfatal(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + fprintf(stderr, "\n"); + exit(1); +} + +void +readsect(int fd, uint n, void *data) +{ + loff_t off; + + off = (loff_t) n * Sectsz; + if(llseek(fd, off, SEEK_SET) != off) + sysfatal("seek to sector 0x%x failed", n); + if(readn(fd, data, Sectsz) != Sectsz) + sysfatal("short read: %m"); +} + +void +writesect(int fd, uint n, void *data) +{ + loff_t off; + + off = (loff_t) n * Sectsz; + if(llseek(fd, off, SEEK_SET) != off) + sysfatal("seek to sector 0x%x failed", n); + if(write(fd, data, Sectsz) != Sectsz) + sysfatal("short write: %m"); +} + +uint +getulong(uchar *s) +{ + return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24); +} + +void +putulong(uchar *s, uint n) +{ + *s++ = n & 0xff; + *s++ = (n >> 8) & 0xff; + *s++ = (n >> 16) & 0xff; + *s++ = (n >> 24) & 0xff; +} + + +uint +getushort(uchar *s) +{ + return s[0] | (s[1] << 8); +} + +int +checksig(uchar *s) +{ + return s[0x1fe] == 0x55 && s[0x1ff] == 0xaa; +} + +void +fixpbs(uchar *pbs, uchar *pbs9, uint lba) +{ + uint a; + uint fatsz, resvd, numfat; + + numfat = pbs[Pnumfat]; + fatsz = getushort(&pbs[Pfatsz]); + if(fatsz == 0) + fatsz = getulong(&pbs[Pfatsz32]); + resvd = getushort(&pbs[Presvd]); + + a = pbs9[1] + 2; + memcpy(pbs, pbs9, 3); + memcpy(pbs+a, pbs9+a, Sectsz-a-2); + a = lba + numfat * fatsz + resvd; + printf("Xroot=%x\n", a); + putulong(&pbs[Pvolid], a); +} + + +int +main(int argc, char *argv[]) +{ + int dev, fd, i; + uchar mbr9[Sectsz], pbs9[Sectsz]; + uchar mbr[Sectsz], pbs[Sectsz]; + uint lba; + int part, want; + char *mbrfn, *pbsfn, *devfn; + + if(argc < 4) + sysfatal("usage: [part]"); + devfn = argv[1]; + mbrfn = argv[2]; + pbsfn = argv[3]; + want = argc >= 5 ? atoi(argv[4]) : -1; + part = -1; + + dev = open(devfn, O_RDWR); + if(dev < 0) + sysfatal("%s: %m", devfn); + + if((fd = open(mbrfn, O_RDONLY)) < 0) + sysfatal("%s: %m", mbrfn); + if(readn(fd, mbr9, Sectsz) < 3) + sysfatal("%s: too short", mbrfn); + close(fd); + + fd = open(pbsfn, O_RDONLY); + if(fd < 0) + sysfatal("%s: %m", pbsfn); + if(readn(fd, pbs9, Sectsz) < 3) + sysfatal("%s: too short", pbsfn); + if(pbs9[0] != 0xeb) + sysfatal("first byte of pbs not a short jump"); + close(fd); + + readsect(dev, 0, mbr); + if(!checksig(mbr)) + sysfatal("sector 0 is missing signature"); + for(i=0; i<4; i++){ + if(mbr[0x1be + i*16] == 0x80 && (part == -1 || i == want)) + part = i; + } + if(part == -1) + sysfatal("no bootable partitions found"); + if(want != -1 && part != want) + sysfatal("partition %d is not bootable", want); + + lba = getulong(&mbr[0x1be + part*16 + 8]); + if(lba == 0) + sysfatal("partition %d has zero LBA", part); + + readsect(dev, lba, pbs); + if(!checksig(pbs)) + sysfatal("partition %d (LBA=0x%x) is missing signaure", part, lba); + if(getushort(&pbs[Psectsz]) != 512) + sysfatal("sector size not 512"); + + printf("using partition %d, LBA=0x%x\n", part, lba); + memcpy(mbr, mbr9, 446); + fixpbs(pbs, pbs9, lba); + + writesect(dev, 0, mbr); + writesect(dev, lba, pbs); + + close(dev); + return 0; +}