1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2019 Peter Tribbble.
25 */
26
27 #include <sys/param.h>
28 #include <sys/fcntl.h>
29 #include <sys/obpdefs.h>
30 #include <sys/reboot.h>
31 #include <sys/promif.h>
32 #include <sys/stat.h>
33 #include <sys/bootvfs.h>
34 #include <sys/platnames.h>
35 #include <sys/salib.h>
36 #include <sys/elf.h>
37 #include <sys/link.h>
38 #include <sys/auxv.h>
39 #include <sys/boot_policy.h>
40 #include <sys/boot_redirect.h>
41 #include <sys/bootconf.h>
42 #include <sys/boot.h>
43 #include "boot_plat.h"
44
45 #define SUCCESS 0
46 #define FAILURE -1
47
48 #define ISSPACE(c) (c == ' ' || c == '\t')
49 #define SKIP_WHITESPC(cp) while (*cp && ISSPACE(*cp)) cp++;
50
51
52 #ifdef DEBUG
53 int debug = 0;
54 #else
55 static const int debug = 0;
56 #endif
57
58 #define dprintf if (debug) printf
59
60 #ifdef DEBUG_LISTS
61 void print_memlist(struct memlist *av);
62 #endif
63
64 extern int (*readfile(int fd, int print))();
65 extern void kmem_init(void);
66 extern void *kmem_alloc(size_t, int);
67 extern void kmem_free(void *, size_t);
68 extern void get_boot_args(char *buf);
69 extern void setup_bootops(void);
70 extern struct bootops bootops;
71 extern void exitto(int (*entrypoint)());
72 extern void exitto64(int (*entrypoint)(), void *bootvec);
73
74 int openfile(char *filename);
75
76 char *default_name;
77 char *default_path;
78
79 int vac; /* virtual address cache type (none == 0) */
80 int is_sun4v; /* sun4u vs. sun4v */
81 int client_isLP64 = 1; /* SPARC clients are always LP64 */
82
83 extern bootplat_defaults_t sun4u_plat_defaults;
84 extern bootplat_defaults_t sun4v_plat_defaults;
85
86 /*
87 * filename is the name of the standalone we're going to execute.
88 */
89 char filename[MAXPATHLEN];
90
91 char * const defname = "kernel/sparcv9/unix";
92
93 /*
94 * We enable the cache by default
95 * but boot -n will leave it alone...
96 * that is, we use whatever state the PROM left it in.
97 */
98 char *mfg_name;
99 int cache_state = 1;
100 char filename2[MAXPATHLEN];
101
102 int boothowto = 0;
103 int verbosemode = 0;
104
105
106 /*
107 * Copy filename and bargs into v2args_buf, which will be exported as the
108 * boot-args boot property. We should probably warn the user if anything gets
109 * cut off.
110 */
111 void
set_client_bootargs(const char * filename,const char * bargs)112 set_client_bootargs(const char *filename, const char *bargs)
113 {
114 int i = 0;
115 const char *s;
116
117 s = filename;
118 while (*s != '\0' && i < V2ARGS_BUF_SZ - 1)
119 v2args_buf[i++] = *s++;
120
121 if (i >= V2ARGS_BUF_SZ - 2) {
122 /* Not enough room for a space and any of bargs. */
123 v2args_buf[i] = '\0';
124 return;
125 }
126
127 v2args_buf[i++] = ' ';
128
129 s = bargs;
130 while (*s != '\0' && i < V2ARGS_BUF_SZ - 1)
131 v2args_buf[i++] = *s++;
132
133 v2args_buf[i] = '\0';
134 }
135
136 /*
137 * The slice redirection file is used on the install CD
138 */
139 static int
read_redirect(char * redirect)140 read_redirect(char *redirect)
141 {
142 int fd;
143 char slicec;
144 size_t nread = 0;
145
146 if ((fd = open(BOOT_REDIRECT, O_RDONLY)) != -1) {
147 /*
148 * Read the character out of the file - this is the
149 * slice to use, in base 36.
150 */
151 nread = read(fd, &slicec, 1);
152 (void) close(fd);
153 if (nread == 1)
154 *redirect++ = slicec;
155 }
156 *redirect = '\0';
157
158 return (nread == 1);
159 }
160
161 void
post_mountroot(char * bootfile,char * redirect)162 post_mountroot(char *bootfile, char *redirect)
163 {
164 int (*go2)();
165 int fd;
166
167 /* Save the bootfile, just in case we need it again */
168 (void) strcpy(filename2, bootfile);
169
170 for (;;) {
171 if (boothowto & RB_ASKNAME) {
172 char tmpname[MAXPATHLEN];
173
174 printf("Enter filename [%s]: ", bootfile);
175 (void) cons_gets(tmpname, sizeof (tmpname));
176 if (tmpname[0] != '\0')
177 (void) strcpy(bootfile, tmpname);
178 }
179
180 if (boothowto & RB_HALT) {
181 printf("Boot halted.\n");
182 prom_enter_mon();
183 }
184
185 if ((fd = openfile(bootfile)) == FAILURE) {
186
187 /*
188 * There are many reasons why this might've
189 * happened .. but one of them is that we're
190 * on the installation CD, and we need to
191 * revector ourselves off to a different partition
192 * of the CD. Check for the redirection file.
193 */
194 if (redirect != NULL &&
195 read_redirect(redirect)) {
196 /* restore bootfile */
197 (void) strcpy(bootfile, filename2);
198 return;
199 /*NOTREACHED*/
200 }
201
202 printf("%s: cannot open %s\n", my_own_name, bootfile);
203 boothowto |= RB_ASKNAME;
204
205 /* restore bootfile */
206 (void) strcpy(bootfile, filename2);
207 continue;
208 }
209
210 if ((go2 = readfile(fd, boothowto & RB_VERBOSE)) !=
211 (int(*)()) -1) {
212 (void) close(fd);
213 } else {
214 printf("boot failed\n");
215 boothowto |= RB_ASKNAME;
216 continue;
217 }
218
219 if (boothowto & RB_HALT) {
220 printf("Boot halted before exit to 0x%p.\n",
221 (void *)go2);
222 prom_enter_mon();
223 }
224
225 my_own_name = bootfile;
226
227 dprintf("Calling exitto64(%p, %p)\n", (void *)go2,
228 (void *)elfbootvecELF64);
229 exitto64(go2, (void *)elfbootvecELF64);
230 }
231 }
232
233 /*ARGSUSED*/
234 static int
boot_open(char * pathname,void * arg)235 boot_open(char *pathname, void *arg)
236 {
237 dprintf("trying '%s'\n", pathname);
238 return (open(pathname, O_RDONLY));
239 }
240
241 /*
242 * Open the given filename, expanding to it's
243 * platform-dependent location if necessary.
244 *
245 * Boot supports OBP and IEEE1275.
246 *
247 * XXX: Move side effects out of this function!
248 */
249 int
openfile(char * filename)250 openfile(char *filename)
251 {
252 static char *fullpath;
253 static int once;
254 int fd;
255
256 if (once == 0) {
257
258 ++once;
259
260 /*
261 * Setup exported 'boot' properties: 'mfg-name'.
262 * XXX: This shouldn't be a side effect of openfile().
263 */
264 if (mfg_name == NULL)
265 mfg_name = get_mfg_name();
266
267 fullpath = (char *)kmem_alloc(MAXPATHLEN, 0);
268 }
269
270 if (*filename == '/') {
271 (void) strcpy(fullpath, filename);
272 fd = boot_open(fullpath, NULL);
273 return (fd);
274 }
275
276 fd = open_platform_file(filename, boot_open, NULL, fullpath);
277 if (fd == -1)
278 return (-1);
279
280 /*
281 * Copy back the name we actually found
282 */
283 (void) strcpy(filename, fullpath);
284 return (fd);
285 }
286
287 /*
288 * Get the boot arguments from the PROM and split it into filename and
289 * options components.
290 *
291 * As per IEEE1275 and boot(8), the boot arguments will have the syntax
292 * "[filename] [-options]". If filename is specified, it is copied into the
293 * first buffer. (Otherwise, the buffer is left alone.) The rest of the string
294 * is copied into the second buffer.
295 */
296 static void
init_bootargs(char * fname_buf,int fname_buf_sz,char * bargs_buf,int bargs_buf_sz)297 init_bootargs(char *fname_buf, int fname_buf_sz, char *bargs_buf,
298 int bargs_buf_sz)
299 {
300 const char *tp = prom_bootargs();
301
302 if (!tp || *tp == '\0') {
303 *bargs_buf = '\0';
304 return;
305 }
306
307 SKIP_WHITESPC(tp);
308
309 /*
310 * If we don't have an option indicator, then we
311 * already have our filename prepended.
312 */
313 if (*tp && *tp != '-') {
314 int i;
315
316 /*
317 * Copy the filename into fname_buf.
318 */
319 for (i = 0; i < fname_buf_sz && *tp && !ISSPACE(*tp); ++i)
320 *fname_buf++ = *tp++;
321
322 if (i >= fname_buf_sz) {
323 printf("boot: boot filename too long!\n");
324 printf("boot halted.\n");
325 prom_enter_mon();
326 /*NOTREACHED*/
327 } else {
328 *fname_buf = '\0';
329 }
330
331 SKIP_WHITESPC(tp);
332 }
333
334 /* The rest of the line is the options. */
335 while (bargs_buf_sz > 1 && *tp) {
336 *bargs_buf++ = *tp++;
337 --bargs_buf_sz;
338 }
339 *bargs_buf = '\0';
340
341 if (bargs_buf_sz == 1) {
342 printf("boot: boot arguments too long!\n");
343 printf("boot halted.\n");
344 prom_enter_mon();
345 /*NOTREACHED*/
346 }
347 }
348
349 boolean_t
is_netdev(char * devpath)350 is_netdev(char *devpath)
351 {
352 pnode_t node = prom_finddevice(devpath);
353 char *options;
354
355 if ((node == OBP_NONODE) || (node == OBP_BADNODE))
356 return (B_FALSE);
357 if (prom_devicetype(node, "network") != 0)
358 return (B_TRUE);
359
360 /*
361 * For Infiniband, network device names will be of the
362 * format XXX/ib@0:port=1,pkey=1234,protocol=ip[,YYY] where
363 * XXX is typically /pci@8,700000/pci@1. The device_type
364 * property will be "ib".
365 */
366 if (prom_devicetype(node, "ib") != 0) {
367 options = prom_path_options(devpath);
368 if (options != NULL) {
369
370 #define SEARCHSTRING ",protocol=ip"
371 #define SEARCHSTRLEN strlen(SEARCHSTRING)
372
373 if (strstr(options, ",protocol=ip,") != NULL)
374 return (B_TRUE);
375 while ((options = strstr(options, SEARCHSTRING)) !=
376 NULL) {
377 char nextc;
378
379 nextc = options[SEARCHSTRLEN];
380 if ((nextc == ',') || (nextc == 0))
381 return (B_TRUE);
382 options += SEARCHSTRLEN;
383 }
384 }
385 }
386 return (B_FALSE);
387 }
388
389 /*
390 * Hook for modifying the OS boot path. This hook allows us to handle
391 * device arguments that the OS can't handle.
392 */
393 void
mangle_os_bootpath(char * bpath)394 mangle_os_bootpath(char *bpath)
395 {
396 pnode_t node;
397 char *stripped_pathname;
398
399 node = prom_finddevice(bpath);
400 if (prom_devicetype(node, "network") == 0)
401 return;
402
403 /*
404 * The OS can't handle network device arguments
405 * eg: boot net:promiscuous,speed=100,duplex=full
406 * So, we remove any argument strings in the device
407 * pathname we hand off to the OS for network devices.
408 *
409 * Internally, within boot, bpath is used to access
410 * the device, but v2path (as the boot property "boot-path")
411 * is the pathname passed to the OS.
412 */
413
414 stripped_pathname = kmem_alloc(OBP_MAXPATHLEN, 0);
415 prom_strip_options(bpath, stripped_pathname);
416 v2path = stripped_pathname;
417 }
418
419 /*
420 * Given the boot path in the native firmware format use
421 * the redirection string to mutate the boot path to the new device.
422 * Fix up the 'v2path' so that it matches the new firmware path.
423 */
424 void
redirect_boot_path(char * bpath,char * redirect)425 redirect_boot_path(char *bpath, char *redirect)
426 {
427 char slicec = *redirect;
428 char *p = bpath + strlen(bpath);
429
430 /*
431 * If the redirection character doesn't fall in this
432 * range, something went horribly wrong.
433 */
434 if (slicec < '0' || slicec > '7') {
435 printf("boot: bad redirection slice '%c'\n", slicec);
436 return;
437 }
438
439 /*
440 * Handle fully qualified OpenBoot pathname.
441 */
442 while (--p >= bpath && *p != '@' && *p != '/')
443 if (*p == ':')
444 break;
445 if (*p++ == ':') {
446 /*
447 * Convert slice number to partition 'letter'.
448 */
449 *p++ = 'a' + slicec - '0';
450 *p = '\0';
451 v2path = bpath;
452 return;
453 }
454 prom_panic("redirect_boot_path: mangled boot path!");
455 }
456
457 void
system_check(void)458 system_check(void)
459 {
460 pnode_t n;
461 char arch[128];
462 size_t len;
463 bootplat_defaults_t *plat_defaults;
464
465 /*
466 * This is a sun4v machine iff the device_type property
467 * exists on the root node and has the value "sun4v".
468 * Some older sunfire proms do not have such a property.
469 */
470 is_sun4v = 0;
471 n = prom_rootnode();
472 len = prom_getproplen(n, "device_type");
473 if (len > 0 && len < sizeof (arch)) {
474 (void) prom_getprop(n, "device_type", arch);
475 arch[len] = '\0';
476 dprintf("device_type=%s\n", arch);
477 if (strcmp(arch, "sun4v") == 0) {
478 is_sun4v = 1;
479 }
480 } else {
481 dprintf("device_type: no such property, len=%d\n", (int)len);
482 }
483
484 /*
485 * Set up defaults per platform
486 */
487 plat_defaults = (is_sun4v) ?
488 &sun4v_plat_defaults : &sun4u_plat_defaults;
489
490 default_name = plat_defaults->plat_defaults_name;
491 default_path = plat_defaults->plat_defaults_path;
492 vac = plat_defaults->plat_defaults_vac;
493
494 dprintf("default_name: %s\n", default_name);
495 dprintf("default_path: %s\n", default_path);
496 dprintf("vac: %d\n", vac);
497 }
498
499 /*
500 * Reads in the standalone (client) program and jumps to it. If this
501 * attempt fails, prints "boot failed" and returns to its caller.
502 *
503 * It will try to determine if it is loading a Unix file by
504 * looking at what should be the magic number. If it makes
505 * sense, it will use it; otherwise it jumps to the first
506 * address of the blocks that it reads in.
507 *
508 * This new boot program will open a file, read the ELF header,
509 * attempt to allocate and map memory at the location at which
510 * the client desires to be linked, and load the program at
511 * that point. It will then jump there.
512 */
513 /*ARGSUSED*/
514 int
main(void * cookie,char ** argv,int argc)515 main(void *cookie, char **argv, int argc)
516 {
517 /*
518 * bpath is the boot device path buffer.
519 * bargs is the boot arguments buffer.
520 */
521 static char bpath[OBP_MAXPATHLEN], bargs[OBP_MAXPATHLEN];
522 boolean_t user_specified_filename;
523
524 prom_init("boot", cookie);
525 fiximp();
526
527 system_check();
528
529 dprintf("\nboot: V%d /boot interface.\n", BO_VERSION);
530 #ifdef HALTBOOT
531 prom_enter_mon();
532 #endif /* HALTBOOT */
533
534 init_memlists();
535
536 #ifdef DEBUG_LISTS
537 dprintf("Physmem avail:\n");
538 if (debug) print_memlist(pfreelistp);
539 dprintf("Virtmem avail:\n");
540 if (debug) print_memlist(vfreelistp);
541 dprintf("Phys installed:\n");
542 if (debug) print_memlist(pinstalledp);
543 prom_enter_mon();
544 #endif /* DEBUG_LISTS */
545
546 /*
547 * Initialize the default filename (exported as "default-name" and
548 * used by kadb).
549 */
550 set_default_filename(defname);
551
552 /*
553 * Parse the arguments ASAP in case there are any flags which may
554 * affect execution.
555 */
556
557 /*
558 * filename is the path to the standalone. Initialize it to the empty
559 * string so we can tell whether the user specified it in the
560 * arguments.
561 */
562 filename[0] = '\0';
563
564 /*
565 * Fetch the boot arguments from the PROM and split the filename off
566 * if it's there.
567 */
568 init_bootargs(filename, sizeof (filename), bargs, sizeof (bargs));
569
570 /*
571 * kadb was delivered as a standalone, and as such, people got used to
572 * typing `boot kadb'. kmdb isn't a standalone - it is loaded by krtld
573 * as just another kernel module. For compatibility, though, when we
574 * see an attempt to `boot kadb' or `boot kmdb', we'll transform that
575 * into a `boot -k' (or equivalent).
576 */
577 if (strcmp(filename, "kmdb") == 0 || strcmp(filename, "kadb") == 0) {
578 boothowto |= RB_KMDB;
579 *filename = '\0'; /* let boot figure out which unix to use */
580 }
581
582 bootflags(bargs, sizeof (bargs));
583
584 user_specified_filename = (filename[0] != '\0');
585
586 /* Fetch the boot path from the PROM. */
587 (void) strncpy(bpath, prom_bootpath(), sizeof (bpath) - 1);
588 bpath[sizeof (bpath) - 1] = '\0';
589
590 dprintf("arch: %s\n", is_sun4v ? "sun4v" : "sun4u");
591 dprintf("bootpath: 0x%p %s\n", (void *)bpath, bpath);
592 dprintf("bootargs: 0x%p %s\n", (void *)bargs, bargs);
593 dprintf("filename: 0x%p %s\n", (void *)filename, filename);
594 dprintf("kernname: 0x%p %s\n", (void *)kernname, kernname);
595
596 /*
597 * *v2path will be exported to the standalone as the boot-path boot
598 * property.
599 */
600 v2path = bpath;
601
602 /*
603 * Our memory lists should be "up" by this time
604 */
605
606 setup_bootops();
607
608 /*
609 * If bpath is a network card, set v2path to a copy of bpath with the
610 * options stripped off.
611 */
612 mangle_os_bootpath(bpath);
613
614 /*
615 * Not necessary on sun4v as nvram is virtual
616 * and kept by the guest manager on the SP.
617 */
618 if (!is_sun4v) {
619 retain_nvram_page();
620 }
621
622 if (bootprog(bpath, bargs, user_specified_filename) == 0) {
623 post_mountroot(filename, NULL);
624 /*NOTREACHED*/
625 }
626
627 return (0);
628 }
629