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