1 /*
2 * read disk partition tables, intended for early use on systems
3 * that don't use 9load. borrowed from 9load.
4 */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <auth.h>
9 #include <fcall.h>
10 #include <bio.h>
11 #include "../boot/boot.h"
12
13 typedef struct Fs Fs;
14 #include "/sys/src/boot/pc/dosfs.h"
15
16 #define GSHORT(p) (((p)[1]<<8)|(p)[0])
17 #define GLONG(p) ((GSHORT((p)+2)<<16)|GSHORT(p))
18
19 #define trace 0
20
21 enum {
22 parttrace = 0,
23
24 Npart = 64,
25 SDnpart = Npart,
26
27 Maxsec = 2048,
28 Cdsec = 2048,
29 Normsec = 512, /* disks */
30
31 NAMELEN = 256, /* hack */
32 };
33
34 typedef struct SDpart SDpart;
35 typedef struct SDunit SDunit;
36
37 typedef struct SDpart {
38 uvlong start;
39 uvlong end;
40 char name[NAMELEN];
41 int valid;
42 } SDpart;
43
44 typedef struct SDunit {
45 int ctl; /* fds */
46 int data;
47
48 char name[NAMELEN];
49
50 uvlong sectors;
51 ulong secsize;
52 SDpart* part;
53 int npart; /* of valid partitions */
54 } SDunit;
55
56 static uchar *mbrbuf, *partbuf;
57
58 static void
sdaddpart(SDunit * unit,char * name,uvlong start,uvlong end)59 sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
60 {
61 SDpart *pp;
62 int i, partno;
63
64 if(parttrace)
65 print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end);
66 /*
67 * Check name not already used
68 * and look for a free slot.
69 */
70 if(unit->part != nil){
71 partno = -1;
72 for(i = 0; i < SDnpart; i++){
73 pp = &unit->part[i];
74 if(!pp->valid){
75 if(partno == -1)
76 partno = i;
77 break;
78 }
79 if(strcmp(name, pp->name) == 0){
80 if(pp->start == start && pp->end == end){
81 if(parttrace)
82 print("already present\n");
83 return;
84 }
85 }
86 }
87 }else{
88 if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
89 if(parttrace)
90 print("malloc failed\n");
91 return;
92 }
93 partno = 0;
94 }
95
96 /*
97 * Check there is a free slot and size and extent are valid.
98 */
99 if(partno == -1 || start > end || end > unit->sectors){
100 print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n",
101 unit->name, name, start, end, unit->sectors,
102 partno==-1 ? "no free partitions" : "partition boundaries out of range");
103 return;
104 }
105 pp = &unit->part[partno];
106 pp->start = start;
107 pp->end = end;
108 strncpy(pp->name, name, NAMELEN);
109 pp->valid = 1;
110 unit->npart++;
111
112 /* update devsd's in-memory partition table */
113 if (fprint(unit->ctl, "part %s %lld %lld\n", name, start, end) < 0)
114 fprint(2, "can't update %s's devsd partition table for %s: %r\n",
115 unit->name, name);
116 dprint("part %s %lld %lld\n", name, start, end);
117 }
118
119 static long
sdread(SDunit * unit,SDpart * pp,void * va,long len,vlong off)120 sdread(SDunit *unit, SDpart *pp, void* va, long len, vlong off)
121 {
122 long l, secsize;
123 uvlong bno, nb;
124
125 /*
126 * Check the request is within partition bounds.
127 */
128 secsize = unit->secsize;
129 if (secsize == 0)
130 sysfatal("sdread: zero sector size");
131 bno = off/secsize + pp->start;
132 nb = (off+len+secsize-1)/secsize + pp->start - bno;
133 if(bno+nb > pp->end)
134 nb = pp->end - bno;
135 if(bno >= pp->end || nb == 0)
136 return 0;
137
138 seek(unit->data, bno * secsize, 0);
139 assert(va); /* "sdread" */
140 l = read(unit->data, va, len);
141 if (l < 0)
142 return 0;
143 return l;
144 }
145
146 static int
sdreadblk(SDunit * unit,SDpart * part,void * a,vlong off,int mbr)147 sdreadblk(SDunit *unit, SDpart *part, void *a, vlong off, int mbr)
148 {
149 uchar *b;
150
151 assert(a); /* sdreadblk */
152 if(sdread(unit, part, a, unit->secsize, off) != unit->secsize){
153 if(trace)
154 print("%s: read %lud at %lld failed\n", unit->name,
155 unit->secsize, (vlong)part->start*unit->secsize+off);
156 return -1;
157 }
158 b = a;
159 if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
160 if(trace)
161 print("%s: bad magic %.2ux %.2ux at %lld\n",
162 unit->name, b[0x1FE], b[0x1FF],
163 (vlong)part->start*unit->secsize+off);
164 return -1;
165 }
166 return 0;
167 }
168
169 /*
170 * read partition table. The partition table is just ascii strings.
171 */
172 #define MAGIC "plan9 partitions"
173 static void
oldp9part(SDunit * unit)174 oldp9part(SDunit *unit)
175 {
176 SDpart *pp;
177 char *field[3], *line[Npart+1];
178 ulong n;
179 uvlong start, end;
180 int i;
181
182 /*
183 * We have some partitions already.
184 */
185 pp = &unit->part[unit->npart];
186
187 /*
188 * We prefer partition tables on the second to last sector,
189 * but some old disks use the last sector instead.
190 */
191 strcpy(pp->name, "partition");
192 pp->start = unit->sectors - 2;
193 pp->end = unit->sectors - 1;
194
195 dprint("oldp9part %s\n", unit->name);
196 if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
197 return;
198
199 if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
200 /* not found on 2nd last sector; look on last sector */
201 pp->start++;
202 pp->end++;
203 if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
204 return;
205 if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
206 return;
207 print("%s: using old plan9 partition table on last sector\n", unit->name);
208 }else
209 print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
210
211 /* we found a partition table, so add a partition partition */
212 unit->npart++;
213 partbuf[unit->secsize-1] = '\0';
214
215 /*
216 * parse partition table
217 */
218 n = gettokens((char*)partbuf, line, Npart+1, "\n");
219 if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
220 for(i = 1; i < n && unit->npart < SDnpart; i++){
221 if(gettokens(line[i], field, 3, " ") != 3)
222 break;
223 start = strtoull(field[1], 0, 0);
224 end = strtoull(field[2], 0, 0);
225 if(start >= end || end > unit->sectors)
226 break;
227 sdaddpart(unit, field[0], start, end);
228 }
229 }
230 }
231
232 static SDpart*
sdfindpart(SDunit * unit,char * name)233 sdfindpart(SDunit *unit, char *name)
234 {
235 int i;
236
237 if(parttrace)
238 print("findpart %d %s %s: ", unit->npart, unit->name, name);
239 for(i=0; i<unit->npart; i++) {
240 if(parttrace)
241 print("%s...", unit->part[i].name);
242 if(strcmp(unit->part[i].name, name) == 0){
243 if(parttrace)
244 print("\n");
245 return &unit->part[i];
246 }
247 }
248 if(parttrace)
249 print("not found\n");
250 return nil;
251 }
252
253 /*
254 * look for a plan 9 partition table on drive `unit' in the second
255 * sector (sector 1) of partition `name'.
256 * if found, add the partitions defined in the table.
257 */
258 static void
p9part(SDunit * unit,char * name)259 p9part(SDunit *unit, char *name)
260 {
261 SDpart *p;
262 char *field[4], *line[Npart+1];
263 uvlong start, end;
264 int i, n;
265
266 dprint("p9part %s %s\n", unit->name, name);
267 p = sdfindpart(unit, name);
268 if(p == nil)
269 return;
270
271 if(sdreadblk(unit, p, partbuf, unit->secsize, 0) < 0)
272 return;
273 partbuf[unit->secsize-1] = '\0';
274
275 if(strncmp((char*)partbuf, "part ", 5) != 0)
276 return;
277
278 n = gettokens((char*)partbuf, line, Npart+1, "\n");
279 if(n == 0)
280 return;
281 for(i = 0; i < n && unit->npart < SDnpart; i++){
282 if(strncmp(line[i], "part ", 5) != 0)
283 break;
284 if(gettokens(line[i], field, 4, " ") != 4)
285 break;
286 start = strtoull(field[2], 0, 0);
287 end = strtoull(field[3], 0, 0);
288 if(start >= end || end > unit->sectors)
289 break;
290 sdaddpart(unit, field[1], p->start+start, p->start+end);
291 }
292 }
293
294 static int
isdos(int t)295 isdos(int t)
296 {
297 return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
298 }
299
300 static int
isextend(int t)301 isextend(int t)
302 {
303 return t==EXTEND || t==EXTHUGE || t==LEXTEND;
304 }
305
306 /*
307 * Fetch the first dos and all plan9 partitions out of the MBR partition table.
308 * We return -1 if we did not find a plan9 partition.
309 */
310 static int
mbrpart(SDunit * unit)311 mbrpart(SDunit *unit)
312 {
313 Dospart *dp;
314 uvlong taboffset, start, end;
315 uvlong firstxpart, nxtxpart;
316 int havedos, i, nplan9;
317 char name[10];
318
319 taboffset = 0;
320 dp = (Dospart*)&mbrbuf[0x1BE];
321 {
322 /* get the MBR (allowing for DMDDO) */
323 if(sdreadblk(unit, &unit->part[0], mbrbuf,
324 (vlong)taboffset * unit->secsize, 1) < 0)
325 return -1;
326 for(i=0; i<4; i++)
327 if(dp[i].type == DMDDO) {
328 if(trace)
329 print("DMDDO partition found\n");
330 taboffset = 63;
331 if(sdreadblk(unit, &unit->part[0], mbrbuf,
332 (vlong)taboffset * unit->secsize, 1) < 0)
333 return -1;
334 i = -1; /* start over */
335 }
336 }
337
338 /*
339 * Read the partitions, first from the MBR and then
340 * from successive extended partition tables.
341 */
342 nplan9 = 0;
343 havedos = 0;
344 firstxpart = 0;
345 for(;;) {
346 if(sdreadblk(unit, &unit->part[0], mbrbuf,
347 (vlong)taboffset * unit->secsize, 1) < 0)
348 return -1;
349 if(trace) {
350 if(firstxpart)
351 print("%s ext %llud ", unit->name, taboffset);
352 else
353 print("%s mbr ", unit->name);
354 }
355 nxtxpart = 0;
356 for(i=0; i<4; i++) {
357 if(trace)
358 print("dp %d...", dp[i].type);
359 start = taboffset+GLONG(dp[i].start);
360 end = start+GLONG(dp[i].len);
361
362 if(dp[i].type == PLAN9) {
363 if(nplan9 == 0)
364 strcpy(name, "plan9");
365 else
366 sprint(name, "plan9.%d", nplan9);
367 sdaddpart(unit, name, start, end);
368 p9part(unit, name);
369 nplan9++;
370 }
371
372 /*
373 * We used to take the active partition (and then the first
374 * when none are active). We have to take the first here,
375 * so that the partition we call ``dos'' agrees with the
376 * partition disk/fdisk calls ``dos''.
377 */
378 if(havedos==0 && isdos(dp[i].type)){
379 havedos = 1;
380 sdaddpart(unit, "dos", start, end);
381 }
382
383 /* nxtxpart is relative to firstxpart (or 0), not taboffset */
384 if(isextend(dp[i].type)){
385 nxtxpart = start-taboffset+firstxpart;
386 if(trace)
387 print("link %llud...", nxtxpart);
388 }
389 }
390 if(trace)
391 print("\n");
392
393 if(!nxtxpart)
394 break;
395 if(!firstxpart)
396 firstxpart = nxtxpart;
397 taboffset = nxtxpart;
398 }
399 return nplan9 ? 0 : -1;
400 }
401
402 /*
403 * To facilitate booting from CDs, we create a partition for
404 * the boot floppy image embedded in a bootable CD.
405 */
406 static int
part9660(SDunit * unit)407 part9660(SDunit *unit)
408 {
409 uchar buf[Maxsec];
410 ulong a, n;
411 uchar *p;
412
413 if(unit->secsize != Cdsec)
414 return -1;
415
416 if(sdread(unit, &unit->part[0], buf, Cdsec, 17*Cdsec) < 0)
417 return -1;
418
419 if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0)
420 return -1;
421
422
423 p = buf+0x47;
424 a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
425
426 if(sdread(unit, &unit->part[0], buf, Cdsec, a*Cdsec) < 0)
427 return -1;
428
429 if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0
430 || memcmp(buf+30, "\x55\xAA", 2) != 0
431 || buf[0x20] != 0x88)
432 return -1;
433
434 p = buf+0x28;
435 a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
436
437 switch(buf[0x21]){
438 case 0x01:
439 n = 1200*1024;
440 break;
441 case 0x02:
442 n = 1440*1024;
443 break;
444 case 0x03:
445 n = 2880*1024;
446 break;
447 default:
448 return -1;
449 }
450 n /= Cdsec;
451
452 print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n);
453 sdaddpart(unit, "cdboot", a, a+n);
454 return 0;
455 }
456
457 enum {
458 NEW = 1<<0,
459 OLD = 1<<1
460 };
461
462 /*
463 * read unit->data to look for partition tables.
464 * if found, stash partitions in environment and write them to ctl too.
465 */
466 static void
partition(SDunit * unit)467 partition(SDunit *unit)
468 {
469 int type;
470 char *p;
471
472 if(unit->part == 0)
473 return;
474
475 if(part9660(unit) == 0)
476 return;
477
478 p = getenv("partition");
479 if(p != nil && strncmp(p, "new", 3) == 0)
480 type = NEW;
481 else if(p != nil && strncmp(p, "old", 3) == 0)
482 type = OLD;
483 else
484 type = NEW|OLD;
485
486 if(mbrbuf == nil) {
487 mbrbuf = malloc(Maxsec);
488 partbuf = malloc(Maxsec);
489 if(mbrbuf==nil || partbuf==nil) {
490 free(mbrbuf);
491 free(partbuf);
492 partbuf = mbrbuf = nil;
493 return;
494 }
495 }
496
497 /*
498 * there might be no mbr (e.g. on a very large device), so look for
499 * a bare plan 9 partition table if mbrpart fails.
500 */
501 if((type & NEW) && mbrpart(unit) >= 0){
502 /* nothing to do */
503 }
504 else if (type & NEW)
505 p9part(unit, "data");
506 else if(type & OLD)
507 oldp9part(unit);
508 }
509
510 static void
rdgeom(SDunit * unit)511 rdgeom(SDunit *unit)
512 {
513 char *line;
514 char *flds[5];
515 Biobuf bb;
516 Biobuf *bp;
517 static char geom[] = "geometry ";
518
519 bp = &bb;
520 seek(unit->ctl, 0, 0);
521 Binit(bp, unit->ctl, OREAD);
522 while((line = Brdline(bp, '\n')) != nil){
523 line[Blinelen(bp) - 1] = '\0';
524 if (strncmp(line, geom, sizeof geom - 1) == 0)
525 break;
526 }
527 if (line != nil && tokenize(line, flds, nelem(flds)) >= 3) {
528 unit->sectors = atoll(flds[1]);
529 unit->secsize = atoll(flds[2]);
530 }
531 Bterm(bp);
532 seek(unit->ctl, 0, 0);
533 }
534
535 static void
setpartitions(char * name,int ctl,int data)536 setpartitions(char *name, int ctl, int data)
537 {
538 SDunit sdunit;
539 SDunit *unit;
540 SDpart *part0;
541
542 unit = &sdunit;
543 memset(unit, 0, sizeof *unit);
544 unit->ctl = ctl;
545 unit->data = data;
546
547 unit->secsize = Normsec; /* default: won't work for CDs */
548 unit->sectors = ~0ull;
549 rdgeom(unit);
550 strncpy(unit->name, name, sizeof unit->name);
551 unit->part = mallocz(sizeof(SDpart) * SDnpart, 1);
552
553 part0 = &unit->part[0];
554 part0->end = unit->sectors - 1;
555 strcpy(part0->name, "data");
556 part0->valid = 1;
557 unit->npart++;
558
559 mbrbuf = malloc(Maxsec);
560 partbuf = malloc(Maxsec);
561 partition(unit);
562 free(unit->part);
563 }
564
565 /*
566 * read disk partition tables so that readnvram via factotum
567 * can see them.
568 */
569 int
readparts(void)570 readparts(void)
571 {
572 int i, n, ctl, data, fd;
573 char *name, *ctlname, *dataname;
574 Dir *dir;
575
576 fd = open("/dev", OREAD);
577 if(fd < 0)
578 return -1;
579 n = dirreadall(fd, &dir);
580 close(fd);
581
582 for(i = 0; i < n; i++) {
583 name = dir[i].name;
584 if (strncmp(name, "sd", 2) != 0)
585 continue;
586
587 ctlname = smprint("/dev/%s/ctl", name);
588 dataname = smprint("/dev/%s/data", name);
589 if (ctlname == nil || dataname == nil) {
590 free(ctlname);
591 free(dataname);
592 continue;
593 }
594
595 ctl = open(ctlname, ORDWR);
596 data = open(dataname, OREAD);
597 free(ctlname);
598 free(dataname);
599
600 if (ctl >= 0 && data >= 0)
601 setpartitions(dataname, ctl, data);
602 close(ctl);
603 close(data);
604 }
605 free(dir);
606 return 0;
607 }
608