loadbsd.c revision 1.4.2.1 1 /*
2 * Load and boot NetBSD kernel on Human68k
3 *
4 * written by Yasha (ITOH Yasufumi)
5 * public domain
6 *
7 * loadbsd [-hvV] [-abDs] [-r root_device] netbsd
8 *
9 * loadbsd options:
10 * -h help
11 * -V print version and exit
12 *
13 * kernel options:
14 * -a auto boot, opposite of -s
15 * -s single user boot (default)
16 * -D enter kernel debugger
17 * -b ask root device
18 * -r specify root device
19 * -q quiet boot
20 * -v verbose boot (also turn on verbosity of loadbsd)
21 *
22 * $NetBSD: loadbsd.c,v 1.4.2.1 2000/11/20 20:30:17 bouyer Exp $
23 */
24
25 #include <sys/cdefs.h>
26
27 __RCSID("$NetBSD: loadbsd.c,v 1.4.2.1 2000/11/20 20:30:17 bouyer Exp $");
28 #define VERSION "$Revision: 1.4.2.1 $ $Date: 2000/11/20 20:30:17 $"
29
30 #include <sys/types.h> /* ntohl */
31 #include <sys/reboot.h>
32 #include <sys/param.h> /* ALIGN, ALIGNBYTES */
33 #include <a.out.h>
34 #include <string.h>
35 #include <machine/bootinfo.h>
36
37 #include <dos.h>
38 #include <iocs.h>
39 #include "../common/xprintf.h"
40 #include "trampoline.h"
41
42 #define DEFAULT_ROOTDEVNAME "sd@0,0:a"
43
44 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
45
46 #define GETDECIMAL(var, str) \
47 do { var *= 10; var += *str++ - '0'; } while (ISDIGIT(*str))
48
49 static const char *lookupif __P((const char *name,
50 unsigned *pif, unsigned *punit));
51 static void get_current_scsi_interface __P((unsigned *pif, unsigned *punit));
52 static int bootdev __P((const char *devstr));
53 static struct tramparg *read_kernel __P((const char *fn));
54 static int chkmpu __P((void));
55 static __dead void usage __P((int status, const char *msg))
56 __attribute__((noreturn));
57
58 int main __P((int argc, char *argv[]));
59
60 int opt_v;
61 int opt_N;
62
63 const struct hatbl {
64 char name[4];
65 unsigned short id;
66 } hatable[] = {
67 X68K_BOOT_SCSIIF_LIST
68 };
69
70 /*
71 * parse interface name
72 * return the next position
73 */
74 static const char *
75 lookupif(name, pif, punit)
76 const char *name;
77 unsigned *pif, *punit;
78 {
79 unsigned u, unit;
80 const char *p;
81
82 for (u = 0; u < sizeof hatable / sizeof hatable[0]; u++) {
83 const char *n;
84
85 for (n = hatable[u].name, p = name; *n && *n == *p; n++, p++)
86 ;
87 if (!*n)
88 goto found;
89 }
90 /* not found */
91 return (char *) 0;
92
93 found:
94 if (*p == '@')
95 p++;
96
97 /* get unit # */
98 if (!ISDIGIT(*p))
99 return (char *) 0;
100
101 unit = 0;
102 GETDECIMAL(unit, p);
103
104 *pif = hatable[u].id;
105 *punit = unit;
106
107 return p;
108 }
109
110 /*
111 * if the SCSI interface is not specified, use the current one
112 */
113 static void
114 get_current_scsi_interface(pif, punit)
115 unsigned *pif, *punit;
116 {
117 unsigned binf;
118 char *bootrom;
119 int bus_err_buf;
120
121 binf = (unsigned) IOCS_BOOTINF();
122 if (binf < 0x00fc0000)
123 return; /* not booted from SCSI */
124
125 bootrom = (char *) (binf & 0x00ffffe0);
126 if (IOCS_B_LPEEK(bootrom + 0x24) == 0x53435349 && /* 'SCSI' */
127 IOCS_B_WPEEK(bootrom + 0x28) == 0x494E) { /* 'IN' */
128 /* spc0 */
129 *pif = X68K_BOOT_SCSIIF_SPC;
130 *punit = 0;
131 } else if (DOS_BUS_ERR(&bus_err_buf, (void *)EXSPC_BDID, 1)) {
132 /* mha0 */
133 *pif = X68K_BOOT_SCSIIF_MHA;
134 *punit = 0;
135 } else {
136 /* spc1 */
137 *pif = X68K_BOOT_SCSIIF_SPC;
138 *punit = 1;
139 }
140 }
141
142 /*
143 * parse device name
144 *
145 * [/<controller>@<unit>/]<device>@<unit>[,<lun>][:<partition>]
146 *
147 * <unit> must be target SCSI ID if <device> is a SCSI device
148 *
149 * full form:
150 * /spc@0/sd@1,2:e
151 *
152 * partial form:
153 * /mha@0/sd@1 = /mha@0/sd@1,0:a
154 * sd@1:e = /current_device/sd@1,0e
155 * sd@1,2:e = /current_device/sd@1,2:e
156 */
157
158 const struct devtbl {
159 char name[3];
160 u_char major;
161 } devtable[] = {
162 X68K_BOOT_DEV_LIST,
163 X68K_BOOT_NETIF_LIST
164 };
165
166 static int
167 bootdev(devstr)
168 const char *devstr;
169 {
170 unsigned u;
171 unsigned major, unit, lun, partition;
172 int dev;
173 const char *s = devstr;
174 unsigned interface = 0, unit_if = 0;
175
176 if (*s == '/') {
177 /*
178 * /<interface>/<device>"
179 * "/spc@1/sd@2,3:e"
180 */
181 while (*++s == '/') /* skip slashes */
182 ;
183 if (!strchr(s, '/'))
184 xerrx(1, "%s: bad format", devstr);
185
186 if (!(s = lookupif(s, &interface, &unit_if)))
187 xerrx(1, "%s: unknown interface", devstr);
188
189 while (*s == '/') /* skip slashes */
190 s++;
191 } else {
192 /* make lint happy */
193 interface = 0;
194 unit_if = 0;
195 }
196
197 /* allow r at the top */
198 if (*s == 'r')
199 s++;
200
201 for (u = 0; u < sizeof devtable / sizeof devtable[0]; u++)
202 if (s[0] == devtable[u].name[0] && s[1] == devtable[u].name[1])
203 goto found;
204
205 /* not found */
206 xerrx(1, "%s: unknown device", devstr);
207
208 found: major = devtable[u].major;
209
210 /*
211 * <type>@unit[,lun][:part]
212 * "sd@1,3:a"
213 */
214
215 /* get device unit # */
216 s += 2;
217 if (*s == '@')
218 s++;
219 if (!*s)
220 xerrx(1, "%s: missing unit number", devstr);
221 if (!ISDIGIT(*s))
222 xerrx(1, "%s: wrong device", devstr);
223
224 unit = 0;
225 GETDECIMAL(unit, s);
226
227 lun = 0;
228 if (*s == ',') {
229 s++;
230 if (!ISDIGIT(*s))
231 xerrx(1, "%s: wrong device", devstr);
232 GETDECIMAL(lun, s);
233 }
234
235 /* get device partition */
236 if (*s == ':')
237 s++;
238 if (!*s)
239 partition = 0; /* no partition letter -- assuming 'a' */
240 else if (!s[1])
241 partition = *s - 'a';
242 else
243 xerrx(1, "%s: wrong partition letter", devstr);
244
245 /*
246 * sanity check
247 */
248 if (unit_if >= 16)
249 xerrx(1, "%s: interface unit # too large", devstr);
250 if (unit >= 16)
251 xerrx(1, "%s: device unit # too large", devstr);
252 if (lun >= 8)
253 xerrx(1, "%s: SCSI LUN >= 8 is not supported yet", devstr);
254 if (partition >= 16)
255 xerrx(1, "%s: unsupported partition", devstr);
256
257 /*
258 * encode device to be passed to kernel
259 */
260 if (X68K_BOOT_DEV_IS_SCSI(major)) {
261 /*
262 * encode SCSI device
263 */
264 if (interface == 0)
265 get_current_scsi_interface(&interface, &unit_if);
266
267 dev = X68K_MAKESCSIBOOTDEV(major, interface, unit_if,
268 unit, lun, partition);
269 } else {
270 /* encode non-SCSI device */
271 dev = X68K_MAKEBOOTDEV(major, unit, partition);
272 }
273
274 if (opt_v)
275 xwarnx("%s: major %u, if %u, un_if %u, unit %u, lun %u, partition %u; bootdev 0x%x",
276 devstr, major, interface, unit_if, unit, lun, partition, dev);
277
278 return dev;
279 }
280
281 /*
282 * read kernel and create trampoline
283 *
284 * |----------------------| <- allocated buf addr
285 * | kernel image |
286 * ~ (header is excluded) ~
287 * | |
288 * |----------------------| <- return value (entry addr of trampoline)
289 * | struct tramparg |
290 * | (trampoline args) |
291 * |----------------------|
292 * | trampoline code |
293 * | (in assembly) |
294 * |----------------------|
295 */
296 static struct tramparg *
297 read_kernel(fn)
298 const char *fn;
299 {
300 int fd;
301 union dos_fcb *fcb;
302 size_t filesize, nread;
303 void *buf;
304 struct exec hdr;
305 int i;
306 struct tramparg *arg;
307 size_t size_tramp = end_trampoline - trampoline;
308
309 if ((fd = DOS_OPEN(fn, 0x20)) < 0) /* RO, share READ */
310 xerr(1, "%s: open", fn);
311
312 if ((int)(fcb = DOS_GET_FCB_ADR(fd)) < 0)
313 xerr(1, "%s: get_fcb_adr", fn);
314
315 /*
316 * XXX FCB is in supervisor area
317 */
318 /*if (fcb->blk.mode != 0)*/
319 if (IOCS_B_BPEEK((char *)fcb + 1) & 0x80)
320 xerrx(1, "%s: Not a regular file", fn);
321
322 /*filesize = fcb->blk.size;*/
323 filesize = IOCS_B_LPEEK(&fcb->blk.size);
324
325 /*
326 * read a.out header
327 */
328 if ((nread = DOS_READ(fd, (void *) &hdr, sizeof hdr)) != sizeof hdr) {
329 if ((int)nread < 0)
330 xerr(1, "%s: read header", fn);
331 else
332 xerrx(1, "%s: Not an a.out", fn);
333 }
334 /*
335 * check header
336 */
337 if (N_GETMAGIC(hdr) != NMAGIC)
338 xerrx(1, "%s: Bad magic number", fn);
339 if ((i = N_GETMID(hdr)) != MID_M68K)
340 xerrx(1, "%s: Wrong architecture (mid %d)", fn, i);
341
342 if (opt_v)
343 xwarnx("%s: %u bytes; text %u, data %u, bss %u, sym %u",
344 fn, filesize, hdr.a_text, hdr.a_data,
345 hdr.a_bss, hdr.a_syms);
346
347 /*
348 * then, read entire body
349 */
350 if ((int)(buf = DOS_MALLOC(filesize + ALIGNBYTES - sizeof hdr
351 + sizeof(struct tramparg)
352 + size_tramp + SIZE_TMPSTACK)) < 0)
353 xerr(1, "read_kernel");
354
355 if ((nread = DOS_READ(fd, buf, filesize - sizeof hdr))
356 != filesize - sizeof hdr) {
357 if ((int)nread < 0)
358 xerr(1, "%s: read", fn);
359 else
360 xerrx(1, "%s: short read", fn);
361 }
362
363 if (DOS_CLOSE(fd) < 0)
364 xerr(1, "%s: close", fn);
365
366 /*
367 * create argument for trampoline code
368 */
369 arg = (struct tramparg *) ALIGN(buf + nread);
370
371 if (opt_v)
372 xwarnx("trampoline arg at %p", arg);
373
374 arg->bsr_inst = TRAMP_BSR + sizeof(struct tramparg) - 2;
375 arg->tmp_stack = (char *) arg + sizeof(struct tramparg)
376 + size_tramp + SIZE_TMPSTACK;
377 arg->mpu_type = IOCS_MPU_STAT() & 0xff;
378 arg->xk.image_top = buf;
379 arg->xk.load_addr = 0x00000000; /* XXX should not be a fixed addr */
380 arg->xk.text_size = hdr.a_text;
381 arg->xk.data_size = hdr.a_data;
382 arg->xk.bss_size = hdr.a_bss;
383 arg->xk.symbol_size = hdr.a_syms;
384 arg->xk.d5 = IOCS_BOOTINF(); /* unused for now */
385 #if 0
386 /* filled afterwards */
387 arg->xk.rootdev =
388 arg->xk.boothowto =
389 #endif
390 arg->xk.entry_addr = hdr.a_entry;
391
392 if (opt_v)
393 xwarnx("args: mpu %d, image %p, load 0x%x, entry 0x%x",
394 arg->mpu_type, arg->xk.image_top, arg->xk.load_addr,
395 arg->xk.entry_addr);
396
397 /*
398 * copy trampoline code
399 */
400 if (opt_v)
401 xwarnx("trampoline code at %p (%u bytes)",
402 (char *) arg + sizeof(struct tramparg), size_tramp);
403
404 memcpy((char *) arg + sizeof(struct tramparg), trampoline, size_tramp);
405
406 return arg;
407 }
408
409 /*
410 * MC68000/010 -> return zero
411 * MC68020 and later -> return nonzero
412 */
413 static int
414 chkmpu()
415 {
416 register int ret asm("d0");
417
418 asm("| %0 <- this must be d0\n\
419 moveq #1,d0\n\
420 .long 0x103B02FF | foo: moveb pc@((foo+1)-foo-2:B,d0:W:2),d0\n\
421 | ^ ^\n\
422 | d0.b = 0x02 (68000/010)\n\
423 | = 0xff (68020 and later)\n\
424 bmis 1f\n\
425 moveq #0,d0 | 68000/010\n\
426 1:" : "=d" (ret));
427
428 return ret;
429 }
430
431 static __dead void
432 usage(status, msg)
433 int status;
434 const char *msg;
435 {
436 extern const char *const __progname;
437
438 if (msg)
439 xwarnx("%s", msg);
440
441 xerrprintf("\
442 %s [-hvV] [-abDs] [-r root_device] netbsd\n\
443 \n\
444 loadbsd options:\n\
445 \t-h help\n\
446 \t-v verbose\n\
447 \t-V print version and exit\n\
448 \n\
449 kernel options:\n\
450 \t-a auto boot, opposite of -s\n\
451 \t-s single user boot (default)\n\
452 \t-D enter kernel debugger\n\
453 \t-b ask root device\n\
454 \t-r specify root device (default %s)\n\
455 \t format: [/interface/]device@unit[,lun][:partition]\n\
456 \t interface: one of spc@0, spc@1, mha@0\n\
457 \t (current boot interface if omitted)\n\
458 \t device: one of fd, sd, cd, md, ne\n\
459 \t unit: device unit number (SCSI ID for SCSI device)\n\
460 \t lun: SCSI LUN # (0 if omitted)\n\
461 \t partition: partition letter ('a' if omitted)\n\
462 ", __progname, DEFAULT_ROOTDEVNAME);
463
464 DOS_EXIT2(status);
465 }
466
467 int
468 main(argc, argv)
469 int argc;
470 char *argv[];
471 {
472 char *rootdevname = 0;
473 int rootdev;
474 u_long boothowto = RB_SINGLE;
475 const char *kernel;
476 char *p, **flg, **arg;
477 struct tramparg *tramp;
478 struct dos_dregs regs; /* unused... */
479 int i;
480
481 /* parse options */
482 for (arg = flg = argv + 1; (p = *flg) && *p == '-'; ) {
483 int c;
484
485 while ((c = *++p))
486 switch (c) {
487 case 'h':
488 usage(0, (char *) 0);
489 /* NOTREACHED */
490 break;
491 case 'N': /* don't actually execute kernel */
492 opt_N = 1;
493 break;
494 case 'v':
495 opt_v = 1;
496 boothowto |= AB_VERBOSE; /* XXX */
497 break;
498 case 'V':
499 xprintf("loadbsd %s\n", VERSION);
500 return 0;
501
502 /*
503 * kernel boot flags
504 */
505 case 'r':
506 if (rootdevname)
507 usage(1, "multiple -r flags");
508 else if (!*++arg)
509 usage(1, "-r requires device name");
510 else
511 rootdevname = *arg;
512 break;
513 case 'b':
514 boothowto |= RB_ASKNAME;
515 break;
516 case 'a':
517 boothowto &= ~RB_SINGLE;
518 break;
519 case 's':
520 boothowto |= RB_SINGLE;
521 break;
522 case 'D':
523 boothowto |= RB_KDB;
524 break;
525 case 'q':
526 boothowto |= AB_QUIET;
527 break;
528
529 default:
530 usage(1, (char *) 0);
531 /* NOTREACHED */
532 break;
533 }
534 flg = ++arg;
535 }
536
537 /* check MPU */
538 if (chkmpu() == 0)
539 xerrx(1, "Can't boot NetBSD on 68000/010");
540
541 argc -= arg - argv;
542 argv = arg;
543
544 if (argc != 1)
545 usage(1, (char *) 0);
546
547 kernel = *argv;
548
549 rootdev = bootdev(rootdevname ? rootdevname : DEFAULT_ROOTDEVNAME);
550
551 if (opt_v)
552 xwarnx("boothowto 0x%x", boothowto);
553
554 tramp = read_kernel(kernel);
555
556 tramp->xk.rootdev = rootdev;
557 tramp->xk.boothowto = boothowto;
558
559 /*
560 * we never return, and make sure the disk cache
561 * be flushed (if write-back cache is enabled)
562 */
563 if (opt_v)
564 xwarnx("flush disk cache...");
565
566 i = DOS_FFLUSH_SET(1); /* enable fflush */
567 DOS_FFLUSH(); /* then, issue fflush */
568 (void) DOS_FFLUSH_SET(i); /* restore old mode just in case */
569
570 /*
571 * the program assumes the MPU caches off
572 */
573 if (opt_v)
574 xwarnx("flush and disable MPU caches...");
575
576 IOCS_CACHE_MD(-1); /* flush */
577 if (!opt_N)
578 IOCS_CACHE_MD(0); /* disable both caches */
579
580 if (opt_v)
581 xwarnx("Jumping to the kernel. Good Luck!");
582
583 if (opt_N)
584 xerrx(0, "But don't actually do it.");
585
586 DOS_SUPER_JSR((void (*) __P((void))) tramp, ®s, ®s);
587
588 /* NOTREACHED */
589
590 xwarnx("??? return from kernel");
591
592 return 1;
593 }
594