Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * 9load - load next kernel from disk and start it
3
 */
4
#include	"u.h"
5
#include	"../port/lib.h"
6
#include	"mem.h"
7
#include	"dat.h"
8
#include	"fns.h"
9
#include	"io.h"
10
#include	"ureg.h"
11
#include	"pool.h"
12
#include	"../port/error.h"
13
#include	"../port/netif.h"
14
#include	"dosfs.h"
15
#include	"../port/sd.h"
16
 
17
/* from <libc.h> */
18
#define	DIRMAX	(sizeof(Dir)+STATMAX)	/* max length of Dir structure */ 
19
#define	STATMAX	65535U	/* max length of machine-independent stat structure */
20
 
21
enum {
22
	Bufsize = 8192,
23
};
24
 
25
int	dosdirread(File *f, char ***nmarray);
26
int	isconf(char *name);
27
 
28
static int progress = 1;
29
static Bootfs fs;
30
 
31
/*
32
 * from 9load's bootp.c:
33
 */
34
 
35
static int
36
dumpfile(char *file)
37
{
38
	int n;
39
	char *buf;
40
 
41
	buf = smalloc(Maxfile + 1);
42
	n = readfile(file, buf, Maxfile);
43
	if (n < 0)
44
		return -1;
45
	buf[n] = 0;
46
	print("%s (%d bytes):\n", file, n);
47
	print("%s\n", buf);
48
	free(buf);
49
	return 0;
50
}
51
 
52
long
53
dirread0(Chan *c, uchar *p, long n)
54
{
55
	long nn, nnn;
56
	vlong off;
57
 
58
	/*
59
	 * The offset is passed through on directories, normally.
60
	 * Sysseek complains, but pread is used by servers like exportfs,
61
	 * that shouldn't need to worry about this issue.
62
	 *
63
	 * Notice that c->devoffset is the offset that c's dev is seeing.
64
	 * The number of bytes read on this fd (c->offset) may be different
65
	 * due to rewritings in rockfix.
66
	 */
67
	/* use and maintain channel's offset */
68
	off = c->offset;
69
	if(off < 0)
70
		error(Enegoff);
71
 
72
	if(off == 0){	/* rewind to the beginning of the directory */
73
		c->offset = 0;
74
		c->devoffset = 0;
75
		mountrewind(c);
76
		unionrewind(c);
77
	}
78
 
79
	if(c->qid.type & QTDIR){
80
		if(mountrockread(c, p, n, &nn)){
81
			/* do nothing: mountrockread filled buffer */
82
		}else if(c->umh)
83
			nn = unionread(c, p, n);
84
		else{
85
			if(off != c->offset)
86
				error(Edirseek);
87
			nn = devtab[c->type]->read(c, p, n, c->devoffset);
88
		}
89
		nnn = mountfix(c, p, nn, n);
90
	}else
91
		nnn = nn = devtab[c->type]->read(c, p, n, off);
92
 
93
	lock(c);
94
	c->devoffset += nn;
95
	c->offset += nnn;
96
	unlock(c);
97
 
98
	/* nnn == 54, sizeof(Dir) == 60 */
99
	return nnn;
100
}
101
 
102
long
103
dirread(Chan *c, Dir **d)
104
{
105
	uchar *buf;
106
	long ts;
107
 
108
	buf = malloc(DIRMAX);
109
	if(buf == nil)
110
		return -1;
111
	ts = dirread0(c, buf, DIRMAX);
112
	if(ts >= 0)
113
		/* convert machine-independent representation to Dirs */
114
		ts = dirpackage(buf, ts, d);
115
	free(buf);
116
	return ts;
117
}
118
 
119
static int
120
addsdev(Dir *dirp)
121
{
122
	int n, f, lines, flds;
123
	vlong start, end;
124
	char *buf, *part;
125
	char *line[64], *fld[5];
126
	char ctl[64], disk[64];
127
 
128
	buf = smalloc(Maxfile + 1);
129
	snprint(ctl, sizeof ctl, "#S/%s/ctl", dirp->name);
130
	n = readfile(ctl, buf, Maxfile);
131
	if (n < 0) {
132
		free(buf);
133
		return -1;
134
	}
135
	buf[n] = 0;
136
 
137
	lines = getfields(buf, line, nelem(line), 0, "\r\n");
138
	part = nil;
139
	for (f = 0; f < lines; f++) {
140
		flds = tokenize(line[f], fld, nelem(fld));
141
		if (flds < 4 || strcmp(fld[0], "part") != 0)
142
			continue;
143
		kstrdup(&part, fld[1]);
144
		start = strtoull(fld[2], nil, 0);
145
		end   = strtoull(fld[3], nil, 0);
146
		if (end > (vlong)100*(vlong)MB*MB) {
147
			print("addsdev: implausible partition #S/%s/%s %lld %lld\n",
148
				dirp->name, part, start, end);
149
			continue;
150
		}
151
		/*
152
		 * We are likely to only see a "data" partition on each disk.
153
		 *
154
		 * Read the on-disk partition tables & set in-core partitions
155
		 * (disk, part, start, end).
156
		 */
157
		print("found partition #S/%s/%s %,lld %,lld\n",
158
			dirp->name, part, start, end);
159
		snprint(disk, sizeof disk, "#S/%s", dirp->name);
160
		readparts(disk);
161
	}
162
	free(buf);
163
	return 0;
164
}
165
 
