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