xref: /illumos-gate/usr/src/uts/sparc/os/bootops.c (revision 67e3a03e)
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 2007 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  * Implementation of the "alloc_virt" boot service
286  */
287 caddr_t
288 bop_alloc_virt(caddr_t virt, size_t size)
289 {
290 	return (prom_claim_virt(size, virt));
291 }
292 
293 /*
294  * Implementation of the "free" boot service.
295  */
296 /*ARGSUSED*/
297 void
298 bop_free(caddr_t virt, size_t size)
299 {
300 	prom_free(virt, size);
301 }
302 
303 
304 
305 /*
306  * Implementation of the "getproplen" boot service.
307  */
308 /*ARGSUSED*/
309 int
310 bop_getproplen(const char *name)
311 {
312 	struct fakeprop *fpp;
313 	pnode_t node;
314 	char *prop;
315 
316 	fpp = fakelook(name);
317 	if (fpp != NULL) {
318 		node = fpp->promnode;
319 		prop = fpp->promname;
320 	} else {
321 		node = chosennode;
322 		prop = (char *)name;
323 	}
324 	return (prom_getproplen(node, prop));
325 }
326 
327 /*
328  * Implementation of the "getprop" boot service.
329  */
330 /*ARGSUSED*/
331 int
332 bop_getprop(const char *name, void *value)
333 {
334 	struct fakeprop *fpp;
335 	pnode_t node;
336 	char *prop;
337 
338 	fpp = fakelook(name);
339 	if (fpp != NULL) {
340 		node = fpp->promnode;
341 		prop = fpp->promname;
342 	} else {
343 		node = chosennode;
344 		prop = (char *)name;
345 	}
346 	return (prom_getprop(node, prop, value));
347 }
348 
349 /*
350  * Implementation of the "print" boot service.
351  */
352 /*ARGSUSED*/
353 void
354 bop_printf(void *ops, const char *fmt, ...)
355 {
356 	va_list adx;
357 
358 	va_start(adx, fmt);
359 	prom_vprintf(fmt, adx);
360 	va_end(adx);
361 }
362 
363 /*
364  * Special routine for kmdb
365  */
366 void
367 bop_putsarg(const char *fmt, char *arg)
368 {
369 	prom_printf(fmt, arg);
370 }
371 
372 /*
373  * panic for krtld only
374  */
375 void
376 bop_panic(const char *s)
377 {
378 	prom_panic((char *)s);
379 }
380 
381 /*
382  * Implementation of the "mount" boot service.
383  *
384  */
385 /*ARGSUSED*/
386 int
387 bop_mountroot(void)
388 {
389 	(void) prom_getprop(chosennode, "bootfs", (caddr_t)&bfs_ih);
390 	(void) prom_getprop(chosennode, "archfs", (caddr_t)&afs_ih);
391 	return ((bfs_ih == -1 && afs_ih == -1) ? BOOT_SVC_FAIL : BOOT_SVC_OK);
392 }
393 
394 /*
395  * Implementation of the "unmountroot" boot service.
396  */
397 /*ARGSUSED*/
398 int
399 bop_unmountroot(void)
400 {
401 
402 	if (bfs_ih != OBP_BADNODE) {
403 		(void) prom_close(bfs_ih);
404 		bfs_ih = OBP_BADNODE;
405 	}
406 	if (afs_ih != OBP_BADNODE) {
407 		(void) prom_close(afs_ih);
408 		afs_ih = OBP_BADNODE;
409 	}
410 	return (BOOT_SVC_OK);
411 }
412 
413 /*
414  * Implementation of the "fstat" boot service.
415  */
416 int
417 bop_fstat(int fd, struct bootstat *st)
418 {
419 	ASSERT(prom_ihs[fd] != 0);
420 	return (prom_fsize(prom_ihs[fd], fd, (size_t *)&st->st_size));
421 }
422 
423 int
424 boot_compinfo(int fd, struct compinfo *cb)
425 {
426 	ASSERT(prom_ihs[fd] != 0);
427 	return (prom_compinfo(prom_ihs[fd], fd,
428 	    &cb->iscmp, &cb->fsize, &cb->blksize));
429 }
430 
431 void
432 bop_free_archive(void)
433 {
434 	char archive[OBP_MAXPATHLEN];
435 	pnode_t arph;
436 	uint32_t arbase, arsize, alloc_size;
437 
438 	/*
439 	 * If the ramdisk will eventually be root, or we weren't
440 	 * booted via the archive, then nothing to do here
441 	 */
442 	if (root_is_ramdisk == B_TRUE ||
443 	    prom_getprop(chosennode, "bootarchive", archive) == -1)
444 		return;
445 	arph = prom_finddevice(archive);
446 	if (arph == -1 ||
447 	    prom_getprop(arph, OBP_ALLOCSIZE, (caddr_t)&alloc_size) == -1 ||
448 	    prom_getprop(arph, OBP_SIZE, (caddr_t)&arsize) == -1 ||
449 	    prom_getprop(arph, OBP_ADDRESS, (caddr_t)&arbase) == -1)
450 		prom_panic("can't free boot archive");
451 
452 #if !defined(C_OBP)
453 	if (alloc_size == 0)
454 		prom_free((caddr_t)(uintptr_t)arbase, arsize);
455 	else {
456 		uint32_t arend = arbase + arsize;
457 
458 		while (arbase < arend) {
459 			prom_free((caddr_t)(uintptr_t)arbase,
460 			    MIN(alloc_size, arend - arbase));
461 			arbase += alloc_size;
462 		}
463 	}
464 #else	/* !C_OBP */
465 	cobp_free_mem((caddr_t)(uintptr_t)arbase, arsize);
466 #endif	/* !C_OBP */
467 }
468 
469 #if defined(C_OBP)
470 /*
471  * Blech.  The C proms have a bug when freeing areas that cross
472  * page sizes, so we have to break up the free into sections
473  * bounded by the various pagesizes.
474  */
475 void
476 cobp_free_mem(caddr_t base, size_t size)
477 {
478 	int i;
479 	size_t len, pgsz;
480 
481 	/*
482 	 * Large pages only used when size > 512k
483 	 */
484 	if (size < MMU_PAGESIZE512K ||
485 	    ((uintptr_t)base & MMU_PAGEOFFSET512K) != 0) {
486 		prom_free(base, size);
487 		return;
488 	}
489 	for (i = 3; i >= 0; i--) {
490 		pgsz = page_get_pagesize(i);
491 		if (size < pgsz)
492 			continue;
493 		len = size & ~(pgsz - 1);
494 		prom_free(base, len);
495 		base += len;
496 		size -= len;
497 	}
498 }
499 #endif	/* C_OBP */
500 
501 
502 /*
503  * Implementation of the "enter_mon" boot service.
504  */
505 void
506 bop_enter_mon(void)
507 {
508 	prom_enter_mon();
509 }
510 
511 /*
512  * free elf info allocated by booter
513  */
514 void
515 bop_free_elf(void)
516 {
517 	uint32_t eadr;
518 	uint32_t esize;
519 	extern Addr dynseg;
520 	extern size_t dynsize;
521 
522 	if (bop_getprop("elfheader-address", (caddr_t)&eadr) == -1 ||
523 	    bop_getprop("elfheader-length", (caddr_t)&esize) == -1)
524 		prom_panic("missing elfheader");
525 	prom_free((caddr_t)(uintptr_t)eadr, roundup(esize, PAGESIZE));
526 
527 	prom_free((caddr_t)(uintptr_t)dynseg, roundup(dynsize, PAGESIZE));
528 }
529 
530 
531 /* Simple message to indicate that the bootops pointer has been zeroed */
532 #ifdef DEBUG
533 int bootops_gone_on = 0;
534 #define	BOOTOPS_GONE() \
535 	if (bootops_gone_on) \
536 		prom_printf("The bootops vec is zeroed now!\n");
537 #else
538 #define	BOOTOPS_GONE()
539 #endif	/* DEBUG */
540 
541 void
542 bop_fini(void)
543 {
544 	bop_free_archive();
545 	(void) bop_unmountroot();
546 	bop_free_elf();
547 	bop_temp_freeall();
548 
549 	bootops = (struct bootops *)NULL;
550 	BOOTOPS_GONE();
551 }
552