166
static File file;
167
 
168
/*
169
 * look for kernels on a 9fat; if there's just one, return it.
170
 * could treat x and x.gz as one kernel.
171
 */
172
static char *
173
findonekernel(Bootfs *fs)
174
{
175
	int n, kerns;
176
	char *bootfile, *name;
177
	char **array;
178
 
179
	if(fswalk(fs, "", &file) <= 0) {
180
		print("can't walk to ''\n");
181
		return nil;
182
	}
183
	dosdirread(&file, &array);
184
	bootfile = nil;
185
	kerns = 0;
186
	for (n = 0; (name = array[n]) != nil; n++)
187
		if(strncmp(name, "9pc", 3) == 0 ||
188
		   strncmp(name, "9k8", 3) == 0 ||
189
		   strncmp(name, "9k10", 4) == 0){
190
			bootfile = name;
191
			kerns++;
192
		}
193
	if (kerns > 1) {
194
		print("found these kernels:");
195
		for (n = 0; (name = array[n]) != nil; n++)
196
			print(" %s", name);
197
		print("\n");
198
	}
199
	return kerns == 1? bootfile: nil;
200
}
201
 
202
int
203
partboot(char *path)
204
{
205
	long n;
206
	char *buf;
207
	Boot boot;
208
	Boot *b;
209
	Chan *ch;
210
 
211
	b = &boot;
212
	memset(b, 0, sizeof *b);
213
	b->state = INITKERNEL;
214
	ch = namecopen(path, OREAD);
215
	if (ch == nil) {
216
		print("can't open partition %s\n", path);
217
		return -1;
218
	}
219
	print("loading %s\n", path);
220
	buf = smalloc(Bufsize);
221
	while((n = devtab[ch->type]->read(ch, buf, Bufsize, ch->offset)) > 0)
222
		if(bootpass(b, buf, n) != MORE)
223
			break;
224
	bootpass(b, nil, 0);		/* attempts to boot */
225
 
226
	free(buf);
227
	cclose(ch);
228
	return -1;
229
}
230
 
231
/* fsroot must be nil or a fat root directory already dosinit'ed */
232
static void
233
trybootfile(char *bootfile, Bootfs *fsroot)
234
{
235
	int nf;
236
	char fat[64];
237
	char *disk, *part, *file, *bootcopy;
238
	char *fields[4];
239
	Boot boot;
240
	static int didaddconf;
241
 
242
	bootcopy = file = nil;
243
	kstrdup(&bootcopy, bootfile);
244
	nf = getfields(bootcopy, fields, nelem(fields), 0, "!");
245
	switch(nf){
246
	case 3:
247
		file = fields[2];
248
		/* fall through */
249
	case 2:
250
		disk = fields[0];
251
		part = fields[1];
252
		break;
253
	default:
254
		print("bad bootfile syntax: %s\n", bootfile);
255
		return;
256
	}
257
 
258
	if(didaddconf == 0) {
259
		didaddconf = 1;
260
		sdaddallconfs(sdaddconf);
261
	}
262
 
263
	snprint(fat, sizeof fat, "#S/%s/%s", disk, part);
264
	if (file == nil) { /* if no file, try to load from partition directly */
265
		partboot(fat);
266
		return;
267
	}
268
 
269
	if (fsroot == nil) {
270
		fsroot = &fs;
271
		memset(fsroot, 0, sizeof *fsroot);
272
		if (dosinit(fsroot, fat) < 0) {
273
			print("dosinit %s failed\n", fat);
274
			return;
275
		}
276
	}
277
 
278
	/* load kernel and jump to it */
279
	memset(&boot, 0, sizeof boot);
280
	boot.state = INITKERNEL;
281
	fsboot(fsroot, file, &boot);
282
 
283
	/* failed to boot */
284
}
285
 
