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 /* 27 * cprboot - prom client that restores kadb/kernel pages 28 * 29 * simple cprboot overview: 30 * reset boot-file/boot-device to their original values 31 * open cpr statefile, usually "/.CPR" 32 * read in statefile 33 * close statefile 34 * restore kernel pages 35 * jump back into kernel text 36 * 37 * 38 * cprboot supports a restartable statefile for FAA/STARS, 39 * Federal Aviation Administration 40 * Standard Terminal Automation Replacement System 41 */ 42 43 #include <sys/types.h> 44 #include <sys/cpr.h> 45 #include <sys/promimpl.h> 46 #include <sys/ddi.h> 47 #include "cprboot.h" 48 49 char *volname = NULL; 50 51 /* 52 * local defs 53 */ 54 #define CB_MAXPROP 256 55 #define CB_MAXARGS 8 56 57 58 /* 59 * globals 60 */ 61 struct statefile sfile; 62 63 char cpr_statefile[OBP_MAXPATHLEN]; 64 char cpr_filesystem[OBP_MAXPATHLEN]; 65 66 int cpr_debug; /* cpr debug, set with uadmin 3 10x */ 67 uint_t cb_msec; /* cprboot start runtime */ 68 uint_t cb_dents; /* number of dtlb entries */ 69 70 int do_halt = 0; /* halt (enter mon) after load */ 71 int verbose = 0; /* verbose, traces cprboot ops */ 72 73 char rsvp[] = "please reboot"; 74 char prog[] = "cprboot"; 75 char entry[] = "ENTRY"; 76 char ent_fmt[] = "\n%s %s\n"; 77 78 79 /* 80 * file scope 81 */ 82 static char cb_argbuf[CB_MAXPROP]; 83 static char *cb_args[CB_MAXARGS]; 84 85 static int reusable; 86 char *specialstate; 87 88 89 static int 90 cb_intro(void) 91 { 92 static char cstr[] = "\014" "\033[1P" "\033[18;21H"; 93 94 CB_VENTRY(cb_intro); 95 96 /* 97 * build/debug aid; this condition should not occur 98 */ 99 if ((uintptr_t)_end > CB_SRC_VIRT) { 100 prom_printf("\ndata collision:\n" 101 "(_end=0x%p > CB_LOW_VIRT=0x%x), recompile...\n", 102 (void *)_end, CB_SRC_VIRT); 103 return (ERR); 104 } 105 106 /* clear console */ 107 prom_printf(cstr); 108 109 prom_printf("Restoring the System. Please Wait... "); 110 return (0); 111 } 112 113 114 /* 115 * read bootargs and convert to arg vector 116 * 117 * sets globals: 118 * cb_argbuf 119 * cb_args 120 */ 121 static void 122 get_bootargs(void) 123 { 124 char *cp, *tail, *argp, **argv; 125 126 CB_VENTRY(get_bootargs); 127 128 (void) prom_strcpy(cb_argbuf, prom_bootargs()); 129 tail = cb_argbuf + prom_strlen(cb_argbuf); 130 131 /* 132 * scan to the trailing NULL so the last arg 133 * will be found without any special-case code 134 */ 135 argv = cb_args; 136 for (cp = argp = cb_argbuf; cp <= tail; cp++) { 137 if (prom_strchr(" \t\n\r", *cp) == NULL) 138 continue; 139 *cp = '\0'; 140 if (cp - argp) { 141 *argv++ = argp; 142 if ((argv - cb_args) == (CB_MAXARGS - 1)) 143 break; 144 } 145 argp = cp + 1; 146 } 147 *argv = NULLP; 148 149 if (verbose) { 150 for (argv = cb_args; *argv; argv++) { 151 prom_printf(" %d: \"%s\"\n", 152 (int)(argv - cb_args), *argv); 153 } 154 } 155 } 156 157 158 static void 159 usage(char *expect, char *got) 160 { 161 if (got == NULL) 162 got = "(NULL)"; 163 prom_printf("\nbad OBP boot args: expect %s, got %s\n" 164 "Usage: boot -F %s [-R] [-S <diskpath>]\n%s\n\n", 165 expect, got, prog, rsvp); 166 prom_exit_to_mon(); 167 } 168 169 170 /* 171 * bootargs should start with "-F cprboot" 172 * 173 * may set globals: 174 * specialstate 175 * reusable 176 * do_halt 177 * verbose 178 */ 179 static void 180 check_bootargs(void) 181 { 182 char **argv, *str, *cp; 183 184 argv = cb_args; 185 186 /* expect "-F" */ 187 str = "-F"; 188 if (*argv == NULL || prom_strcmp(*argv, str)) 189 usage(str, *argv); 190 argv++; 191 192 /* expect "cprboot*" */ 193 if (*argv == NULL || prom_strncmp(*argv, prog, sizeof (prog) - 1)) 194 usage(prog, *argv); 195 196 /* 197 * optional args 198 */ 199 str = "-[SR]"; 200 for (argv++; *argv; argv++) { 201 cp = *argv; 202 if (*cp != '-') 203 usage(str, *argv); 204 205 switch (*++cp) { 206 case 'R': 207 case 'r': 208 reusable = 1; 209 break; 210 case 'S': 211 case 's': 212 if (*++argv) 213 specialstate = *argv; 214 else 215 usage("statefile-path", *argv); 216 break; 217 case 'h': 218 do_halt = 1; 219 break; 220 case 'v': 221 verbose = 1; 222 break; 223 default: 224 usage(str, *argv); 225 break; 226 } 227 } 228 } 229 230 231 /* 232 * reset prom props and get statefile info 233 * 234 * sets globals: 235 * cpr_filesystem 236 * cpr_statefile 237 */ 238 static int 239 cb_startup(void) 240 { 241 CB_VENTRY(cb_startup); 242 243 if (!reusable) { 244 /* 245 * Restore the original values of the nvram properties modified 246 * during suspend. Note: if we can't get this info from the 247 * defaults file, the state file may be obsolete or bad, so we 248 * abort. However, failure to restore one or more properties 249 * is NOT fatal (better to continue the resume). 250 */ 251 if (cpr_reset_properties() == -1) { 252 prom_printf("\n%s: cannot read saved " 253 "nvram info, %s\n", prog, rsvp); 254 return (ERR); 255 } 256 } 257 258 /* 259 * simple copy if using specialstate, 260 * otherwise read in fs and statefile from a config file 261 */ 262 if (specialstate) 263 (void) prom_strcpy(cpr_statefile, specialstate); 264 else if (cpr_locate_statefile(cpr_statefile, cpr_filesystem) == -1) { 265 prom_printf("\n%s: cannot find cpr statefile, %s\n", 266 prog, rsvp); 267 return (ERR); 268 } 269 270 return (0); 271 } 272 273 274 static int 275 cb_open_sf(void) 276 { 277 CB_VENTRY(cb_open_sf); 278 279 sfile.fd = cpr_statefile_open(cpr_statefile, cpr_filesystem); 280 if (sfile.fd == -1) { 281 prom_printf("\n%s: can't open %s", prog, cpr_statefile); 282 if (specialstate) 283 prom_printf(" on %s", cpr_filesystem); 284 prom_printf("\n%s\n", rsvp); 285 return (ERR); 286 } 287 288 /* 289 * for block devices, seek past the disk label and bootblock 290 */ 291 if (volname) 292 (void) cpr_fs_seek(sfile.fd, CPR_SPEC_OFFSET); 293 else if (specialstate) 294 (void) prom_seek(sfile.fd, CPR_SPEC_OFFSET); 295 return (0); 296 } 297 298 299 static int 300 cb_close_sf(void) 301 { 302 CB_VENTRY(cb_close_sf); 303 304 /* 305 * close the device so the prom will free up 20+ pages 306 */ 307 (void) cpr_statefile_close(sfile.fd); 308 return (0); 309 } 310 311 312 /* 313 * to restore kernel pages, we have to open a prom device to read-in 314 * the statefile contents; a prom "open" request triggers the driver 315 * and various packages to allocate 20+ pages; unfortunately, some or 316 * all of those pages always clash with kernel pages, and we cant write 317 * to them without corrupting the prom. 318 * 319 * to solve that problem, the only real solution is to close the device 320 * to free up those pages; this means we need to open, read-in the entire 321 * statefile, and close; and to store the statefile, we need to allocate 322 * plenty of space, usually around 2 to 60 MB. 323 * 324 * the simplest alloc means is prom_alloc(), which will "claim" both 325 * virt and phys pages, and creates mappings with a "map" request; 326 * "map" also causes the prom to alloc pages, and again these clash 327 * with kernel pages... 328 * 329 * to solve the "map" problem, we just reserve virt and phys pages and 330 * manage the translations by creating our own tlb entries instead of 331 * relying on the prom. 332 * 333 * sets globals: 334 * cpr_test_mode 335 * sfile.kpages 336 * sfile.size 337 * sfile.buf 338 * sfile.low_ppn 339 * sfile.high_ppn 340 */ 341 static int 342 cb_read_statefile(void) 343 { 344 size_t alsize, len, resid; 345 physaddr_t phys, dst_phys; 346 char *str, *dst_virt; 347 int err, cnt, mmask; 348 uint_t dtlb_index; 349 ssize_t nread; 350 cdd_t cdump; 351 352 str = "cb_read_statefile"; 353 CB_VPRINTF((ent_fmt, str, entry)); 354 355 /* 356 * read-in and check cpr dump header 357 */ 358 if (cpr_read_cdump(sfile.fd, &cdump, CPR_MACHTYPE_4U) == -1) 359 return (ERR); 360 361 if (cpr_debug) 362 prom_printf("\n"); 363 cb_nbitmaps = cdump.cdd_bitmaprec; 364 cpr_test_mode = cdump.cdd_test_mode; 365 sfile.kpages = cdump.cdd_dumppgsize; 366 CPR_DEBUG(CPR_DEBUG4, "%s: total kpages %d\n", prog, sfile.kpages); 367 368 /* 369 * alloc virt and phys space with 512K alignment; 370 * alloc size should be (n * tte size); 371 */ 372 sfile.size = PAGE_ROUNDUP(cdump.cdd_filesize); 373 alsize = (cdump.cdd_filesize + MMU_PAGEOFFSET512K) & 374 MMU_PAGEMASK512K; 375 phys = 0; 376 err = cb_alloc(alsize, MMU_PAGESIZE512K, &sfile.buf, &phys); 377 CB_VPRINTF(("%s:\n alloc size 0x%lx, buf size 0x%lx\n" 378 " virt 0x%p, phys 0x%llx\n", 379 str, alsize, sfile.size, (void *)sfile.buf, phys)); 380 if (err) { 381 prom_printf("%s: cant alloc statefile buf, size 0x%lx\n%s\n", 382 str, sfile.size, rsvp); 383 return (ERR); 384 } 385 386 /* 387 * record low and high phys page numbers for sfile.buf 388 */ 389 sfile.low_ppn = ADDR_TO_PN(phys); 390 sfile.high_ppn = sfile.low_ppn + mmu_btop(sfile.size) - 1; 391 392 /* 393 * setup destination virt and phys addrs for reads; 394 * mapin-mask tells when to create a new tlb entry for the 395 * next set of reads; NB: the read and tlb method needs 396 * ((big-pagesize % read-size) == 0) 397 */ 398 dst_phys = phys; 399 mmask = (MMU_PAGESIZE512K / PROM_MAX_READ) - 1; 400 401 cnt = 0; 402 dtlb_index = cb_dents - 1; 403 if (volname) 404 (void) cpr_fs_seek(sfile.fd, CPR_SPEC_OFFSET); 405 else if (specialstate) 406 (void) prom_seek(sfile.fd, CPR_SPEC_OFFSET); 407 else 408 (void) cpr_fs_seek(sfile.fd, 0); 409 CPR_DEBUG(CPR_DEBUG1, "%s: reading statefile... ", prog); 410 for (resid = cdump.cdd_filesize; resid; resid -= len) { 411 /* 412 * do a full spin (4 spin chars) 413 * for every MB read (8 reads = 256K) 414 */ 415 if ((cnt & 0x7) == 0) 416 cb_spin(); 417 418 /* 419 * map-in statefile buf pages in 512K blocks; 420 * see MMU_PAGESIZE512K above 421 */ 422 if ((cnt & mmask) == 0) { 423 dst_virt = sfile.buf; 424 cb_mapin(dst_virt, ADDR_TO_PN(dst_phys), 425 TTE512K, TTE_HWWR_INT, dtlb_index); 426 } 427 428 cnt++; 429 430 len = min(PROM_MAX_READ, resid); 431 nread = cpr_read(sfile.fd, dst_virt, len); 432 if (nread != (ssize_t)len) { 433 prom_printf("\n%s: prom read error, " 434 "expect %ld, got %ld\n", str, len, nread); 435 return (ERR); 436 } 437 dst_virt += len; 438 dst_phys += len; 439 } 440 CPR_DEBUG(CPR_DEBUG1, " \b\n"); 441 442 /* 443 * free up any unused phys pages trailing the statefile buffer; 444 * these pages will later appear on the physavail list 445 */ 446 if (alsize > sfile.size) { 447 len = alsize - sfile.size; 448 prom_free_phys(len, phys + sfile.size); 449 CB_VPRINTF(("%s: freed %ld phys pages (0x%llx - 0x%llx)\n", 450 str, mmu_btop(len), 451 (unsigned long long)(phys + sfile.size), 452 (unsigned long long)(phys + alsize))); 453 } 454 455 /* 456 * start the statefile buffer offset at the base of 457 * the statefile buffer and skip past the dump header 458 */ 459 sfile.buf_offset = 0; 460 SF_ADV(sizeof (cdump)); 461 462 /* 463 * finish with the first block mapped-in to provide easy virt access 464 * to machdep structs and the bitmap; for 2.8, the combined size of 465 * (cdd_t + cmd_t + csu_md_t + prom_words + cbd_t) is about 1K, 466 * leaving room for a bitmap representing nearly 32GB 467 */ 468 cb_mapin(sfile.buf, sfile.low_ppn, 469 TTE512K, TTE_HWWR_INT, dtlb_index); 470 471 return (0); 472 } 473 474 475 /* 476 * cprboot first stage worklist 477 */ 478 static int (*first_worklist[])(void) = { 479 cb_intro, 480 cb_mountroot, 481 cb_startup, 482 cb_get_props, 483 cb_usb_setup, 484 cb_unmountroot, 485 cb_open_sf, 486 cb_read_statefile, 487 cb_close_sf, 488 cb_check_machdep, 489 cb_interpret, 490 cb_get_physavail, 491 cb_set_bitmap, 492 cb_get_newstack, 493 NULL 494 }; 495 496 /* 497 * cprboot second stage worklist 498 */ 499 static int (*second_worklist[])(void) = { 500 cb_relocate, 501 cb_tracking_setup, 502 cb_restore_kpages, 503 cb_terminator, 504 cb_ksetup, 505 cb_mpsetup, 506 NULL 507 }; 508 509 510 /* 511 * simple loop driving major cprboot operations; 512 * exits to prom if any error is returned 513 */ 514 static void 515 cb_drive(int (**worklist)(void)) 516 { 517 int i; 518 519 for (i = 0; worklist[i] != NULL; i++) { 520 if (worklist[i]()) 521 cb_exit_to_mon(); 522 } 523 } 524 525 526 /* 527 * debugging support: drop to prom if do_halt is set 528 */ 529 static void 530 check_halt(char *str) 531 { 532 if (do_halt) { 533 prom_printf("\n%s halted by -h flag\n==> before %s\n\n", 534 prog, str); 535 cb_enter_mon(); 536 } 537 } 538 539 540 /* 541 * main is called twice from "cb_srt0.s", args are: 542 * cookie ieee1275 cif handle 543 * first (true): first stage, (false): second stage 544 * 545 * first stage summary: 546 * various setup 547 * allocate a big statefile buffer 548 * read in the statefile 549 * setup the bitmap 550 * create a new stack 551 * 552 * return to "cb_srt0.s", switch to new stack 553 * 554 * second stage summary: 555 * relocate cprboot phys pages 556 * setup tracking for statefile buffer pages 557 * restore kernel pages 558 * various cleanup 559 * install tlb entries for the nucleus and cpr module 560 * restore registers and jump into cpr module 561 */ 562 int 563 main(void *cookie, int first) 564 { 565 if (first) { 566 prom_init(prog, cookie); 567 cb_msec = prom_gettime(); 568 get_bootargs(); 569 check_bootargs(); 570 check_halt("first_worklist"); 571 cb_drive(first_worklist); 572 return (0); 573 } else { 574 cb_drive(second_worklist); 575 if (verbose || CPR_DBG(1)) { 576 prom_printf("%s: milliseconds %d\n", 577 prog, prom_gettime() - cb_msec); 578 prom_printf("%s: resume pc 0x%lx\n", 579 prog, mdinfo.func); 580 prom_printf("%s: exit_to_kernel(0x%p, 0x%p)\n\n", 581 prog, cookie, (void *)&mdinfo); 582 } 583 check_halt("exit_to_kernel"); 584 exit_to_kernel(cookie, &mdinfo); 585 return (ERR); 586 } 587 } 588