xref: /plan9/sys/src/cmd/disk/mbr.c (revision 71369a22e879a4cda58ef8d58775fc458553580b)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * install new master boot record boot code on PC disk.
37dd7cddfSDavid du Colombier  */
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier #include <u.h>
67dd7cddfSDavid du Colombier #include <libc.h>
77dd7cddfSDavid du Colombier #include <disk.h>
87dd7cddfSDavid du Colombier 
939734e7eSDavid du Colombier typedef struct {
1039734e7eSDavid du Colombier 	uchar	active;			/* active flag */
1139734e7eSDavid du Colombier 	uchar	starth;			/* starting head */
1239734e7eSDavid du Colombier 	uchar	starts;			/* starting sector */
1339734e7eSDavid du Colombier 	uchar	startc;			/* starting cylinder */
1439734e7eSDavid du Colombier 	uchar	type;			/* partition type */
1539734e7eSDavid du Colombier 	uchar	endh;			/* ending head */
1639734e7eSDavid du Colombier 	uchar	ends;			/* ending sector */
1739734e7eSDavid du Colombier 	uchar	endc;			/* ending cylinder */
1839734e7eSDavid du Colombier 	uchar	lba[4];			/* starting LBA */
1939734e7eSDavid du Colombier 	uchar	size[4];		/* size in sectors */
2039734e7eSDavid du Colombier } Tentry;
2139734e7eSDavid du Colombier 
227dd7cddfSDavid du Colombier enum {
2339734e7eSDavid du Colombier 	Toffset = 0x1BE,		/* offset of partition table */
2439734e7eSDavid du Colombier 
2539734e7eSDavid du Colombier 	Type9	= 0x39,
267dd7cddfSDavid du Colombier };
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier /*
297dd7cddfSDavid du Colombier  * Default boot block prints an error message and reboots.
307dd7cddfSDavid du Colombier  */
317dd7cddfSDavid du Colombier static int ndefmbr = Toffset;
327dd7cddfSDavid du Colombier static char defmbr[512] = {
337dd7cddfSDavid du Colombier [0x000]	0xEB, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347dd7cddfSDavid du Colombier 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357dd7cddfSDavid du Colombier [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
367dd7cddfSDavid du Colombier 	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
377dd7cddfSDavid du Colombier 	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
387dd7cddfSDavid du Colombier 	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
397dd7cddfSDavid du Colombier 	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
407dd7cddfSDavid du Colombier 	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
417dd7cddfSDavid du Colombier 	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
427dd7cddfSDavid du Colombier 	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
437dd7cddfSDavid du Colombier 	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
447dd7cddfSDavid du Colombier 	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
457dd7cddfSDavid du Colombier 	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
467dd7cddfSDavid du Colombier 	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
477dd7cddfSDavid du Colombier 	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
487dd7cddfSDavid du Colombier 	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
497dd7cddfSDavid du Colombier 	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
507dd7cddfSDavid du Colombier 	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
517dd7cddfSDavid du Colombier };
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier void
usage(void)547dd7cddfSDavid du Colombier usage(void)
557dd7cddfSDavid du Colombier {
567dd7cddfSDavid du Colombier 	fprint(2, "usage: disk/mbr [-m mbrfile] disk\n");
577dd7cddfSDavid du Colombier 	exits("usage");
587dd7cddfSDavid du Colombier }
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier void
fatal(char * fmt,...)617dd7cddfSDavid du Colombier fatal(char *fmt, ...)
627dd7cddfSDavid du Colombier {
639a747e4fSDavid du Colombier 	char err[ERRMAX];
647dd7cddfSDavid du Colombier 	va_list arg;
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier 	va_start(arg, fmt);
679a747e4fSDavid du Colombier 	vsnprint(err, ERRMAX, fmt, arg);
687dd7cddfSDavid du Colombier 	va_end(arg);
697dd7cddfSDavid du Colombier 	fprint(2, "mbr: %s\n", err);
707dd7cddfSDavid du Colombier 	exits(err);
717dd7cddfSDavid du Colombier }
727dd7cddfSDavid du Colombier 
7339734e7eSDavid du Colombier static void
putle32(void * v,u32int i)7439734e7eSDavid du Colombier putle32(void* v, u32int i)
7539734e7eSDavid du Colombier {
7639734e7eSDavid du Colombier 	uchar *p;
7739734e7eSDavid du Colombier 
7839734e7eSDavid du Colombier 	p = v;
7939734e7eSDavid du Colombier 	p[0] = i;
8039734e7eSDavid du Colombier 	p[1] = i>>8;
8139734e7eSDavid du Colombier 	p[2] = i>>16;
8239734e7eSDavid du Colombier 	p[3] = i>>24;
8339734e7eSDavid du Colombier }
8439734e7eSDavid du Colombier 
8539734e7eSDavid du Colombier static void
writechs(Disk * disk,uchar * p,vlong lba)8639734e7eSDavid du Colombier writechs(Disk *disk, uchar *p, vlong lba)
8739734e7eSDavid du Colombier {
8839734e7eSDavid du Colombier 	int c, h, s;
8939734e7eSDavid du Colombier 
9039734e7eSDavid du Colombier 	s = lba % disk->s;
9139734e7eSDavid du Colombier 	h = (lba / disk->s) % disk->h;
9239734e7eSDavid du Colombier 	c = lba / (disk->s * disk->h);
9339734e7eSDavid du Colombier 
9439734e7eSDavid du Colombier 	if(c >= 1024) {
9539734e7eSDavid du Colombier 		c = 1023;
9639734e7eSDavid du Colombier 		h = disk->h - 1;
9739734e7eSDavid du Colombier 		s = disk->s - 1;
9839734e7eSDavid du Colombier 	}
9939734e7eSDavid du Colombier 
10039734e7eSDavid du Colombier 	p[0] = h;
10139734e7eSDavid du Colombier 	p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0);
10239734e7eSDavid du Colombier 	p[2] = c;
10339734e7eSDavid du Colombier }
10439734e7eSDavid du Colombier 
10539734e7eSDavid du Colombier static void
wrtentry(Disk * disk,Tentry * tp,int type,u32int base,u32int lba,u32int end)10639734e7eSDavid du Colombier wrtentry(Disk *disk, Tentry *tp, int type, u32int base, u32int lba, u32int end)
10739734e7eSDavid du Colombier {
108390ad7e1SDavid du Colombier 	tp->active = 0x80;		/* make this sole partition active */
10939734e7eSDavid du Colombier 	tp->type = type;
11039734e7eSDavid du Colombier 	writechs(disk, &tp->starth, lba);
11139734e7eSDavid du Colombier 	writechs(disk, &tp->endh, end-1);
11239734e7eSDavid du Colombier 	putle32(tp->lba, lba-base);
11339734e7eSDavid du Colombier 	putle32(tp->size, end-lba);
11439734e7eSDavid du Colombier }
11539734e7eSDavid du Colombier 
1167dd7cddfSDavid du Colombier void
main(int argc,char ** argv)1177dd7cddfSDavid du Colombier main(int argc, char **argv)
1187dd7cddfSDavid du Colombier {
1197dd7cddfSDavid du Colombier 	Disk *disk;
12039734e7eSDavid du Colombier 	Tentry *tp;
1217dd7cddfSDavid du Colombier 	uchar *mbr, *buf;
1227dd7cddfSDavid du Colombier 	char *mbrfile;
1237dd7cddfSDavid du Colombier 	ulong secsize;
12439734e7eSDavid du Colombier 	int flag9, sysfd, nmbr;
1257dd7cddfSDavid du Colombier 
12639734e7eSDavid du Colombier 	flag9 = 0;
1277dd7cddfSDavid du Colombier 	mbrfile = nil;
1287dd7cddfSDavid du Colombier 	ARGBEGIN {
12939734e7eSDavid du Colombier 	case '9':
13039734e7eSDavid du Colombier 		flag9 = 1;
13139734e7eSDavid du Colombier 		break;
1327dd7cddfSDavid du Colombier 	case 'm':
133390ad7e1SDavid du Colombier 		mbrfile = EARGF(usage());
1347dd7cddfSDavid du Colombier 		break;
1357dd7cddfSDavid du Colombier 	default:
1367dd7cddfSDavid du Colombier 		usage();
1377dd7cddfSDavid du Colombier 	} ARGEND
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier 	if(argc < 1)
1407dd7cddfSDavid du Colombier 		usage();
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier 	disk = opendisk(argv[0], 0, 0);
1437dd7cddfSDavid du Colombier 	if(disk == nil)
1447dd7cddfSDavid du Colombier 		fatal("opendisk %s: %r", argv[0]);
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier 	if(disk->type == Tfloppy)
1477dd7cddfSDavid du Colombier 		fatal("will not install mbr on floppy");
148*71369a22SDavid du Colombier 	/*
149*71369a22SDavid du Colombier 	 * we need to cope with 4k-byte sectors on some newer disks.
150*71369a22SDavid du Colombier 	 * we're only interested in 512 bytes of mbr, so
151*71369a22SDavid du Colombier 	 * on 4k disks, rely on /dev/sd to read-modify-write.
152*71369a22SDavid du Colombier 	 */
153*71369a22SDavid du Colombier 	secsize = 512;
154*71369a22SDavid du Colombier 	if(disk->secsize != secsize)
155*71369a22SDavid du Colombier 		fprint(2, "%s: sector size %lld not %ld, should be okay\n",
156*71369a22SDavid du Colombier 			argv0, disk->secsize, secsize);
1577dd7cddfSDavid du Colombier 
1583ff48bf5SDavid du Colombier 	buf = malloc(secsize*(disk->s+1));
1593ff48bf5SDavid du Colombier 	mbr = malloc(secsize*disk->s);
1607dd7cddfSDavid du Colombier 	if(buf == nil || mbr == nil)
1617dd7cddfSDavid du Colombier 		fatal("out of memory");
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	/*
1647dd7cddfSDavid du Colombier 	 * Start with initial sector from disk.
1657dd7cddfSDavid du Colombier 	 */
1667dd7cddfSDavid du Colombier 	if(seek(disk->fd, 0, 0) < 0)
1677dd7cddfSDavid du Colombier 		fatal("seek to boot sector: %r\n");
1687dd7cddfSDavid du Colombier 	if(read(disk->fd, mbr, secsize) != secsize)
1697dd7cddfSDavid du Colombier 		fatal("reading boot sector: %r");
1707dd7cddfSDavid du Colombier 
1717dd7cddfSDavid du Colombier 	if(mbrfile == nil){
1727dd7cddfSDavid du Colombier 		nmbr = ndefmbr;
1737dd7cddfSDavid du Colombier 		memmove(mbr, defmbr, nmbr);
1747dd7cddfSDavid du Colombier 	} else {
1753ff48bf5SDavid du Colombier 		memset(buf, 0, secsize*disk->s);
1767dd7cddfSDavid du Colombier 		if((sysfd = open(mbrfile, OREAD)) < 0)
1777dd7cddfSDavid du Colombier 			fatal("open %s: %r", mbrfile);
1783ff48bf5SDavid du Colombier 		if((nmbr = read(sysfd, buf, secsize*(disk->s+1))) < 0)
1797dd7cddfSDavid du Colombier 			fatal("read %s: %r", mbrfile);
1803ff48bf5SDavid du Colombier 		if(nmbr > secsize*disk->s)
1813ff48bf5SDavid du Colombier 			fatal("master boot record too large %d > %d", nmbr, secsize*disk->s);
1823ff48bf5SDavid du Colombier 		if(nmbr < secsize)
1833ff48bf5SDavid du Colombier 			nmbr = secsize;
1847dd7cddfSDavid du Colombier 		close(sysfd);
1853ff48bf5SDavid du Colombier 		memmove(buf+Toffset, mbr+Toffset, secsize-Toffset);
1867dd7cddfSDavid du Colombier 		memmove(mbr, buf, nmbr);
1877dd7cddfSDavid du Colombier 	}
18839734e7eSDavid du Colombier 
18939734e7eSDavid du Colombier 	if(flag9){
19039734e7eSDavid du Colombier 		tp = (Tentry*)(mbr+Toffset);
19139734e7eSDavid du Colombier 		memset(tp, 0, secsize-Toffset);
19239734e7eSDavid du Colombier 		wrtentry(disk, tp, Type9, 0, disk->s, disk->secs);
19339734e7eSDavid du Colombier 	}
1947dd7cddfSDavid du Colombier 	mbr[secsize-2] = 0x55;
1957dd7cddfSDavid du Colombier 	mbr[secsize-1] = 0xAA;
1963ff48bf5SDavid du Colombier 	nmbr = (nmbr+secsize-1)&~(secsize-1);
1977dd7cddfSDavid du Colombier 	if(seek(disk->wfd, 0, 0) < 0)
1987dd7cddfSDavid du Colombier 		fatal("seek to MBR sector: %r\n");
1993ff48bf5SDavid du Colombier 	if(write(disk->wfd, mbr, nmbr) != nmbr)
2007dd7cddfSDavid du Colombier 		fatal("writing MBR: %r");
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier 	exits(0);
2037dd7cddfSDavid du Colombier }
204