xref: /plan9/sys/src/cmd/disk/mbr.c (revision 71369a22e879a4cda58ef8d58775fc458553580b)
1 /*
2  * install new master boot record boot code on PC disk.
3  */
4 
5 #include <u.h>
6 #include <libc.h>
7 #include <disk.h>
8 
9 typedef struct {
10 	uchar	active;			/* active flag */
11 	uchar	starth;			/* starting head */
12 	uchar	starts;			/* starting sector */
13 	uchar	startc;			/* starting cylinder */
14 	uchar	type;			/* partition type */
15 	uchar	endh;			/* ending head */
16 	uchar	ends;			/* ending sector */
17 	uchar	endc;			/* ending cylinder */
18 	uchar	lba[4];			/* starting LBA */
19 	uchar	size[4];		/* size in sectors */
20 } Tentry;
21 
22 enum {
23 	Toffset = 0x1BE,		/* offset of partition table */
24 
25 	Type9	= 0x39,
26 };
27 
28 /*
29  * Default boot block prints an error message and reboots.
30  */
31 static int ndefmbr = Toffset;
32 static char defmbr[512] = {
33 [0x000]	0xEB, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 [0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0,
36 	0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19,
37 	0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00,
38 	0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12,
39 	0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF,
40 	0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4,
41 	0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2,
42 	0xC3,  'N',  'o',  't',  ' ',  'a',  ' ',  'b',
43 	 'o',  'o',  't',  'a',  'b',  'l',  'e',  ' ',
44 	 'd',  'i',  's',  'c',  ' ',  'o',  'r',  ' ',
45 	 'd',  'i',  's',  'c',  ' ',  'e',  'r',  'r',
46 	 'o',  'r', '\r', '\n',  'P',  'r',  'e',  's',
47 	 's',  ' ',  'a',  'l',  'm',  'o',  's',  't',
48 	 ' ',  'a',  'n',  'y',  ' ',  'k',  'e',  'y',
49 	 ' ',  't',  'o',  ' ',  'r',  'e',  'b',  'o',
50 	 'o',  't',  '.',  '.',  '.', 0x00, 0x00, 0x00,
51 };
52 
53 void
usage(void)54 usage(void)
55 {
56 	fprint(2, "usage: disk/mbr [-m mbrfile] disk\n");
57 	exits("usage");
58 }
59 
60 void
fatal(char * fmt,...)61 fatal(char *fmt, ...)
62 {
63 	char err[ERRMAX];
64 	va_list arg;
65 
66 	va_start(arg, fmt);
67 	vsnprint(err, ERRMAX, fmt, arg);
68 	va_end(arg);
69 	fprint(2, "mbr: %s\n", err);
70 	exits(err);
71 }
72 
73 static void
putle32(void * v,u32int i)74 putle32(void* v, u32int i)
75 {
76 	uchar *p;
77 
78 	p = v;
79 	p[0] = i;
80 	p[1] = i>>8;
81 	p[2] = i>>16;
82 	p[3] = i>>24;
83 }
84 
85 static void
writechs(Disk * disk,uchar * p,vlong lba)86 writechs(Disk *disk, uchar *p, vlong lba)
87 {
88 	int c, h, s;
89 
90 	s = lba % disk->s;
91 	h = (lba / disk->s) % disk->h;
92 	c = lba / (disk->s * disk->h);
93 
94 	if(c >= 1024) {
95 		c = 1023;
96 		h = disk->h - 1;
97 		s = disk->s - 1;
98 	}
99 
100 	p[0] = h;
101 	p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0);
102 	p[2] = c;
103 }
104 
105 static void
wrtentry(Disk * disk,Tentry * tp,int type,u32int base,u32int lba,u32int end)106 wrtentry(Disk *disk, Tentry *tp, int type, u32int base, u32int lba, u32int end)
107 {
108 	tp->active = 0x80;		/* make this sole partition active */
109 	tp->type = type;
110 	writechs(disk, &tp->starth, lba);
111 	writechs(disk, &tp->endh, end-1);
112 	putle32(tp->lba, lba-base);
113 	putle32(tp->size, end-lba);
114 }
115 
116 void
main(int argc,char ** argv)117 main(int argc, char **argv)
118 {
119 	Disk *disk;
120 	Tentry *tp;
121 	uchar *mbr, *buf;
122 	char *mbrfile;
123 	ulong secsize;
124 	int flag9, sysfd, nmbr;
125 
126 	flag9 = 0;
127 	mbrfile = nil;
128 	ARGBEGIN {
129 	case '9':
130 		flag9 = 1;
131 		break;
132 	case 'm':
133 		mbrfile = EARGF(usage());
134 		break;
135 	default:
136 		usage();
137 	} ARGEND
138 
139 	if(argc < 1)
140 		usage();
141 
142 	disk = opendisk(argv[0], 0, 0);
143 	if(disk == nil)
144 		fatal("opendisk %s: %r", argv[0]);
145 
146 	if(disk->type == Tfloppy)
147 		fatal("will not install mbr on floppy");
148 	/*
149 	 * we need to cope with 4k-byte sectors on some newer disks.
150 	 * we're only interested in 512 bytes of mbr, so
151 	 * on 4k disks, rely on /dev/sd to read-modify-write.
152 	 */
153 	secsize = 512;
154 	if(disk->secsize != secsize)
155 		fprint(2, "%s: sector size %lld not %ld, should be okay\n",
156 			argv0, disk->secsize, secsize);
157 
158 	buf = malloc(secsize*(disk->s+1));
159 	mbr = malloc(secsize*disk->s);
160 	if(buf == nil || mbr == nil)
161 		fatal("out of memory");
162 
163 	/*
164 	 * Start with initial sector from disk.
165 	 */
166 	if(seek(disk->fd, 0, 0) < 0)
167 		fatal("seek to boot sector: %r\n");
168 	if(read(disk->fd, mbr, secsize) != secsize)
169 		fatal("reading boot sector: %r");
170 
171 	if(mbrfile == nil){
172 		nmbr = ndefmbr;
173 		memmove(mbr, defmbr, nmbr);
174 	} else {
175 		memset(buf, 0, secsize*disk->s);
176 		if((sysfd = open(mbrfile, OREAD)) < 0)
177 			fatal("open %s: %r", mbrfile);
178 		if((nmbr = read(sysfd, buf, secsize*(disk->s+1))) < 0)
179 			fatal("read %s: %r", mbrfile);
180 		if(nmbr > secsize*disk->s)
181 			fatal("master boot record too large %d > %d", nmbr, secsize*disk->s);
182 		if(nmbr < secsize)
183 			nmbr = secsize;
184 		close(sysfd);
185 		memmove(buf+Toffset, mbr+Toffset, secsize-Toffset);
186 		memmove(mbr, buf, nmbr);
187 	}
188 
189 	if(flag9){
190 		tp = (Tentry*)(mbr+Toffset);
191 		memset(tp, 0, secsize-Toffset);
192 		wrtentry(disk, tp, Type9, 0, disk->s, disk->secs);
193 	}
194 	mbr[secsize-2] = 0x55;
195 	mbr[secsize-1] = 0xAA;
196 	nmbr = (nmbr+secsize-1)&~(secsize-1);
197 	if(seek(disk->wfd, 0, 0) < 0)
198 		fatal("seek to MBR sector: %r\n");
199 	if(write(disk->wfd, mbr, nmbr) != nmbr)
200 		fatal("writing MBR: %r");
201 
202 	exits(0);
203 }
204