xref: /illumos-gate/usr/src/uts/sparc/os/bootops.c (revision b2b3ca14)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Definitions of interfaces that provide services from the secondary
30  * boot program to its clients (primarily Solaris, krtld, kmdb and their
31  * successors.) This interface replaces the bootops (BOP) implementation
32  * as the interface to be called by boot clients.
33  *
34  */
35 
36 #include <sys/types.h>
37 #include <sys/systm.h>
38 #include <sys/reboot.h>
39 #include <sys/param.h>
40 #include <sys/varargs.h>
41 #include <sys/obpdefs.h>
42 #include <sys/promimpl.h>
43 #include <sys/prom_plat.h>
44 #include <sys/bootconf.h>
45 #include <sys/bootstat.h>
46 #include <sys/kobj_impl.h>
47 
48 struct bootops *bootops;
49 struct bootops kbootops;
50 
51 pnode_t chosennode;
52 
53 #define	FAKE_ROOT	(pnode_t)1
54 
55 struct fakeprop {
56 	char	*bootname;
57 	pnode_t	promnode;
58 	char	*promname;
59 } fakeprops[] = {
60 	{ "mfg-name", FAKE_ROOT, "name" },
61 	{ NULL, 0, NULL }
62 };
63 
64 static void
65 fakelook_init(void)
66 {
67 	struct fakeprop *fpp = fakeprops;
68 
69 	while (fpp->bootname != NULL) {
70 		switch (fpp->promnode) {
71 		case FAKE_ROOT:
72 			fpp->promnode = prom_rootnode();
73 			break;
74 		}
75 		fpp++;
76 	}
77 }
78 
79 static struct fakeprop *
80 fakelook(const char *prop)
81 {
82 	struct fakeprop *fpp = fakeprops;
83 
84 	while (fpp->bootname != NULL) {
85 		if (strcmp(prop, fpp->bootname) == 0)
86 			return (fpp);
87 		fpp++;
88 	}
89 	return (NULL);
90 }
91 
92 ihandle_t bfs_ih = OBP_BADNODE;
93 ihandle_t afs_ih = OBP_BADNODE;
94 
95 void
96 bop_init(void)
97 {
98 	chosennode = prom_chosennode();
99 
100 	fakelook_init();
101 
102 	/* fake bootops - it needs to point to non-NULL */
103 	bootops = &kbootops;
104 }
105 
106 #define	MAXPROMFD	16
107 
108 static ihandle_t prom_ihs[MAXPROMFD];
109 int filter_etc = 1;
110 
111 /*
112  * Implementation of the "open" boot service.
113  */
114 /*ARGSUSED*/
115 int
116 bop_open(const char *name, int flags)
117 {
118 	int fd = -1, layered;
119 	ihandle_t ih;
120 
121 	/*
122 	 * Only look underneath archive for /etc files
123 	 */
124 	layered = filter_etc ?
125 	    strncmp(name, "/etc", sizeof ("/etc") - 1) == 0 : 1;
126 
127 	if (afs_ih != OBP_BADNODE) {
128 		ih = afs_ih;
129 		fd = prom_fopen(ih, (char *)name);
130 		if (fd == -1 && !layered)
131 			return (BOOT_SVC_FAIL);
132 	}
133 	if (fd == -1 && bfs_ih != OBP_BADNODE) {
134 		ih = bfs_ih;
135 		fd = prom_fopen(ih, (char *)name);
136 	}
137 	if (fd == -1)
138 		return (BOOT_SVC_FAIL);
139 	ASSERT(fd < MAXPROMFD);
140 	ASSERT(prom_ihs[fd] == 0);
141 	prom_ihs[fd] = ih;
142 	return (fd);
143 }
144 
145 static void
146 spinner(void)
147 {
148 	static int pos;
149 	static char ind[] = "|/-\\";	/* that's entertainment? */
150 	static int blks_read;
151 
152 	if ((blks_read++ & 0x3) == 0)
153 		prom_printf("%c\b", ind[pos++ & 3]);
154 }
155 
156 /*
157  * Implementation of the "read" boot service.
158  */
159 int
160 bop_read(int fd, caddr_t buf, size_t size)
161 {
162 	ASSERT(prom_ihs[fd] != 0);
163 	spinner();
164 	return (prom_fread(prom_ihs[fd], fd, buf, size));
165 }
166 
167 /*
168  * Implementation of the "seek" boot service.
169  */
170 int
171 bop_seek(int fd, off_t off)
172 {
173 	ASSERT(prom_ihs[fd] != 0);
174 	return (prom_fseek(prom_ihs[fd], fd, off));
175 }
176 
177 /*
178  * Implementation of the "close" boot service.
179  */
180 int
181 bop_close(int fd)
182 {
183 	ASSERT(prom_ihs[fd] != 0);
184 	prom_fclose(prom_ihs[fd], fd);
185 	prom_ihs[fd] = 0;
186 	return (0);
187 }
188 
189 /*
190  * Simple temp memory allocator
191  *
192  * >PAGESIZE allocations are gotten directly from prom at bighand
193  * smaller ones are satisfied from littlehand, which does a
194  *  1 page bighand allocation when it runs out of memory
195  */
196 static	caddr_t bighand = (caddr_t)BOOTTMPBASE;
197 static	caddr_t littlehand = (caddr_t)BOOTTMPBASE;
198 
199 #define	NTMPALLOC	128
200 
201 static	caddr_t temp_base[NTMPALLOC];
202 static	size_t	temp_size[NTMPALLOC];
203 static	int temp_indx;
204 
205 #if defined(C_OBP)
206 void	cobp_free_mem(caddr_t, size_t);
207 #endif	/* C_OBP */
208 
209 
210 /*
211  * temporary memory storage until bop_tmp_freeall is called
212  * (after the kernel heap is initialized)
213  */
214 caddr_t
215 bop_temp_alloc(size_t size, int align)
216 {
217 	caddr_t ret;
218 
219 	/*
220 	 * OBP allocs 10MB to boot, which is where virthint = 0
221 	 * memory was allocated from.  Without boot, we allocate
222 	 * from BOOTTMPBASE and free when we're ready to take
223 	 * the machine from OBP
224 	 */
225 	if (size < PAGESIZE) {
226 		size_t left =
227 		    ALIGN(littlehand, PAGESIZE) - (uintptr_t)littlehand;
228 
229 		size = roundup(size, MAX(align, 8));
230 		if (size <= left) {
231 			ret = littlehand;
232 			littlehand += size;
233 			return (ret);
234 		}
235 		littlehand = bighand + size;
236 	}
237 	size = roundup(size, PAGESIZE);
238 	ret = prom_alloc(bighand, size, align);
239 	if (ret == NULL)
240 		prom_panic("boot temp overflow");
241 	bighand += size;
242 
243 	/* log it for bop_fini() */
244 	temp_base[temp_indx] = ret;
245 	temp_size[temp_indx] = size;
246 	if (++temp_indx == NTMPALLOC)
247 		prom_panic("out of bop temp space");
248 
249 	return (ret);
250 }
251 
252 void
253 bop_temp_freeall(void)
254 {
255 	int i;
256 
257 	/*
258 	 * We have to call prom_free() with the same args
259 	 * as we used in prom_alloc()
260 	 */
261 	for (i = 0; i < NTMPALLOC; i++) {
262 		if (temp_base[i] == NULL)
263 			break;
264 #if !defined(C_OBP)
265 		prom_free(temp_base[i], temp_size[i]);
266 #else	/* !C_OBP */
267 		cobp_free_mem(temp_base[i], temp_size[i]);
268 #endif	/* !C_OBP */
269 	}
270 }
271 
272 
273 /*
274  * Implementation of the "alloc" boot service.
275  */
276 caddr_t
277 bop_alloc(caddr_t virthint, size_t size, int align)
278 {
279 	if (virthint == NULL)
280 		return (bop_temp_alloc(size, align));
281 	return (prom_alloc(virthint, size, align));
282 }
283 
284 
285 /*
286  * Similar to bop_alloc functionality except that
287  * it will try to breakup into PAGESIZE chunk allocations
288  * if the original single chunk request failed.
289  * This routine does not guarantee physical contig
290  * allocation.
291  */
292 caddr_t
293 bop_alloc_chunk(caddr_t virthint, size_t size, int align)
294 {
295 	caddr_t ret;
296 	size_t chunksz;
297 
298 	if (virthint == NULL)
299 		return (bop_temp_alloc(size, align));
300 
301 	if ((ret = prom_alloc(virthint, size, align)))
302 		return (ret);
303 
304 	/*
305 	 * Normal request to prom_alloc has failed.
306 	 * We will attempt to satisfy the request by allocating
307 	 * smaller chunks resulting in allocation that
308 	 * will be virtually contiguous but potentially
309 	 * not physically contiguous. There are additional
310 	 * requirements before we want to do this:
311 	 * 1. virthirt must be PAGESIZE aligned.
312 	 * 2. align must not be greater than PAGESIZE
313 	 * 3. size request must be at least PAGESIZE
314 	 * Otherwise, we will revert back to the original
315 	 * bop_alloc behavior i.e. return failure.
316 	 */
317 	if (P2PHASE_TYPED(virthint, PAGESIZE, size_t) != 0 ||
318 	    align > PAGESIZE || size < PAGESIZE)
319 		return (ret);
320 
321 	/*
322 	 * Now we will break up the allocation
323 	 * request in smaller chunks that are
324 	 * always PAGESIZE aligned.
325 	 */
326 	ret = virthint;
327 	chunksz = P2ALIGN((size >> 1), PAGESIZE);
328 	chunksz = MAX(chunksz, PAGESIZE);
329 
330 	while (size) {
331 		do {
332 			/*LINTED E_FUNC_SET_NOT_USED*/
333 			caddr_t res;
334 			if ((res = prom_alloc(virthint, chunksz,
335 			    PAGESIZE))) {
336 				ASSERT(virthint == res);
337 				break;
338 			}
339 
340 			chunksz >>= 1;
341 			chunksz = P2ALIGN(chunksz, PAGESIZE);
342 		} while (chunksz >= PAGESIZE);
343 
344 		if (chunksz < PAGESIZE)
345 			/* Can't really happen.. */
346 			prom_panic("bop_alloc_chunk failed");
347 
348 		virthint += chunksz;
349 		size -= chunksz;
350 		if (size < chunksz)
351 			chunksz = size;
352 	}
353 	return (ret);
354 }
355 
356 
357 /*
358  * Implementation of the "alloc_virt" boot service
359  */
360 caddr_t
361 bop_alloc_virt(caddr_t virt, size_t size)
362 {
363 	return (prom_claim_virt(size, virt));
364 }
365 
366 /*
367  * Implementation of the "free" boot service.
368  */
369 /*ARGSUSED*/
370 void
371 bop_free(caddr_t virt, size_t size)
372 {
373 	prom_free(virt, size);
374 }
375 
376 
377 
378 /*
379  * Implementation of the "getproplen" boot service.
380  */
381 /*ARGSUSED*/
382 int
383 bop_getproplen(const char *name)
384 {
385 	struct fakeprop *fpp;
386 	pnode_t node;
387 	char *prop;
388 
389 	fpp = fakelook(name);
390 	if (fpp != NULL) {
391 		node = fpp->promnode;
392 		prop = fpp->promname;
393 	} else {
394 		node = chosennode;
395 		prop = (char *)name;
396 	}
397 	return (prom_getproplen(node, prop));
398 }
399 
400 /*
401  * Implementation of the "getprop" boot service.
402  */
403 /*ARGSUSED*/
404 int
405 bop_getprop(const char *name, void *value)
406 {
407 	struct fakeprop *fpp;
408 	pnode_t node;
409 	char *prop;
410 
411 	fpp = fakelook(name);
412 	if (fpp != NULL) {
413 		node = fpp->promnode;
414 		prop = fpp->promname;
415 	} else {
416 		node = chosennode;
417 		prop = (char *)name;
418 	}
419 	return (prom_getprop(node, prop, value));
420 }
421 
422 /*
423  * Implementation of the "print" boot service.
424  */
425 /*ARGSUSED*/
426 void
427 bop_printf(void *ops, const char *fmt, ...)
428 {
429 	va_list adx;
430 
431 	va_start(adx, fmt);
432 	prom_vprintf(fmt, adx);
433 	va_end(adx);
434 }
435 
436 /*
437  * Special routine for kmdb
438  */
439 void
440 bop_putsarg(const char *fmt, char *arg)
441 {
442 	prom_printf(fmt, arg);
443 }
444 
445 /*
446  * panic for krtld only
447  */
448 void
449 bop_panic(const char *s)
450 {
451 	prom_panic((char *)s);
452 }
453 
454 /*
455  * Implementation of the "mount" boot service.
456  *
457  */
458 /*ARGSUSED*/
459 int
460 bop_mountroot(void)
461 {
462 	(void) prom_getprop(chosennode, "bootfs", (caddr_t)&bfs_ih);
463 	(void) prom_getprop(chosennode, "archfs", (caddr_t)&afs_ih);
464 	return ((bfs_ih == -1 && afs_ih == -1) ? BOOT_SVC_FAIL : BOOT_SVC_OK);
465 }
466 
467 /*
468  * Implementation of the "unmountroot" boot service.
469  */
470 /*ARGSUSED*/
471 int
472 bop_unmountroot(void)
473 {
474 
475 	if (bfs_ih != OBP_BADNODE) {
476 		(void) prom_close(bfs_ih);
477 		bfs_ih = OBP_BADNODE;
478 	}
479 	if (afs_ih != OBP_BADNODE) {
480 		(void) prom_close(afs_ih);
481 		afs_ih = OBP_BADNODE;
482 	}
483 	return (BOOT_SVC_OK);
484 }
485 
486 /*
487  * Implementation of the "fstat" boot service.
488  */
489 int
490 bop_fstat(int fd, struct bootstat *st)
491 {
492 	ASSERT(prom_ihs[fd] != 0);
493 	return (prom_fsize(prom_ihs[fd], fd, (size_t *)&st->st_size));
494 }
495 
496 int
497 boot_compinfo(int fd, struct compinfo *cb)
498 {
499 	ASSERT(prom_ihs[fd] != 0);
500 	return (prom_compinfo(prom_ihs[fd], fd,
501 	    &cb->iscmp, &cb->fsize, &cb->blksize));
502 }
503 
504 void
505 bop_free_archive(void)
506 {
507 	char archive[OBP_MAXPATHLEN];
508 	pnode_t arph;
509 	uint32_t arbase, arsize, alloc_size;
510 
511 	/*
512 	 * If the ramdisk will eventually be root, or we weren't
513 	 * booted via the archive, then nothing to do here
514 	 */
515 	if (root_is_ramdisk == B_TRUE ||
516 	    prom_getprop(chosennode, "bootarchive", archive) == -1)
517 		return;
518 	arph = prom_finddevice(archive);
519 	if (arph == -1 ||
520 	    prom_getprop(arph, OBP_ALLOCSIZE, (caddr_t)&alloc_size) == -1 ||
521 	    prom_getprop(arph, OBP_SIZE, (caddr_t)&arsize) == -1 ||
522 	    prom_getprop(arph, OBP_ADDRESS, (caddr_t)&arbase) == -1)
523 		prom_panic("can't free boot archive");
524 
525 #if !defined(C_OBP)
526 	if (alloc_size == 0)
527 		prom_free((caddr_t)(uintptr_t)arbase, arsize);
528 	else {
529 		uint32_t arend = arbase + arsize;
530 
531 		while (arbase < arend) {
532 			prom_free((caddr_t)(uintptr_t)arbase,
533 			    MIN(alloc_size, arend - arbase));
534 			arbase += alloc_size;
535 		}
536 	}
537 #else	/* !C_OBP */
538 	cobp_free_mem((caddr_t)(uintptr_t)arbase, arsize);
539 #endif	/* !C_OBP */
540 }
541 
542 #if defined(C_OBP)
543 /*
544  * Blech.  The C proms have a bug when freeing areas that cross
545  * page sizes, so we have to break up the free into sections
546  * bounded by the various pagesizes.
547  */
548 void
549 cobp_free_mem(caddr_t base, size_t size)
550 {
551 	int i;
552 	size_t len, pgsz;
553 
554 	/*
555 	 * Large pages only used when size > 512k
556 	 */
557 	if (size < MMU_PAGESIZE512K ||
558 	    ((uintptr_t)base & MMU_PAGEOFFSET512K) != 0) {
559 		prom_free(base, size);
560 		return;
561 	}
562 	for (i = 3; i >= 0; i--) {
563 		pgsz = page_get_pagesize(i);
564 		if (size < pgsz)
565 			continue;
566 		len = size & ~(pgsz - 1);
567 		prom_free(base, len);
568 		base += len;
569 		size -= len;
570 	}
571 }
572 #endif	/* C_OBP */
573 
574 
575 /*
576  * Implementation of the "enter_mon" boot service.
577  */
578 void
579 bop_enter_mon(void)
580 {
581 	prom_enter_mon();
582 }
583 
584 /*
585  * free elf info allocated by booter
586  */
587 void
588 bop_free_elf(void)
589 {
590 	uint32_t eadr;
591 	uint32_t esize;
592 	extern Addr dynseg;
593 	extern size_t dynsize;
594 
595 	if (bop_getprop("elfheader-address", (caddr_t)&eadr) == -1 ||
596 	    bop_getprop("elfheader-length", (caddr_t)&esize) == -1)
597 		prom_panic("missing elfheader");
598 	prom_free((caddr_t)(uintptr_t)eadr, roundup(esize, PAGESIZE));
599 
600 	prom_free((caddr_t)(uintptr_t)dynseg, roundup(dynsize, PAGESIZE));
601 }
602 
603 
604 /* Simple message to indicate that the bootops pointer has been zeroed */
605 #ifdef DEBUG
606 int bootops_gone_on = 0;
607 #define	BOOTOPS_GONE() \
608 	if (bootops_gone_on) \
609 		prom_printf("The bootops vec is zeroed now!\n");
610 #else
611 #define	BOOTOPS_GONE()
612 #endif	/* DEBUG */
613 
614 void
615 bop_fini(void)
616 {
617 	bop_free_archive();
618 	(void) bop_unmountroot();
619 	bop_free_elf();
620 	bop_temp_freeall();
621 
622 	bootops = (struct bootops *)NULL;
623 	BOOTOPS_GONE();
624 }
625