xref: /inferno-os/os/boot/pc/part.c (revision 8a8c2d742b51525f66c2210e3c8a251de10022ff)
1 #include	"u.h"
2 #include	"lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 
7 #include	"sd.h"
8 #include	"fs.h"
9 
10 enum {
11 	Npart = 32
12 };
13 
14 uchar *mbrbuf, *partbuf;
15 int nbuf;
16 #define trace 0
17 
18 int
tsdbio(SDunit * unit,SDpart * part,void * a,vlong off,int mbr)19 tsdbio(SDunit *unit, SDpart *part, void *a, vlong off, int mbr)
20 {
21 	uchar *b;
22 
23 	if(sdbio(unit, part, a, unit->secsize, off) != unit->secsize){
24 		if(trace)
25 			print("%s: read %lud at %lld failed\n", unit->name,
26 				unit->secsize, (vlong)part->start*unit->secsize+off);
27 		return -1;
28 	}
29 	b = a;
30 	if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
31 		if(trace)
32 			print("%s: bad magic %.2ux %.2ux at %lld\n",
33 				unit->name, b[0x1FE], b[0x1FF],
34 				(vlong)part->start*unit->secsize+off);
35 		return -1;
36 	}
37 	return 0;
38 }
39 
40 /*
41  *  read partition table.  The partition table is just ascii strings.
42  */
43 #define MAGIC "plan9 partitions"
44 static void
oldp9part(SDunit * unit)45 oldp9part(SDunit *unit)
46 {
47 	SDpart *pp;
48 	char *field[3], *line[Npart+1];
49 	ulong n, start, end;
50 	int i;
51 
52 	/*
53 	 *  We have some partitions already.
54 	 */
55 	pp = &unit->part[unit->npart];
56 
57 	/*
58 	 * We prefer partition tables on the second to last sector,
59 	 * but some old disks use the last sector instead.
60 	 */
61 	strcpy(pp->name, "partition");
62 	pp->start = unit->sectors - 2;
63 	pp->end = unit->sectors - 1;
64 
65 	if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
66 		return;
67 
68 	if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
69 		/* not found on 2nd last sector; look on last sector */
70 		pp->start++;
71 		pp->end++;
72 		if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
73 			return;
74 		if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
75 			return;
76 		print("%s: using old plan9 partition table on last sector\n", unit->name);
77 	}else
78 		print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
79 
80 	/* we found a partition table, so add a partition partition */
81 	unit->npart++;
82 	partbuf[unit->secsize-1] = '\0';
83 
84 	/*
85 	 * parse partition table
86 	 */
87 	n = getfields((char*)partbuf, line, Npart+1, '\n');
88 	if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
89 		for(i = 1; i < n && unit->npart < SDnpart; i++){
90 			if(getfields(line[i], field, 3, ' ') != 3)
91 				break;
92 			start = strtoull(field[1], 0, 0);
93 			end = strtoull(field[2], 0, 0);
94 			if(start >= end || end > unit->sectors)
95 				break;
96 			sdaddpart(unit, field[0], start, end);
97 		}
98 	}
99 }
100 
101 static void
p9part(SDunit * unit,char * name)102 p9part(SDunit *unit, char *name)
103 {
104 	SDpart *p;
105 	char *field[4], *line[Npart+1];
106 	uvlong start, end;
107 	int i, n;
108 
109 	p = sdfindpart(unit, name);
110 	if(p == nil)
111 		return;
112 
113 	if(tsdbio(unit, p, partbuf, unit->secsize, 0) < 0)
114 		return;
115 	partbuf[unit->secsize-1] = '\0';
116 
117 	if(strncmp((char*)partbuf, "part ", 5) != 0)
118 		return;
119 
120 	n = getfields((char*)partbuf, line, Npart+1, '\n');
121 	if(n == 0)
122 		return;
123 	for(i = 0; i < n && unit->npart < SDnpart; i++){
124 		if(strncmp(line[i], "part ", 5) != 0)
125 			break;
126 		if(getfields(line[i], field, 4, ' ') != 4)
127 			break;
128 		start = strtoull(field[2], 0, 0);
129 		end = strtoull(field[3], 0, 0);
130 		if(start >= end || end > unit->sectors)
131 			break;
132 		sdaddpart(unit, field[1], p->start+start, p->start+end);
133 	}
134 }
135 
136 int
isdos(int t)137 isdos(int t)
138 {
139 	return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
140 }
141 
142 int
isextend(int t)143 isextend(int t)
144 {
145 	return t==EXTEND || t==EXTHUGE || t==LEXTEND;
146 }
147 
148 /*
149  * Fetch the first dos and all plan9 partitions out of the MBR partition table.
150  * We return -1 if we did not find a plan9 partition.
151  */
152 static int
mbrpart(SDunit * unit)153 mbrpart(SDunit *unit)
154 {
155 	Dospart *dp;
156 	ulong taboffset, start, end;
157 	ulong firstxpart, nxtxpart;
158 	int havedos, i, nplan9;
159 	char name[10];
160 
161 	taboffset = 0;
162 	dp = (Dospart*)&mbrbuf[0x1BE];
163 	if(1) {
164 		/* get the MBR (allowing for DMDDO) */
165 		if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
166 			return -1;
167 		for(i=0; i<4; i++)
168 			if(dp[i].type == DMDDO) {
169 				if(trace)
170 					print("DMDDO partition found\n");
171 				taboffset = 63;
172 				if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
173 					return -1;
174 				i = -1;	/* start over */
175 			}
176 	}
177 
178 	/*
179 	 * Read the partitions, first from the MBR and then
180 	 * from successive extended partition tables.
181 	 */
182 	nplan9 = 0;
183 	havedos = 0;
184 	firstxpart = 0;
185 	for(;;) {
186 		if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
187 			return -1;
188 		if(trace) {
189 			if(firstxpart)
190 				print("%s ext %lud ", unit->name, taboffset);
191 			else
192 				print("%s mbr ", unit->name);
193 		}
194 		nxtxpart = 0;
195 		for(i=0; i<4; i++) {
196 			if(trace)
197 				print("dp %d...", dp[i].type);
198 			start = taboffset+GLONG(dp[i].start);
199 			end = start+GLONG(dp[i].len);
200 
201 			if(dp[i].type == PLAN9) {
202 				if(nplan9 == 0)
203 					strcpy(name, "plan9");
204 				else
205 					sprint(name, "plan9.%d", nplan9);
206 				sdaddpart(unit, name, start, end);
207 				p9part(unit, name);
208 				nplan9++;
209 			}
210 
211 			/*
212 			 * We used to take the active partition (and then the first
213 			 * when none are active).  We have to take the first here,
214 			 * so that the partition we call ``dos'' agrees with the
215 			 * partition disk/fdisk calls ``dos''.
216 			 */
217 			if(havedos==0 && isdos(dp[i].type)){
218 				havedos = 1;
219 				sdaddpart(unit, "dos", start, end);
220 			}
221 
222 			/* nxtxpart is relative to firstxpart (or 0), not taboffset */
223 			if(isextend(dp[i].type)){
224 				nxtxpart = start-taboffset+firstxpart;
225 				if(trace)
226 					print("link %lud...", nxtxpart);
227 			}
228 		}
229 		if(trace)
230 			print("\n");
231 
232 		if(!nxtxpart)
233 			break;
234 		if(!firstxpart)
235 			firstxpart = nxtxpart;
236 		taboffset = nxtxpart;
237 	}
238 	return nplan9 ? 0 : -1;
239 }
240 
241 /*
242  * To facilitate booting from CDs, we create a partition for
243  * the boot floppy image embedded in a bootable CD.
244  */
245 static int
part9660(SDunit * unit)246 part9660(SDunit *unit)
247 {
248 	uchar buf[2048];
249 	ulong a, n;
250 	uchar *p;
251 
252 	if(unit->secsize != 2048)
253 		return -1;
254 
255 	if(sdbio(unit, &unit->part[0], buf, 2048, 17*2048) < 0)
256 		return -1;
257 
258 	if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0)
259 		return -1;
260 
261 
262 	p = buf+0x47;
263 	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
264 
265 	if(sdbio(unit, &unit->part[0], buf, 2048, a*2048) < 0)
266 		return -1;
267 
268 	if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0
269 	|| memcmp(buf+30, "\x55\xAA", 2) != 0
270 	|| buf[0x20] != 0x88)
271 		return -1;
272 
273 	p = buf+0x28;
274 	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
275 
276 	switch(buf[0x21]){
277 	case 0x01:
278 		n = 1200*1024;
279 		break;
280 	case 0x02:
281 		n = 1440*1024;
282 		break;
283 	case 0x03:
284 		n = 2880*1024;
285 		break;
286 	default:
287 		return -1;
288 	}
289 	n /= 2048;
290 
291 	print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n);
292 	sdaddpart(unit, "cdboot", a, a+n);
293 	return 0;
294 }
295 
296 enum {
297 	NEW = 1<<0,
298 	OLD = 1<<1
299 };
300 
301 void
partition(SDunit * unit)302 partition(SDunit *unit)
303 {
304 	int type;
305 	char *p;
306 
307 	if(unit->part == 0)
308 		return;
309 
310 	if(part9660(unit) == 0)
311 		return;
312 
313 	p = getconf("partition");
314 	if(p == nil)
315 		p = defaultpartition;
316 
317 	if(p != nil && strncmp(p, "new", 3) == 0)
318 		type = NEW;
319 	else if(p != nil && strncmp(p, "old", 3) == 0)
320 		type = OLD;
321 	else
322 		type = NEW|OLD;
323 
324 	if(nbuf < unit->secsize) {
325 		free(mbrbuf);
326 		free(partbuf);
327 		mbrbuf = malloc(unit->secsize);
328 		partbuf = malloc(unit->secsize);
329 		if(mbrbuf==nil || partbuf==nil) {
330 			free(mbrbuf);
331 			free(partbuf);
332 			partbuf = mbrbuf = nil;
333 			nbuf = 0;
334 			return;
335 		}
336 		nbuf = unit->secsize;
337 	}
338 
339 	if((type & NEW) && mbrpart(unit) >= 0){
340 		/* nothing to do */;
341 	}
342 	else if(type & OLD)
343 		oldp9part(unit);
344 }
345