boot.c revision 1.24 1 /* $NetBSD: boot.c,v 1.24 2010/08/25 16:38:04 christos 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/loadfile.h>
48 #include <lib/libkern/libkern.h>
49
50 #include <sys/param.h>
51 #include <sys/reboot.h>
52 #include <sys/disklabel.h>
53 #include <sys/boot_flag.h>
54
55 #include <machine/cpu.h>
56 #include <machine/promlib.h>
57 #include <machine/bootinfo.h>
58
59 #include "boot.h"
60 #include "ofdev.h"
61 #include "openfirm.h"
62
63
64 #define COMPAT_BOOT(marks) (marks[MARK_START] == marks[MARK_ENTRY])
65
66
67 typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3,
68 long ofw);
69
70 /*
71 * Boot device is derived from ROM provided information, or if there is none,
72 * this list is used in sequence, to find a kernel.
73 */
74 const char *kernelnames[] = {
75 "netbsd",
76 "netbsd.gz",
77 "netbsd.old",
78 "netbsd.old.gz",
79 "onetbsd",
80 "onetbsd.gz",
81 "vmunix ",
82 #ifdef notyet
83 "netbsd.pl ",
84 "netbsd.pl.gz ",
85 "netbsd.el ",
86 "netbsd.el.gz ",
87 #endif
88 NULL
89 };
90
91 char bootdev[PROM_MAX_PATH];
92 bool root_fs_quickseekable = true; /* unset for tftp boots */
93 static bool bootinfo_pass_bootdev = false;
94
95 int debug = 0;
96 int compatmode = 0;
97
98 #if 0
99 static void
100 prom2boot(char *dev)
101 {
102 char *cp, *lp = 0;
103 int handle;
104 char devtype[16];
105
106 for (cp = dev; *cp; cp++)
107 if (*cp == ':')
108 lp = cp;
109 if (!lp)
110 lp = cp;
111 *lp = 0;
112 }
113 #endif
114
115 static int
116 bootoptions(const char *ap, char *loaddev, char *kernel, char *options)
117 {
118 int v = 0;
119 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL;
120 const char *path;
121 char partition, *pp;
122
123 *kernel = '\0';
124 *options = '\0';
125
126 if (ap == NULL) {
127 return (0);
128 }
129
130 while (*ap == ' ') {
131 ap++;
132 }
133
134 if (*ap != '-') {
135 start1 = ap;
136 while (*ap != '\0' && *ap != ' ') {
137 ap++;
138 }
139 end1 = ap;
140
141 while (*ap != '\0' && *ap == ' ') {
142 ap++;
143 }
144
145 if (*ap != '-') {
146 start2 = ap;
147 while (*ap != '\0' && *ap != ' ') {
148 ap++;
149 }
150 end2 = ap;
151 while (*ap != '\0' && *ap == ' ') {
152 ap++;
153 }
154 }
155 }
156 if (end2 == start2) {
157 start2 = end2 = NULL;
158 }
159 if (end1 == start1) {
160 start1 = end1 = NULL;
161 }
162
163 if (start1 == NULL) {
164 /* only options */
165 } else if (start2 == NULL) {
166 memcpy(kernel, start1, (end1 - start1));
167 kernel[end1 - start1] = '\0';
168 path = filename(kernel, &partition);
169 if (path == NULL) {
170 strcpy(loaddev, kernel);
171 kernel[0] = '\0';
172 } else if (path != kernel) {
173 /* copy device part */
174 memcpy(loaddev, kernel, path-kernel);
175 loaddev[path-kernel] = '\0';
176 if (partition) {
177 pp = loaddev + strlen(loaddev);
178 pp[0] = ':';
179 pp[1] = partition;
180 pp[2] = '\0';
181 }
182 /* and kernel path */
183 strcpy(kernel, path);
184 }
185 } else {
186 memcpy(loaddev, start1, (end1-start1));
187 loaddev[end1-start1] = '\0';
188 memcpy(kernel, start2, (end2 - start2));
189 kernel[end2 - start2] = '\0';
190 }
191
192 strcpy(options, ap);
193 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
194 BOOT_FLAG(*ap, v);
195 switch(*ap++) {
196 case 'D':
197 debug = 2;
198 break;
199 case 'C':
200 compatmode = 1;
201 break;
202 default:
203 break;
204 }
205 }
206
207 if (((v & RB_KDB) != 0) && (debug == 0)) {
208 debug = 1;
209 }
210
211 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n",
212 loaddev, kernel, options));
213 return (v);
214 }
215
216 /*
217 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
218 * ksyms information unless it resides in a dedicated memory allocated from
219 * PROM and aligned on NBPG boundary. This is because the kernels calculate
220 * their ends on their own, they use address of 'end[]' reference which follows
221 * text segment. Ok, allocate some memory from PROM and copy symbol information
222 * over there.
223 */
224 static void
225 ksyms_copyout(void **ssym, void **esym)
226 {
227 void *addr;
228 int kssize = (int)(long)(*esym - *ssym + 1);
229
230 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
231 *ssym, *esym, kssize));
232
233 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
234 panic("ksyms_copyout(): no space for symbol table");
235 }
236
237 memcpy(addr, *ssym, kssize);
238 *ssym = addr;
239 *esym = addr + kssize - 1;
240
241 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
242 }
243
244 /*
245 * Prepare boot information and jump directly to the kernel.
246 */
247 static void
248 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw)
249 {
250 extern char end[];
251 int l, machine_tag;
252 long newargs[4];
253 void *ssym, *esym;
254 vaddr_t bootinfo;
255 struct btinfo_symtab bi_sym;
256 struct btinfo_kernend bi_kend;
257 char *cp;
258 char bootline[PROM_MAX_PATH * 2];
259
260 /* Compose kernel boot line. */
261 strncpy(bootline, kernel, sizeof(bootline));
262 cp = bootline + strlen(bootline);
263 if (*args) {
264 *cp++ = ' ';
265 strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
266 }
267 *cp = 0; args = bootline;
268
269 /* Record symbol information in the bootinfo. */
270 bootinfo = bi_init(marks[MARK_END]);
271 bi_sym.nsym = marks[MARK_NSYM];
272 bi_sym.ssym = marks[MARK_SYM];
273 bi_sym.esym = marks[MARK_END];
274 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
275 bi_kend.addr= bootinfo + BOOTINFO_SIZE;
276 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
277 if (bootinfo_pass_bootdev) {
278 struct {
279 struct btinfo_common common;
280 char name[256];
281 } info;
282
283 strcpy(info.name, bootdev);
284 bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev)
285 +sizeof(struct btinfo_bootdev));
286 }
287
288 sparc64_finalize_tlb(marks[MARK_DATA]);
289 sparc64_bi_add();
290
291 ssym = (void*)(long)marks[MARK_SYM];
292 esym = (void*)(long)marks[MARK_END];
293
294 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
295
296 /* Adjust ksyms pointers, if needed. */
297 if (COMPAT_BOOT(marks) || compatmode) {
298 ksyms_copyout(&ssym, &esym);
299 }
300
301 freeall();
302 /*
303 * When we come in args consists of a pointer to the boot
304 * string. We need to fix it so it takes into account
305 * other params such as romp.
306 */
307
308 /*
309 * Stash pointer to end of symbol table after the argument
310 * strings.
311 */
312 l = strlen(args) + 1;
313 memcpy(args + l, &esym, sizeof(esym));
314 l += sizeof(esym);
315
316 /*
317 * Tell the kernel we're an OpenFirmware system.
318 */
319 machine_tag = SPARC_MACHINE_OPENFIRMWARE;
320 memcpy(args + l, &machine_tag, sizeof(machine_tag));
321 l += sizeof(machine_tag);
322
323 /*
324 * Since we don't need the boot string (we can get it from /chosen)
325 * we won't pass it in. Just pass in esym and magic #
326 */
327 newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
328 newargs[1] = (long)esym;
329 newargs[2] = (long)ssym;
330 newargs[3] = (long)(void*)bootinfo;
331 args = (char *)newargs;
332 l = sizeof(newargs);
333
334 /* if -D is set then pause in the PROM. */
335 if (debug > 1) callrom();
336
337 /*
338 * Jump directly to the kernel. Solaris kernel and Sun PROM
339 * flash updates expect ROMP vector in %o0, so we do. Format
340 * of other parameters and their order reflect OF_chain()
341 * symantics since this is what older NetBSD kernels rely on.
342 * (see sparc64/include/bootinfo.h for specification).
343 */
344 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
345 (long)args, (long)l, (long)ofw, (long)ofw,
346 (void*)marks[MARK_ENTRY]));
347 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
348 (long)ofw);
349 printf("Returned from kernel entry point!\n");
350 }
351
352 static void
353 start_kernel(char *kernel, char *bootline, void *ofw, int isfloppy)
354 {
355 int fd;
356 u_long marks[MARK_MAX];
357 int flags = LOAD_ALL;
358 if (isfloppy)
359 flags &= ~LOAD_BACKWARDS;
360
361 /*
362 * First, load headers using default allocator and check whether kernel
363 * entry address matches kernel text load address. If yes, this is the
364 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
365 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
366 */
367 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
368 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
369 if (COMPAT_BOOT(marks) || compatmode) {
370 (void)printf("[c] ");
371 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
372 } else {
373 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
374 }
375 (void)printf("Loading %s: ", kernel);
376
377 if (fdloadfile(fd, marks, flags) != -1) {
378 close(fd);
379 jump_to_kernel(marks, kernel, bootline, ofw);
380 }
381 }
382 (void)printf("Failed to load '%s'.\n", kernel);
383 }
384
385 static void
386 help(void)
387 {
388 printf( "enter a special command\n"
389 " halt\n"
390 " exit\n"
391 " to return to OpenFirmware\n"
392 " ?\n"
393 " help\n"
394 " to display this message\n"
395 "or a boot specification:\n"
396 " [device] [kernel] [options]\n"
397 "\n"
398 "for example:\n"
399 " disk:a netbsd -s\n");
400 }
401
402 static void
403 do_config_command(const char *cmd, const char *arg)
404 {
405 DPRINTF(("do_config_command: %s\n", cmd));
406 if (strcmp(cmd, "bootpartition") == 0) {
407 char *c;
408
409 DPRINTF(("switching boot partition to %s from %s\n",
410 arg, bootdev));
411 c = strrchr(bootdev, ':');
412 if (!c) return;
413 if (c[1] == 0) return;
414 if (strlen(arg) > strlen(c)) return;
415 strcpy(c, arg);
416 DPRINTF(("new boot device: %s\n", bootdev));
417 bootinfo_pass_bootdev = true;
418 }
419 }
420
421 static void
422 parse_boot_config(char *cfg, size_t len)
423 {
424 const char *cmd = NULL, *arg = NULL;
425
426 while (len) {
427 if (isspace(*cfg)) {
428 cfg++; len--; continue;
429 }
430 if (*cfg == ';' || *cfg == '#') {
431 while (len && *cfg != '\r' && *cfg != '\n') {
432 cfg++; len--;
433 }
434 continue;
435 }
436 cmd = cfg;
437 while (len && !isspace(*cfg)) {
438 cfg++; len--;
439 }
440 *cfg = 0;
441 if (len > 0) {
442 cfg++; len--;
443 while (isspace(*cfg) && len) {
444 cfg++; len--;
445 }
446 if (len > 0 ) {
447 arg = cfg;
448 while (len && !isspace(*cfg)) {
449 cfg++; len--;
450 }
451 *cfg = 0;
452 }
453 }
454 do_config_command(cmd, arg);
455 if (len > 0) {
456 cfg++; len--;
457 }
458 }
459 }
460
461 static void
462 check_boot_config(void)
463 {
464 int fd, err, off, len;
465 struct stat st;
466 char *bc;
467
468 if (!root_fs_quickseekable) return;
469 DPRINTF(("checking for /boot.cfg...\n"));
470 fd = open("/boot.cfg", 0);
471 if (fd < 0) return;
472 DPRINTF(("found /boot.cfg\n"));
473 if (fstat(fd, &st) == -1 || st.st_size > 32*1024) {
474 close(fd);
475 return;
476 }
477 bc = alloc(st.st_size+1);
478 off = 0;
479 do {
480 len = read(fd, bc+off, 1024);
481 if (len <= 0)
482 break;
483 off += len;
484 } while (len > 0);
485 bc[off] = 0;
486 close(fd);
487
488 parse_boot_config(bc, off);
489 }
490
491 void
492 main(void *ofw)
493 {
494 int boothowto, i = 0, isfloppy;
495
496 char kernel[PROM_MAX_PATH];
497 char bootline[PROM_MAX_PATH];
498
499 /* Initialize OpenFirmware */
500 romp = ofw;
501 prom_init();
502
503 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
504 DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date));
505
506 /* Figure boot arguments */
507 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
508 boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
509 isfloppy = strstr(bootdev, "fd") || strstr(bootdev, "floppy");
510
511 for (;; *kernel = '\0') {
512 if (boothowto & RB_ASKNAME) {
513 char *cp, cmdline[PROM_MAX_PATH];
514
515 printf("Boot: ");
516 gets(cmdline);
517
518 if (!strcmp(cmdline,"exit") ||
519 !strcmp(cmdline,"halt")) {
520 prom_halt();
521 } else if (!strcmp(cmdline, "?") ||
522 !strcmp(cmdline, "help")) {
523 help();
524 continue;
525 }
526
527 boothowto = bootoptions(cmdline, bootdev, kernel,
528 bootline);
529 boothowto |= RB_ASKNAME;
530 i = 0;
531 }
532
533 if (*kernel == '\0') {
534 if (kernelnames[i] == NULL) {
535 boothowto |= RB_ASKNAME;
536 continue;
537 }
538 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
539 } else if (i == 0) {
540 /*
541 * Kernel name was passed via command line -- ask user
542 * again if requested image fails to boot.
543 */
544 boothowto |= RB_ASKNAME;
545 }
546
547 check_boot_config();
548 start_kernel(kernel, bootline, ofw, isfloppy);
549
550 /*
551 * Try next name from kernel name list if not in askname mode,
552 * enter askname on reaching list's end.
553 */
554 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
555 printf(": trying %s...\n", kernelnames[i]);
556 } else {
557 printf("\n");
558 boothowto |= RB_ASKNAME;
559 }
560 }
561
562 (void)printf("Boot failed! Exiting to the Firmware.\n");
563 prom_halt();
564 }
565