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