Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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*
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
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
295
isdos(int t)
296
{
297
	return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
298
}
299
 
300
static int
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
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
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
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
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
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
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
}