git: 9front

Download patch

ref: a1ec2a718e05beae06a888a5db66bbc9b6cd19ca
parent: 155fd0aca565cea7e1c42d40830247352d6daeff
parent: be261bbe429ddb14ddf2403a294449fd54add0a8
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Thu Apr 28 18:39:10 EDT 2011

merge

--- 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 @@
 	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 @@
 {
 }
 
+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,44 +164,23 @@
 }
 
 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
 dirname(Dir *d, char buf[Maxpath])
 {
@@ -155,18 +211,27 @@
 	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,13 +243,13 @@
 			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;
 			}
@@ -191,13 +256,69 @@
 			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;
 		uchar bchs[3];
@@ -206,21 +327,23 @@
 		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;
 	}
 	return -1;
@@ -231,12 +354,14 @@
 {
 	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();
--- 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 @@
 	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 @@
 	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 */
--- a/sys/src/cmd/disk/format.c
+++ b/sys/src/cmd/disk/format.c
@@ -442,9 +442,9 @@
 	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 @@
 			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 @@
 		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);
--- /dev/null
+++ b/sys/src/cmd/unix/mbrfix.c
@@ -1,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;
+}
--