1 /*
2 * Load and boot NetBSD kernel on Human68k
3 *
4 * written by ITOH Yasufumi
5 * public domain
6 *
7 * loadbsd [-hvV] [-abDNqs] [-r root_device] netbsd
8 *
9 * loadbsd options:
10 * -h help
11 * -N do not actually execute kernel
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 * -q quiet boot
21 * -v verbose boot (also turn on verbosity of loadbsd)
22 *
23 * $NetBSD: loadbsd.c,v 1.16 2024/01/07 07:58:34 isaki Exp $
24 */
25
26 #include <sys/cdefs.h>
27
28 __RCSID("$NetBSD: loadbsd.c,v 1.16 2024/01/07 07:58:34 isaki Exp $");
29 #define VERSION "$Revision: 1.16 $ $Date: 2024/01/07 07:58:34 $"
30
31 #include <sys/types.h> /* ntohl */
32 #include <sys/reboot.h>
33 #include <sys/param.h> /* ALIGN, ALIGNBYTES */
34 #include <a.out.h>
35 #include <sys/exec_elf.h>
36 #include <string.h>
37 #include <machine/bootinfo.h>
38
39 #include <dos.h>
40 #include <iocs.h>
41 #include "../common/xprintf.h"
42 #include "trampoline.h"
43
44 #define DEFAULT_ROOTDEVNAME "sd@0,0:a"
45
46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
47
48 #define GETDECIMAL(var, str) \
49 do { var *= 10; var += *str++ - '0'; } while (ISDIGIT(*str))
50
51 static const char *lookupif(const char *name,
52 unsigned *pif, unsigned *punit);
53 static void get_current_scsi_interface(unsigned *pif, unsigned *punit);
54 static int bootdev(const char *devstr);
55 static struct tramparg *read_kernel(const char *fn);
56 static int chkmpu(void);
57 static __dead void usage(int status, const char *msg)
58 __attribute__((noreturn));
59
60 int main(int argc, char *argv[]);
61
62 int opt_v;
63 int opt_N;
64 const char *kernel_fn;
65
66 const struct hatbl {
67 char name[4];
68 unsigned short id;
69 } hatable[] = {
70 X68K_BOOT_SCSIIF_LIST
71 };
72
73 /*
74 * parse interface name
75 * return the next position
76 */
77 static const char *
lookupif(const char * name,unsigned * pif,unsigned * punit)78 lookupif(const char *name, unsigned *pif, unsigned *punit)
79 {
80 unsigned u, unit;
81 const char *p;
82
83 for (u = 0; u < sizeof hatable / sizeof hatable[0]; u++) {
84 const char *n;
85
86 for (n = hatable[u].name, p = name; *n && *n == *p; n++, p++)
87 ;
88 if (!*n)
89 goto found;
90 }
91 /* not found */
92 return (char *) 0;
93
94 found:
95 if (*p == '@')
96 p++;
97
98 /* get unit # */
99 if (!ISDIGIT(*p))
100 return (char *) 0;
101
102 unit = 0;
103 GETDECIMAL(unit, p);
104
105 *pif = hatable[u].id;
106 *punit = unit;
107
108 return p;
109 }
110
111 /*
112 * if the SCSI interface is not specified, use the current one
113 */
114 static void
get_current_scsi_interface(unsigned * pif,unsigned * punit)115 get_current_scsi_interface(unsigned *pif, unsigned *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
bootdev(const char * devstr)167 bootdev(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 #define LOADBSD
281 #include "../common/exec_sub.c"
282
283 /*
284 * read kernel and create trampoline
285 *
286 * |----------------------| <- allocated buf addr
287 * | kernel image |
288 * ~ (exec file contents) ~
289 * | |
290 * |----------------------| <- return value (entry addr of trampoline)
291 * | struct tramparg |
292 * | (trampoline args) |
293 * |----------------------|
294 * | trampoline code |
295 * | (in assembly) |
296 * |----------------------|
297 */
298 static struct tramparg *
read_kernel(const char * fn)299 read_kernel(const char *fn)
300 {
301 int fd;
302 union dos_fcb *fcb;
303 size_t filesize, nread;
304 void *buf;
305 struct tramparg *arg;
306 size_t size_tramp = end_trampoline - trampoline;
307
308 kernel_fn = fn;
309
310 if ((fd = DOS_OPEN(fn, 0x00)) < 0) /* read only */
311 xerr(1, "%s: open", fn);
312
313 if ((int)(fcb = DOS_GET_FCB_ADR(fd)) < 0)
314 xerr(1, "%s: get_fcb_adr", fn);
315
316 /*
317 * XXX FCB is in supervisor area
318 */
319 /*if (fcb->blk.mode != 0)*/
320 if (IOCS_B_BPEEK((char *)fcb + 1) & 0x80)
321 xerrx(1, "%s: Not a regular file", fn);
322
323 /*filesize = fcb->blk.size;*/
324 filesize = IOCS_B_LPEEK(&fcb->blk.size);
325
326 if (filesize < sizeof(Elf32_Ehdr))
327 xerrx(1, "%s: Unknown format", fn);
328
329 /*
330 * read entire file
331 */
332 if ((int)(buf = DOS_MALLOC(filesize + ALIGNBYTES
333 + sizeof(struct tramparg)
334 + size_tramp + SIZE_TMPSTACK)) < 0)
335 xerr(1, "read_kernel");
336
337 if ((nread = DOS_READ(fd, buf, filesize)) != filesize) {
338 if ((int)nread < 0)
339 xerr(1, "%s: read", fn);
340 else
341 xerrx(1, "%s: short read", fn);
342 }
343
344 if (DOS_CLOSE(fd) < 0)
345 xerr(1, "%s: close", fn);
346
347 /*
348 * address for argument for trampoline code
349 */
350 arg = (struct tramparg *) ALIGN((char *) buf + nread);
351
352 if (opt_v)
353 xwarnx("trampoline arg at %p", arg);
354
355 xk_load(&arg->xk, buf, 0 /* XXX load addr should not be fixed */);
356
357 /*
358 * create argument for trampoline code
359 */
360 arg->bsr_inst = TRAMP_BSR + sizeof(struct tramparg) - 2;
361 arg->tmp_stack = (char *) arg + sizeof(struct tramparg)
362 + size_tramp + SIZE_TMPSTACK;
363 arg->mpu_type = IOCS_MPU_STAT() & 0xff;
364
365 arg->xk.d5 = IOCS_BOOTINF(); /* unused for now */
366 #if 0
367 /* filled afterwards */
368 arg->xk.rootdev =
369 arg->xk.boothowto =
370 #endif
371
372 if (opt_v)
373 xwarnx("args: mpu %d, image %p, load 0x%x, entry 0x%x",
374 arg->mpu_type, arg->xk.sec[0].sec_image,
375 arg->xk.load_addr, arg->xk.entry_addr);
376
377 /*
378 * copy trampoline code
379 */
380 if (opt_v)
381 xwarnx("trampoline code at %p (%u bytes)",
382 (char *) arg + sizeof(struct tramparg), size_tramp);
383
384 memcpy((char *) arg + sizeof(struct tramparg), trampoline, size_tramp);
385
386 return arg;
387 }
388
389 /*
390 * MC68000/010 -> return zero
391 * MC68020 and later -> return nonzero
392 */
393 static int
chkmpu(void)394 chkmpu(void)
395 {
396 register int ret __asm("%d0");
397
398 __asm("| %0 <- this must be %%d0\n\
399 moveq #1,%%d0\n\
400 .long 0x103B02FF | foo: moveb %%pc@((foo+1)-foo-2:B,d0:W:2),%%d0\n\
401 | ^ ^\n\
402 | %%d0.b = 0x02 (68000/010)\n\
403 | = 0xff (68020 and later)\n\
404 bmis 1f\n\
405 moveq #0,%%d0 | 68000/010\n\
406 1:" : "=d" (ret));
407
408 return ret;
409 }
410
411 static __dead void
usage(int status,const char * msg)412 usage(int status, const char *msg)
413 {
414 extern const char *const __progname;
415
416 if (msg)
417 xwarnx("%s", msg);
418
419 xerrprintf("\
420 %s [-hvV] [-abDNqs] [-r root_device] netbsd\n\
421 \n\
422 loadbsd options:\n\
423 \t-h help\n\
424 \t-N do not execute kernel\n\
425 \t-v verbose\n\
426 \t-V print version and exit\n\
427 \n\
428 kernel options:\n\
429 \t-a auto boot, opposite of -s\n\
430 \t-s single user boot (default)\n\
431 \t-D enter kernel debugger\n\
432 \t-q quiet boot\n\
433 \t-b ask root device\n\
434 \t-r specify root device (default %s)\n\
435 \t format: [/interface/]device@unit[,lun][:partition]\n\
436 \t interface: one of spc@0, spc@1, mha@0\n\
437 \t (current boot interface if omitted)\n\
438 \t device: one of fd, sd, cd, md, ne\n\
439 \t unit: device unit number (SCSI ID for SCSI device)\n\
440 \t lun: SCSI LUN # (0 if omitted)\n\
441 \t partition: partition letter ('a' if omitted)\n\
442 ", __progname, DEFAULT_ROOTDEVNAME);
443
444 DOS_EXIT2(status);
445 }
446
447 int
main(int argc,char * argv[])448 main(int argc, char *argv[])
449 {
450 char *rootdevname = 0;
451 int rootdev;
452 u_long boothowto = RB_SINGLE;
453 const char *kernel;
454 char *p, **flg, **arg;
455 struct tramparg *tramp;
456 struct dos_dregs regs; /* unused... */
457 int i;
458
459 /* parse options */
460 for (arg = flg = argv + 1; (p = *flg) && *p == '-'; ) {
461 int c;
462
463 while ((c = *++p))
464 switch (c) {
465 case 'h':
466 usage(0, (char *) 0);
467 /* NOTREACHED */
468 break;
469 case 'N': /* don't actually execute kernel */
470 opt_N = 1;
471 break;
472 case 'v':
473 opt_v = 1;
474 boothowto |= AB_VERBOSE; /* XXX */
475 break;
476 case 'V':
477 xprintf("loadbsd %s\n", VERSION);
478 return 0;
479
480 /*
481 * kernel boot flags
482 */
483 case 'r':
484 if (rootdevname)
485 usage(1, "multiple -r flags");
486 else if (!*++arg)
487 usage(1, "-r requires device name");
488 else
489 rootdevname = *arg;
490 break;
491 case 'b':
492 boothowto |= RB_ASKNAME;
493 break;
494 case 'a':
495 boothowto &= ~RB_SINGLE;
496 break;
497 case 's':
498 boothowto |= RB_SINGLE;
499 break;
500 case 'D':
501 boothowto |= RB_KDB;
502 break;
503 case 'q':
504 boothowto |= AB_QUIET;
505 break;
506
507 default:
508 usage(1, (char *) 0);
509 /* NOTREACHED */
510 break;
511 }
512 flg = ++arg;
513 }
514
515 /* check MPU */
516 if (chkmpu() == 0)
517 xerrx(1, "Can't boot NetBSD on 68000/010");
518
519 argc -= arg - argv;
520 argv = arg;
521
522 if (argc != 1)
523 usage(1, (char *) 0);
524
525 kernel = *argv;
526
527 rootdev = bootdev(rootdevname ? rootdevname : DEFAULT_ROOTDEVNAME);
528
529 if (opt_v)
530 xwarnx("boothowto 0x%x", boothowto);
531
532 tramp = read_kernel(kernel);
533
534 tramp->xk.rootdev = rootdev;
535 tramp->xk.boothowto = boothowto;
536
537 /*
538 * we never return, and make sure the disk cache
539 * be flushed (if write-back cache is enabled)
540 */
541 if (opt_v)
542 xwarnx("flush disk cache...");
543
544 i = DOS_FFLUSH_SET(1); /* enable fflush */
545 DOS_FFLUSH(); /* then, issue fflush */
546 (void) DOS_FFLUSH_SET(i); /* restore old mode just in case */
547
548 /*
549 * the program assumes the MPU caches off
550 */
551 if (opt_v)
552 xwarnx("flush and disable MPU caches...");
553
554 IOCS_CACHE_MD(-1); /* flush */
555 if (!opt_N)
556 IOCS_CACHE_MD(0); /* disable both caches */
557
558 if (opt_v)
559 xwarnx("Jumping to the kernel. Good Luck!");
560
561 if (opt_N)
562 xerrx(0, "But don't actually do it.");
563
564 DOS_SUPER_JSR((void (*)(void)) tramp, ®s, ®s);
565
566 /* NOTREACHED */
567
568 xwarnx("??? return from kernel");
569
570 return 1;
571 }
572