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 bootfile = name; 190 kerns++; 191 } 192 if (kerns > 1) { 193 print("found these kernels:"); 194 for (n = 0; (name = array[n]) != nil; n++) 195 print(" %s", name); 196 print("\n"); 197 } 198 return kerns == 1? bootfile: nil; 199 } 200 201 int 202 partboot(char *path) 203 { 204 long n; 205 char *buf; 206 Boot boot; 207 Boot *b; 208 Chan *ch; 209 210 b = &boot; 211 memset(b, 0, sizeof *b); 212 b->state = INITKERNEL; 213 ch = namecopen(path, OREAD); 214 if (ch == nil) { 215 print("can't open partition %s\n", path); 216 return -1; 217 } 218 print("loading %s\n", path); 219 buf = smalloc(Bufsize); 220 while((n = devtab[ch->type]->read(ch, buf, Bufsize, ch->offset)) > 0) 221 if(bootpass(b, buf, n) != MORE) 222 break; 223 bootpass(b, nil, 0); /* attempts to boot */ 224 225 free(buf); 226 cclose(ch); 227 return -1; 228 } 229 230 /* fsroot must be nil or a fat root directory already dosinit'ed */ 231 static void 232 trybootfile(char *bootfile, Bootfs *fsroot) 233 { 234 int nf; 235 char fat[64]; 236 char *disk, *part, *file, *bootcopy; 237 char *fields[4]; 238 Boot boot; 239 static int didaddconf; 240 241 bootcopy = file = nil; 242 kstrdup(&bootcopy, bootfile); 243 nf = getfields(bootcopy, fields, nelem(fields), 0, "!"); 244 switch(nf){ 245 case 3: 246 file = fields[2]; 247 /* fall through */ 248 case 2: 249 disk = fields[0]; 250 part = fields[1]; 251 break; 252 default: 253 print("bad bootfile syntax: %s\n", bootfile); 254 return; 255 } 256 257 if(didaddconf == 0) { 258 didaddconf = 1; 259 sdaddallconfs(sdaddconf); 260 } 261 262 snprint(fat, sizeof fat, "#S/%s/%s", disk, part); 263 if (file == nil) { /* if no file, try to load from partition directly */ 264 partboot(fat); 265 return; 266 } 267 268 if (fsroot == nil) { 269 fsroot = &fs; 270 memset(fsroot, 0, sizeof *fsroot); 271 if (dosinit(fsroot, fat) < 0) { 272 print("dosinit %s failed\n", fat); 273 return; 274 } 275 } 276 277 /* load kernel and jump to it */ 278 memset(&boot, 0, sizeof boot); 279 boot.state = INITKERNEL; 280 fsboot(fsroot, file, &boot); 281 282 /* failed to boot */ 283 } 284 285 /* 286 * for a given disk's 9fat, find & load plan9.ini, parse it, 287 * extract kernel filename, load that kernel and jump to it. 288 */ 289 static void 290 trydiskboot(char *disk) 291 { 292 int n; 293 char fat[80]; 294 char *ini, *bootfile; 295 296 /* mount the disk's 9fat */ 297 memset(&fs, 0, sizeof fs); 298 snprint(fat, sizeof fat, "#S/%s/9fat", disk); 299 if (dosinit(&fs, fat) < 0) { 300 print("dosinit %s failed\n", fat); 301 return; 302 } 303 304 /* open plan9.ini, read it */ 305 ini = smalloc(Maxfile+1); 306 if(fswalk(&fs, "plan9.ini", &file) <= 0) { 307 print("no plan9.ini in %s\n", fat); 308 n = 0; 309 } else { 310 n = fsread(&file, ini, Maxfile); 311 if (n < 0) 312 panic("error reading %s", ini); 313 } 314 ini[n] = 0; 315 316 /* 317 * take note of plan9.ini contents. consumes ini to make config vars, 318 * thus we can't free ini. 319 */ 320 dotini(ini); 321 i8250console(); /* (re)configure serial port */ 322 323 bootfile = nil; /* for kstrdup in askbootfile */ 324 if(isconf("bootfile")) { 325 kstrdup(&bootfile, getconf("bootfile")); 326 if(strcmp(bootfile, "manual") == 0) 327 askbootfile(fat, sizeof fat, &bootfile, 0, ""); 328 329 /* pass arguments to kernels that can use them */ 330 strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, bootfile); 331 } else if ((bootfile = findonekernel(&fs)) != nil) { /* look in fat */ 332 snprint(fat, sizeof fat, "%s!9fat!%s", disk, bootfile); 333 bootfile = fat; 334 print("no bootfile named in plan9.ini; found %s\n", bootfile); 335 } else { 336 /* if #S/disk/kernel partition exists, load from it. */ 337 snprint(fat, sizeof fat, "#S/%s/kernel", disk); 338 partboot(fat); 339 /* last resort: ask the user */ 340 askbootfile(fat, sizeof fat, &bootfile, Promptsecs, 341 "sdC0!9fat!9pccpu"); 342 } 343 trybootfile(bootfile, &fs); 344 345 /* failed; try again */ 346 } 347 348 /* 349 * find all the disks in #S, read their partition tables and set those 350 * partitions in core, mainly so that we can access 9fat file systems. 351 * for each disk's 9fat, read plan9.ini and boot the named kernel. 352 */ 353 void 354 bootloadproc(void *) 355 { 356 int n, dirs, sdev; 357 char kern[64]; 358 char *sdevs[128]; 359 Chan *sdch; 360 Dir *dirp, *dp; 361 362 memset(sdevs, 0, sizeof sdevs); 363 sdch = nil; 364 while(waserror()) { 365 print("error caught at top level in bootload\n"); 366 if(sdch) { 367 cclose(sdch); 368 sdch = nil; 369 } 370 } 371 bind("#S", "/dev", MAFTER); /* try to force an attach */ 372 sdch = namecopen("#S", OREAD); 373 if (sdch == nil) 374 panic("no disks (no #S)"); 375 sdev = 0; 376 while ((dirs = dirread(sdch, &dirp)) > 0) { 377 for (dp = dirp; dirs-- > 0; dp++) 378 if (strcmp(dp->name, "sdctl") != 0) { 379 addsdev(dp); 380 if (sdev >= nelem(sdevs)) 381 print("too many sdevs; ignoring %s\n", 382 dp->name); 383 else 384 kstrdup(&sdevs[sdev++], dp->name); 385 } 386 free(dirp); 387 } 388 cclose(sdch); 389 sdch = nil; 390 if (sdev == 0) 391 panic("no disks (in #S)"); 392 393 print("disks:"); 394 for (n = 0; n < sdev; n++) 395 print(" %s", sdevs[n]); 396 print("\n"); 397 398 for (n = 0; n < sdev; n++) { 399 print("trying %s...", sdevs[n]); 400 trydiskboot(sdevs[n]); 401 } 402 USED(sdch); 403 for (;;) { 404 askbootfile(kern, sizeof kern, nil, 0, ""); 405 trybootfile(kern, nil); 406 } 407 // poperror(); 408 } 409