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