286
/*
287
 * for a given disk's 9fat, find & load plan9.ini, parse it,
288
 * extract kernel filename, load that kernel and jump to it.
289
 */
290
static void
291
trydiskboot(char *disk)
292
{
293
	int n;
294
	char fat[80];
295
	char *ini, *bootfile;
296
 
297
	/* mount the disk's 9fat */
298
	memset(&fs, 0, sizeof fs);
299
	snprint(fat, sizeof fat, "#S/%s/9fat", disk);
300
	if (dosinit(&fs, fat) < 0) {
301
		print("dosinit %s failed\n", fat);
302
		return;
303
	}
304
 
305
	/* open plan9.ini, read it */
306
	ini = smalloc(Maxfile+1);
307
	if(fswalk(&fs, "plan9.ini", &file) <= 0) {
308
		print("no plan9.ini in %s\n", fat);
309
		n = 0;
310
	} else {
311
		n = fsread(&file, ini, Maxfile);
312
		if (n < 0)
313
			panic("error reading %s", ini);
314
	}
315
	ini[n] = 0;
316
 
317
	/*
318
	 * take note of plan9.ini contents.  consumes ini to make config vars,
319
	 * thus we can't free ini.
320
	 */
321
	dotini(ini);
322
	i8250console();			/* (re)configure serial port */
323
 
324
	bootfile = nil;			/* for kstrdup in askbootfile */
325
	if(isconf("bootfile")) {
326
		kstrdup(&bootfile, getconf("bootfile"));
327
		if(strcmp(bootfile, "manual") == 0)
328
			askbootfile(fat, sizeof fat, &bootfile, 0, "");
329
 
330
		/* pass arguments to kernels that can use them */
331
		strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, bootfile);
332
	} else if ((bootfile = findonekernel(&fs)) != nil) {  /* look in fat */
333
		snprint(fat, sizeof fat, "%s!9fat!%s", disk, bootfile);
334
		bootfile = fat;
335
		print("no bootfile named in plan9.ini; found %s\n", bootfile);
336
	} else {
337
		/* if #S/disk/kernel partition exists, load from it. */
338
		snprint(fat, sizeof fat, "#S/%s/kernel", disk);
339
		partboot(fat);
340
		/* last resort: ask the user */
341
		askbootfile(fat, sizeof fat, &bootfile, Promptsecs,
342
			"sdC0!9fat!9pccpu");
343
	}
344
	trybootfile(bootfile, &fs);
345
 
346
	/* failed; try again */
347
}
348
 
349
/*
350
 * find all the disks in #S, read their partition tables and set those
351
 * partitions in core, mainly so that we can access 9fat file systems.
352
 * for each disk's 9fat, read plan9.ini and boot the named kernel.
353
 */
354
void
355
bootloadproc(void *)
356
{
357
	int n, dirs, sdev;
358
	char kern[64];
359
	char *sdevs[128];
360
	Chan *sdch;
361
	Dir *dirp, *dp;
362
 
363
	memset(sdevs, 0, sizeof sdevs);
364
	sdch = nil;
365
	while(waserror()) {
366
		print("error caught at top level in bootload\n");
367
		if(sdch) {
368
			cclose(sdch);
369
			sdch = nil;
370
		}
371
	}
372
	bind("#S", "/dev", MAFTER);		/* try to force an attach */
373
	sdch = namecopen("#S", OREAD);
374
	if (sdch == nil)
375
		panic("no disks (no #S)");
376
	sdev = 0;
377
	while ((dirs = dirread(sdch, &dirp)) > 0) {
378
		for (dp = dirp; dirs-- > 0; dp++)
379
			if (strcmp(dp->name, "sdctl") != 0) {
380
				addsdev(dp);
381
				if (sdev >= nelem(sdevs))
382
					print("too many sdevs; ignoring %s\n",
383
						dp->name);
384
				else
385
					kstrdup(&sdevs[sdev++], dp->name);
386
			}
387
		free(dirp);
388
	}
389
	cclose(sdch);
390
	sdch = nil;
391
	if (sdev == 0)
392
		panic("no disks (in #S)");
393
 
394
	print("disks:");
395
	for (n = 0; n < sdev; n++)
396
		print(" %s", sdevs[n]);
397
	print("\n");
398
 
399
	for (n = 0; n < sdev; n++) {
400
		print("trying %s...", sdevs[n]);
401
		trydiskboot(sdevs[n]);
402
	}
403
	USED(sdch);
404
	for (;;) {
405
		askbootfile(kern, sizeof kern, nil, 0, "");
406
		trybootfile(kern, nil);
407
	}
408
	// poperror();
409
}