1 /* $NetBSD: boot.c,v 1.35 2017/09/15 13:25:34 martin Exp $ */
2
3 /*
4 * Copyright (c) 1997, 1999 Eduardo E. Horvath. All rights reserved.
5 * Copyright (c) 1997 Jason R. Thorpe. All rights reserved.
6 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
7 * Copyright (C) 1995, 1996 TooLs GmbH.
8 * All rights reserved.
9 *
10 * ELF support derived from NetBSD/alpha's boot loader, written
11 * by Christopher G. Demetriou.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by TooLs GmbH.
24 * 4. The name of TooLs GmbH may not be used to endorse or promote products
25 * derived from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * First try for the boot code
41 *
42 * Input syntax is:
43 * [promdev[{:|,}partition]]/[filename] [flags]
44 */
45
46 #include <lib/libsa/stand.h>
47 #include <lib/libsa/bootcfg.h>
48 #include <lib/libsa/loadfile.h>
49 #include <lib/libkern/libkern.h>
50
51 #include <sys/param.h>
52 #include <sys/reboot.h>
53 #include <sys/disklabel.h>
54 #include <sys/boot_flag.h>
55
56 #include <machine/cpu.h>
57 #include <machine/promlib.h>
58 #include <machine/bootinfo.h>
59 #include <sparc/stand/common/isfloppy.h>
60
61 #include "boot.h"
62 #include "ofdev.h"
63 #include "openfirm.h"
64
65
66 #define COMPAT_BOOT(marks) (marks[MARK_START] == marks[MARK_ENTRY])
67
68
69 typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3,
70 long ofw);
71
72 /*
73 * Boot device is derived from ROM provided information, or if there is none,
74 * this list is used in sequence, to find a kernel.
75 */
76 const char *kernelnames[] = {
77 "netbsd",
78 "netbsd.gz",
79 "netbsd.old",
80 "netbsd.old.gz",
81 "onetbsd",
82 "onetbsd.gz",
83 "vmunix ",
84 #ifdef notyet
85 "netbsd.pl ",
86 "netbsd.pl.gz ",
87 "netbsd.el ",
88 "netbsd.el.gz ",
89 #endif
90 NULL
91 };
92
93 char bootdev[PROM_MAX_PATH];
94 bool root_fs_quickseekable = true; /* unset for tftp boots */
95 static bool bootinfo_pass_bootdev = false;
96
97 int debug = 0;
98 int compatmode = 0;
99 extern char twiddle_toggle;
100
101 #if 0
102 static void
103 prom2boot(char *dev)
104 {
105 char *cp, *lp = 0;
106 int handle;
107 char devtype[16];
108
109 for (cp = dev; *cp; cp++)
110 if (*cp == ':')
111 lp = cp;
112 if (!lp)
113 lp = cp;
114 *lp = 0;
115 }
116 #endif
117
118 static int
bootoptions(const char * ap,char * loaddev,char * kernel,char * options)119 bootoptions(const char *ap, char *loaddev, char *kernel, char *options)
120 {
121 int v = 0;
122 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL;
123 const char *path;
124 char partition, *pp;
125
126 *kernel = '\0';
127 *options = '\0';
128
129 if (ap == NULL) {
130 return (0);
131 }
132
133 while (*ap == ' ') {
134 ap++;
135 }
136
137 if (*ap != '-') {
138 start1 = ap;
139 while (*ap != '\0' && *ap != ' ') {
140 ap++;
141 }
142 end1 = ap;
143
144 while (*ap == ' ') {
145 ap++;
146 }
147
148 if (*ap != '-') {
149 start2 = ap;
150 while (*ap != '\0' && *ap != ' ') {
151 ap++;
152 }
153 end2 = ap;
154 while (*ap != '\0' && *ap == ' ') {
155 ap++;
156 }
157 }
158 }
159 if (end2 == start2) {
160 start2 = end2 = NULL;
161 }
162 if (end1 == start1) {
163 start1 = end1 = NULL;
164 }
165
166 if (start1 == NULL) {
167 /* only options */
168 } else if (start2 == NULL) {
169 memcpy(kernel, start1, (end1 - start1));
170 kernel[end1 - start1] = '\0';
171 path = filename(kernel, &partition);
172 if (path == NULL) {
173 strcpy(loaddev, kernel);
174 kernel[0] = '\0';
175 } else if (path != kernel) {
176 /* copy device part */
177 memcpy(loaddev, kernel, path-kernel);
178 loaddev[path-kernel] = '\0';
179 if (partition) {
180 pp = loaddev + strlen(loaddev);
181 pp[0] = ':';
182 pp[1] = partition;
183 pp[2] = '\0';
184 }
185 /* and kernel path */
186 strcpy(kernel, path);
187 }
188 } else {
189 memcpy(loaddev, start1, (end1-start1));
190 loaddev[end1-start1] = '\0';
191 memcpy(kernel, start2, (end2 - start2));
192 kernel[end2 - start2] = '\0';
193 }
194
195 twiddle_toggle = 1;
196 strcpy(options, ap);
197 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
198 BOOT_FLAG(*ap, v);
199 switch(*ap++) {
200 case 'D':
201 debug = 2;
202 break;
203 case 'C':
204 compatmode = 1;
205 break;
206 case 'T':
207 twiddle_toggle = 1 - twiddle_toggle;
208 break;
209 default:
210 break;
211 }
212 }
213
214 if (((v & RB_KDB) != 0) && (debug == 0)) {
215 debug = 1;
216 }
217
218 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n",
219 loaddev, kernel, options));
220 return (v);
221 }
222
223 /*
224 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
225 * ksyms information unless it resides in a dedicated memory allocated from
226 * PROM and aligned on NBPG boundary. This is because the kernels calculate
227 * their ends on their own, they use address of 'end[]' reference which follows
228 * text segment. Ok, allocate some memory from PROM and copy symbol information
229 * over there.
230 */
231 static void
ksyms_copyout(void ** ssym,void ** esym)232 ksyms_copyout(void **ssym, void **esym)
233 {
234 uint8_t *addr;
235 int kssize = (int)(long)((char *)*esym - (char *)*ssym + 1);
236
237 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
238 *ssym, *esym, kssize));
239
240 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
241 panic("ksyms_copyout(): no space for symbol table");
242 }
243
244 memcpy(addr, *ssym, kssize);
245 *ssym = addr;
246 *esym = addr + kssize - 1;
247
248 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
249 }
250
251 /*
252 * Prepare boot information and jump directly to the kernel.
253 */
254 static void
jump_to_kernel(u_long * marks,char * kernel,char * args,void * ofw,int boothowto)255 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw,
256 int boothowto)
257 {
258 int l, machine_tag;
259 long newargs[4];
260 void *ssym, *esym;
261 vaddr_t bootinfo;
262 struct btinfo_symtab bi_sym;
263 struct btinfo_kernend bi_kend;
264 struct btinfo_boothowto bi_howto;
265 char *cp;
266 char bootline[PROM_MAX_PATH * 2];
267
268 /* Compose kernel boot line. */
269 strncpy(bootline, kernel, sizeof(bootline));
270 cp = bootline + strlen(bootline);
271 if (*args) {
272 *cp++ = ' ';
273 strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
274 }
275 *cp = 0; args = bootline;
276
277 /* Record symbol information in the bootinfo. */
278 bootinfo = bi_init(marks[MARK_END]);
279 bi_sym.nsym = marks[MARK_NSYM];
280 bi_sym.ssym = marks[MARK_SYM];
281 bi_sym.esym = marks[MARK_END];
282 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
283 bi_kend.addr= bootinfo + BOOTINFO_SIZE;
284 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
285 bi_howto.boothowto = boothowto;
286 bi_add(&bi_howto, BTINFO_BOOTHOWTO, sizeof(bi_howto));
287 if (bootinfo_pass_bootunit)
288 bi_add(&bi_unit, BTINFO_BOOTDEV_UNIT,
289 sizeof(bi_unit));
290 if (bootinfo_pass_bootdev) {
291 struct {
292 struct btinfo_common common;
293 char name[256];
294 } info;
295
296 strcpy(info.name, bootdev);
297 bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev)
298 +sizeof(struct btinfo_bootdev));
299 }
300
301 sparc64_finalize_tlb(marks[MARK_DATA]);
302 sparc64_bi_add();
303
304 ssym = (void*)(long)marks[MARK_SYM];
305 esym = (void*)(long)marks[MARK_END];
306
307 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
308
309 /* Adjust ksyms pointers, if needed. */
310 if (COMPAT_BOOT(marks) || compatmode) {
311 ksyms_copyout(&ssym, &esym);
312 }
313
314 freeall();
315 /*
316 * When we come in args consists of a pointer to the boot
317 * string. We need to fix it so it takes into account
318 * other params such as romp.
319 */
320
321 /*
322 * Stash pointer to end of symbol table after the argument
323 * strings.
324 */
325 l = strlen(args) + 1;
326 memcpy(args + l, &esym, sizeof(esym));
327 l += sizeof(esym);
328
329 /*
330 * Tell the kernel we're an OpenFirmware system.
331 */
332 machine_tag = SPARC_MACHINE_OPENFIRMWARE;
333 memcpy(args + l, &machine_tag, sizeof(machine_tag));
334 l += sizeof(machine_tag);
335
336 /*
337 * Since we don't need the boot string (we can get it from /chosen)
338 * we won't pass it in. Just pass in esym and magic #
339 */
340 newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
341 newargs[1] = (long)esym;
342 newargs[2] = (long)ssym;
343 newargs[3] = (long)(void*)bootinfo;
344 args = (char *)newargs;
345 l = sizeof(newargs);
346
347 /* if -D is set then pause in the PROM. */
348 if (debug > 1) callrom();
349
350 /*
351 * Jump directly to the kernel. Solaris kernel and Sun PROM
352 * flash updates expect ROMP vector in %o0, so we do. Format
353 * of other parameters and their order reflect OF_chain()
354 * symantics since this is what older NetBSD kernels rely on.
355 * (see sparc64/include/bootinfo.h for specification).
356 */
357 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
358 (long)args, (long)l, (long)ofw, (long)ofw,
359 (void*)marks[MARK_ENTRY]));
360 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
361 (long)ofw);
362 printf("Returned from kernel entry point!\n");
363 }
364
365 static void
start_kernel(char * kernel,char * bootline,void * ofw,int isfloppy,int boothowto)366 start_kernel(char *kernel, char *bootline, void *ofw, int isfloppy,
367 int boothowto)
368 {
369 int fd;
370 u_long marks[MARK_MAX] = {0};
371 int flags = LOAD_ALL;
372
373 if (isfloppy)
374 flags &= ~LOAD_BACKWARDS;
375
376 /*
377 * First, load headers using default allocator and check whether kernel
378 * entry address matches kernel text load address. If yes, this is the
379 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
380 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
381 */
382 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
383 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
384 if (COMPAT_BOOT(marks) || compatmode) {
385 (void)printf("[c] ");
386 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
387 } else {
388 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
389 }
390 (void)printf("Loading %s: ", kernel);
391
392 if (fdloadfile(fd, marks, flags) != -1) {
393 close(fd);
394 jump_to_kernel(marks, kernel, bootline, ofw, boothowto);
395 }
396 }
397 (void)printf("Failed to load '%s'.\n", kernel);
398 }
399
400 static void
help(void)401 help(void)
402 {
403 printf( "enter a special command\n"
404 " halt\n"
405 " exit\n"
406 " to return to OpenFirmware\n"
407 " ?\n"
408 " help\n"
409 " to display this message\n"
410 "or a boot specification:\n"
411 " [device] [kernel] [options]\n"
412 "\n"
413 "for example:\n"
414 " disk:a netbsd -s\n");
415 }
416
417 static void
do_config_command(const char * cmd,char * arg)418 do_config_command(const char *cmd, char *arg)
419 {
420 DPRINTF(("do_config_command: %s\n", cmd));
421 if (strcmp(cmd, "bootpartition") == 0) {
422 char *c;
423
424 DPRINTF(("switching boot partition to %s from %s\n",
425 arg, bootdev));
426 c = strrchr(bootdev, ':');
427 if (!c) return;
428 if (c[1] == 0) return;
429 if (strlen(arg) > strlen(c)) return;
430 strcpy(c, arg);
431 DPRINTF(("new boot device: %s\n", bootdev));
432 bootinfo_pass_bootdev = true;
433 }
434 }
435
436 static void
check_boot_config(void)437 check_boot_config(void)
438 {
439 if (!root_fs_quickseekable)
440 return;
441
442 perform_bootcfg(BOOTCFG_FILENAME, &do_config_command, 32768);
443 }
444
445 void
main(void * ofw)446 main(void *ofw)
447 {
448 int boothowto, i = 0, isfloppy, kboothowto;
449
450 char kernel[PROM_MAX_PATH];
451 char bootline[PROM_MAX_PATH];
452
453 /* Initialize OpenFirmware */
454 romp = ofw;
455 prom_init();
456
457 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
458
459 /* Figure boot arguments */
460 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
461 kboothowto = boothowto =
462 bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
463 isfloppy = bootdev_isfloppy(bootdev);
464
465 for (;; *kernel = '\0') {
466 if (boothowto & RB_ASKNAME) {
467 char cmdline[PROM_MAX_PATH];
468
469 printf("Boot: ");
470 kgets(cmdline, sizeof(cmdline));
471
472 if (!strcmp(cmdline,"exit") ||
473 !strcmp(cmdline,"halt")) {
474 prom_halt();
475 } else if (!strcmp(cmdline, "?") ||
476 !strcmp(cmdline, "help")) {
477 help();
478 continue;
479 }
480
481 boothowto = bootoptions(cmdline, bootdev, kernel,
482 bootline);
483 boothowto |= RB_ASKNAME;
484 i = 0;
485 }
486
487 if (*kernel == '\0') {
488 if (kernelnames[i] == NULL) {
489 boothowto |= RB_ASKNAME;
490 continue;
491 }
492 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
493 } else if (i == 0) {
494 /*
495 * Kernel name was passed via command line -- ask user
496 * again if requested image fails to boot.
497 */
498 boothowto |= RB_ASKNAME;
499 }
500
501 check_boot_config();
502 start_kernel(kernel, bootline, ofw, isfloppy, kboothowto);
503
504 /*
505 * Try next name from kernel name list if not in askname mode,
506 * enter askname on reaching list's end.
507 */
508 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
509 printf(": trying %s...\n", kernelnames[i]);
510 } else {
511 printf("\n");
512 boothowto |= RB_ASKNAME;
513 }
514 }
515
516 (void)printf("Boot failed! Exiting to the Firmware.\n");
517 prom_halt();
518 }
519