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