xref: /plan9/sys/src/9/pcboot/diskload.c (revision 4fa3bc585d2612e6bb007aaec8a7fe23f7fa9940)
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
dumpfile(char * file)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
dirread0(Chan * c,uchar * p,long n)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
dirread(Chan * c,Dir ** d)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
addsdev(Dir * dirp)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 *
findonekernel(Bootfs * fs)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
partboot(char * path)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
trybootfile(char * bootfile,Bootfs * fsroot)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
trydiskboot(char * disk)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
bootloadproc(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 }
410