xref: /illumos-gate/usr/src/psm/stand/boot/sparc/common/boot_plat.c (revision b666b5bebdde1067e2d0f2b91fd28a5243311a1a)
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
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
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
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
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
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(1M), 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
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
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
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
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
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
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