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