boot.c revision 1.16 1 /* $NetBSD: boot.c,v 1.16 2007/09/19 13:52:02 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/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
93 int debug = 0;
94 int compatmode = 0;
95
96 #if 0
97 static void
98 prom2boot(char *dev)
99 {
100 char *cp, *lp = 0;
101 int handle;
102 char devtype[16];
103
104 for (cp = dev; *cp; cp++)
105 if (*cp == ':')
106 lp = cp;
107 if (!lp)
108 lp = cp;
109 *lp = 0;
110 }
111 #endif
112
113 static int
114 bootoptions(const char *ap, char *loaddev, char *kernel, char *options)
115 {
116 int v = 0;
117 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL;
118 const char *path;
119 char partition;
120
121 *kernel = '\0';
122 *options = '\0';
123
124 if (ap == NULL) {
125 return (0);
126 }
127
128 while (*ap == ' ') {
129 ap++;
130 }
131
132 if (*ap != '-') {
133 start1 = ap;
134 while (*ap != '\0' && *ap != ' ') {
135 ap++;
136 }
137 end1 = ap;
138
139 while (*ap != '\0' && *ap == ' ') {
140 ap++;
141 }
142
143 if (*ap != '-') {
144 start2 = ap;
145 while (*ap != '\0' && *ap != ' ') {
146 ap++;
147 }
148 end2 = ap;
149 while (*ap != '\0' && *ap == ' ') {
150 ap++;
151 }
152 }
153 }
154 if (end2 == start2) {
155 start2 = end2 = NULL;
156 }
157 if (end1 == start1) {
158 start1 = end1 = NULL;
159 }
160
161 if (start1 == NULL) {
162 /* only options */
163 } else if (start2 == NULL) {
164 memcpy(kernel, start1, (end1 - start1));
165 kernel[end1 - start1] = '\0';
166 path = filename(kernel, &partition);
167 if (path == NULL) {
168 strcpy(loaddev, kernel);
169 kernel[0] = '\0';
170 } else if (path != kernel) {
171 /* copy device part */
172 memcpy(loaddev, kernel, path-kernel);
173 loaddev[path-kernel] = '\0';
174 /* and kernel path */
175 strcpy(kernel, path);
176 }
177 } else {
178 memcpy(loaddev, start1, (end1-start1));
179 loaddev[end1-start1] = '\0';
180 memcpy(kernel, start2, (end2 - start2));
181 kernel[end2 - start2] = '\0';
182 }
183
184 strcpy(options, ap);
185 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
186 BOOT_FLAG(*ap, v);
187 switch(*ap++) {
188 case 'D':
189 debug = 2;
190 break;
191 case 'C':
192 compatmode = 1;
193 break;
194 default:
195 break;
196 }
197 }
198
199 if (((v & RB_KDB) != 0) && (debug == 0)) {
200 debug = 1;
201 }
202
203 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n",
204 loaddev, kernel, options));
205 return (v);
206 }
207
208 /*
209 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
210 * ksyms information unless it resides in a dedicated memory allocated from
211 * PROM and aligned on NBPG boundary. This is because the kernels calculate
212 * their ends on their own, they use address of 'end[]' reference which follows
213 * text segment. Ok, allocate some memory from PROM and copy symbol information
214 * over there.
215 */
216 static void
217 ksyms_copyout(void **ssym, void **esym)
218 {
219 void *addr;
220 int kssize = (int)(long)(*esym - *ssym + 1);
221
222 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
223 *ssym, *esym, kssize));
224
225 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
226 panic("ksyms_copyout(): no space for symbol table");
227 }
228
229 memcpy(addr, *ssym, kssize);
230 *ssym = addr;
231 *esym = addr + kssize - 1;
232
233 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
234 }
235
236 /*
237 * Prepare boot information and jump directly to the kernel.
238 */
239 static void
240 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw)
241 {
242 extern char end[];
243 int l, machine_tag;
244 long newargs[4];
245 void *ssym, *esym;
246 vaddr_t bootinfo;
247 struct btinfo_symtab bi_sym;
248 struct btinfo_kernend bi_kend;
249 char *cp;
250 char bootline[PROM_MAX_PATH * 2];
251
252 /* Compose kernel boot line. */
253 strncpy(bootline, kernel, sizeof(bootline));
254 cp = bootline + strlen(bootline);
255 if (*args) {
256 *cp++ = ' ';
257 strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
258 }
259 *cp = 0; args = bootline;
260
261 /* Record symbol information in the bootinfo. */
262 bootinfo = bi_init(marks[MARK_END]);
263 bi_sym.nsym = marks[MARK_NSYM];
264 bi_sym.ssym = marks[MARK_SYM];
265 bi_sym.esym = marks[MARK_END];
266 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
267 bi_kend.addr= bootinfo + BOOTINFO_SIZE;
268 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
269 sparc64_finalize_tlb(marks[MARK_DATA]);
270 sparc64_bi_add();
271
272 ssym = (void*)(long)marks[MARK_SYM];
273 esym = (void*)(long)marks[MARK_END];
274
275 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
276
277 /* Adjust ksyms pointers, if needed. */
278 if (COMPAT_BOOT(marks) || compatmode) {
279 ksyms_copyout(&ssym, &esym);
280 }
281
282 freeall();
283 /*
284 * When we come in args consists of a pointer to the boot
285 * string. We need to fix it so it takes into account
286 * other params such as romp.
287 */
288
289 /*
290 * Stash pointer to end of symbol table after the argument
291 * strings.
292 */
293 l = strlen(args) + 1;
294 bcopy(&esym, args + l, sizeof(esym));
295 l += sizeof(esym);
296
297 /*
298 * Tell the kernel we're an OpenFirmware system.
299 */
300 machine_tag = SPARC_MACHINE_OPENFIRMWARE;
301 bcopy(&machine_tag, args + l, sizeof(machine_tag));
302 l += sizeof(machine_tag);
303
304 /*
305 * Since we don't need the boot string (we can get it from /chosen)
306 * we won't pass it in. Just pass in esym and magic #
307 */
308 newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
309 newargs[1] = (long)esym;
310 newargs[2] = (long)ssym;
311 newargs[3] = (long)(void*)bootinfo;
312 args = (char *)newargs;
313 l = sizeof(newargs);
314
315 /* if -D is set then pause in the PROM. */
316 if (debug > 1) callrom();
317
318 /*
319 * Jump directly to the kernel. Solaris kernel and Sun PROM
320 * flash updates expect ROMP vector in %o0, so we do. Format
321 * of other parameters and their order reflect OF_chain()
322 * symantics since this is what older NetBSD kernels rely on.
323 * (see sparc64/include/bootinfo.h for specification).
324 */
325 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
326 (long)args, (long)l, (long)ofw, (long)ofw,
327 (void*)marks[MARK_ENTRY]));
328 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
329 (long)ofw);
330 printf("Returned from kernel entry point!\n");
331 }
332
333 static void
334 start_kernel(char *kernel, char *bootline, void *ofw)
335 {
336 int fd;
337 u_long marks[MARK_MAX];
338
339 /*
340 * First, load headers using default allocator and check whether kernel
341 * entry address matches kernel text load address. If yes, this is the
342 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
343 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
344 */
345 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
346 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
347 if (COMPAT_BOOT(marks) || compatmode) {
348 (void)printf("[c] ");
349 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
350 } else {
351 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
352 }
353 (void)printf("Loading %s: ", kernel);
354
355 if (fdloadfile(fd, marks, LOAD_ALL) != -1) {
356 jump_to_kernel(marks, kernel, bootline, ofw);
357 }
358 }
359 (void)printf("Failed to load '%s'.\n", kernel);
360 }
361
362 static void
363 help(void)
364 {
365 printf( "enter a special command\n"
366 " halt\n"
367 " exit\n"
368 " to return to OpenFirmware\n"
369 " ?\n"
370 " help\n"
371 " to display this message\n"
372 "or a boot specification:\n"
373 " [device] [kernel] [options]\n"
374 "\n"
375 "for example:\n"
376 " disk:a netbsd -s\n");
377 }
378
379 void
380 main(void *ofw)
381 {
382 int boothowto, i = 0;
383
384 char kernel[PROM_MAX_PATH];
385 char bootline[PROM_MAX_PATH];
386
387 /* Initialize OpenFirmware */
388 romp = ofw;
389 prom_init();
390
391 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
392 DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date));
393
394 /* Figure boot arguments */
395 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
396 boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
397
398 for (;; *kernel = '\0') {
399 if (boothowto & RB_ASKNAME) {
400 char *cp, cmdline[PROM_MAX_PATH];
401
402 printf("Boot: ");
403 gets(cmdline);
404
405 if (!strcmp(cmdline,"exit") ||
406 !strcmp(cmdline,"halt")) {
407 prom_halt();
408 } else if (!strcmp(cmdline, "?") ||
409 !strcmp(cmdline, "help")) {
410 help();
411 continue;
412 }
413
414 boothowto = bootoptions(cmdline, bootdev, kernel,
415 bootline);
416 boothowto |= RB_ASKNAME;
417 i = 0;
418 }
419
420 if (*kernel == '\0') {
421 if (kernelnames[i] == NULL) {
422 boothowto |= RB_ASKNAME;
423 continue;
424 }
425 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
426 } else if (i == 0) {
427 /*
428 * Kernel name was passed via command line -- ask user
429 * again if requested image fails to boot.
430 */
431 boothowto |= RB_ASKNAME;
432 }
433
434 start_kernel(kernel, bootline, ofw);
435
436 /*
437 * Try next name from kernel name list if not in askname mode,
438 * enter askname on reaching list's end.
439 */
440 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
441 printf(": trying %s...\n", kernelnames[i]);
442 } else {
443 printf("\n");
444 boothowto |= RB_ASKNAME;
445 }
446 }
447
448 (void)printf("Boot failed! Exiting to the Firmware.\n");
449 prom_halt();
450 }
451