summaryrefslogtreecommitdiff
path: root/sys/src
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@centraldogma>2011-04-28 22:39:10 +0000
committercinap_lenrek <cinap_lenrek@centraldogma>2011-04-28 22:39:10 +0000
commit4e9d8a7435e7c91618282b68b4247f6f9b5bd395 (patch)
tree273e5ff231538252c60835eb664762be49b66991 /sys/src
parent24f96acf8067ef332fde0ef3808c1de14836bffb (diff)
parent3544c39d3a383c07c4b8d452b6bb25ba639e042f (diff)
merge
Diffstat (limited to 'sys/src')
-rw-r--r--sys/src/boot/pc/fat.c235
-rw-r--r--sys/src/boot/pc/pbs.s25
-rw-r--r--sys/src/cmd/disk/format.c23
-rw-r--r--sys/src/cmd/unix/mbrfix.c196
4 files changed, 413 insertions, 66 deletions
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 <u.h>
#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 Extend
+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
+{
+ 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)
-{
- 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)
+void
+close(void *)
{
- 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+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: <device> <mbrfile> <pbsfile> [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;
+}