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 * cprboot - prom client that restores kadb/kernel pages 30 * 31 * simple cprboot overview: 32 * reset boot-file/boot-device to their original values 33 * open cpr statefile, usually "/.CPR" 34 * read in statefile 35 * close statefile 36 * restore kernel pages 37 * jump back into kernel text 38 * 39 * 40 * cprboot supports a restartable statefile for FAA/STARS, 41 * Federal Aviation Administration 42 * Standard Terminal Automation Replacement System 43 */ 44 45 #include <sys/types.h> 46 #include <sys/cpr.h> 47 #include <sys/promimpl.h> 48 #include <sys/ddi.h> 49 #include "cprboot.h" 50 51 char *volname = NULL; 52 53 /* 54 * local defs 55 */ 56 #define CB_MAXPROP 256 57 #define CB_MAXARGS 8 58 59 60 /* 61 * globals 62 */ 63 struct statefile sfile; 64 65 char cpr_statefile[OBP_MAXPATHLEN]; 66 char cpr_filesystem[OBP_MAXPATHLEN]; 67 68 int cpr_debug; /* cpr debug, set with uadmin 3 10x */ 69 uint_t cb_msec; /* cprboot start runtime */ 70 uint_t cb_dents; /* number of dtlb entries */ 71 72 int do_halt = 0; /* halt (enter mon) after load */ 73 int verbose = 0; /* verbose, traces cprboot ops */ 74 75 char rsvp[] = "please reboot"; 76 char prog[] = "cprboot"; 77 char entry[] = "ENTRY"; 78 char ent_fmt[] = "\n%s %s\n"; 79 80 81 /* 82 * file scope 83 */ 84 static char cb_argbuf[CB_MAXPROP]; 85 static char *cb_args[CB_MAXARGS]; 86 87 static int reusable; 88 char *specialstate; 89 90 91 static int 92 cb_intro(void) 93 { 94 static char cstr[] = "\014" "\033[1P" "\033[18;21H"; 95 96 CB_VENTRY(cb_intro); 97 98 /* 99 * build/debug aid; this condition should not occur 100 */ 101 if ((uintptr_t)_end > CB_SRC_VIRT) { 102 prom_printf("\ndata collision:\n" 103 "(_end=0x%p > CB_LOW_VIRT=0x%x), recompile...\n", 104 _end, CB_SRC_VIRT); 105 return (ERR); 106 } 107 108 /* clear console */ 109 prom_printf(cstr); 110 111 prom_printf("Restoring the System. Please Wait... "); 112 return (0); 113 } 114 115 116 /* 117 * read bootargs and convert to arg vector 118 * 119 * sets globals: 120 * cb_argbuf 121 * cb_args 122 */ 123 static void 124 get_bootargs(void) 125 { 126 char *cp, *tail, *argp, **argv; 127 128 CB_VENTRY(get_bootargs); 129 130 (void) prom_strcpy(cb_argbuf, prom_bootargs()); 131 tail = cb_argbuf + prom_strlen(cb_argbuf); 132 133 /* 134 * scan to the trailing NULL so the last arg 135 * will be found without any special-case code 136 */ 137 argv = cb_args; 138 for (cp = argp = cb_argbuf; cp <= tail; cp++) { 139 if (prom_strchr(" \t\n\r", *cp) == NULL) 140 continue; 141 *cp = '\0'; 142 if (cp - argp) { 143 *argv++ = argp; 144 if ((argv - cb_args) == (CB_MAXARGS - 1)) 145 break; 146 } 147 argp = cp + 1; 148 } 149 *argv = NULLP; 150 151 if (verbose) { 152 for (argv = cb_args; *argv; argv++) { 153 prom_printf(" %ld: \"%s\"\n", 154 (argv - cb_args), *argv); 155 } 156 } 157 } 158 159 160 static void 161 usage(char *expect, char *got) 162 { 163 if (got == NULL) 164 got = "(NULL)"; 165 prom_printf("\nbad OBP boot args: expect %s, got %s\n" 166 "Usage: boot -F %s [-R] [-S <diskpath>]\n%s\n\n", 167 expect, got, prog, rsvp); 168 prom_exit_to_mon(); 169 } 170 171 172 /* 173 * bootargs should start with "-F cprboot" 174 * 175 * may set globals: 176 * specialstate 177 * reusable 178 * do_halt 179 * verbose 180 */ 181 static void 182 check_bootargs(void) 183 { 184 char **argv, *str, *cp; 185 186 argv = cb_args; 187 188 /* expect "-F" */ 189 str = "-F"; 190 if (*argv == NULL || prom_strcmp(*argv, str)) 191 usage(str, *argv); 192 argv++; 193 194 /* expect "cprboot*" */ 195 if (*argv == NULL || prom_strncmp(*argv, prog, sizeof (prog) - 1)) 196 usage(prog, *argv); 197 198 /* 199 * optional args 200 */ 201 str = "-[SR]"; 202 for (argv++; *argv; argv++) { 203 cp = *argv; 204 if (*cp != '-') 205 usage(str, *argv); 206 207 switch (*++cp) { 208 case 'R': 209 case 'r': 210 reusable = 1; 211 break; 212 case 'S': 213 case 's': 214 if (*++argv) 215 specialstate = *argv; 216 else 217 usage("statefile-path", *argv); 218 break; 219 case 'h': 220 do_halt = 1; 221 break; 222 case 'v': 223 verbose = 1; 224 break; 225 default: 226 usage(str, *argv); 227 break; 228 } 229 } 230 } 231 232 233 /* 234 * reset prom props and get statefile info 235 * 236 * sets globals: 237 * cpr_filesystem 238 * cpr_statefile 239 */ 240 static int 241 cb_startup(void) 242 { 243 CB_VENTRY(cb_startup); 244 245 if (!reusable) { 246 /* 247 * Restore the original values of the nvram properties modified 248 * during suspend. Note: if we can't get this info from the 249 * defaults file, the state file may be obsolete or bad, so we 250 * abort. However, failure to restore one or more properties 251 * is NOT fatal (better to continue the resume). 252 */ 253 if (cpr_reset_properties() == -1) { 254 prom_printf("\n%s: cannot read saved " 255 "nvram info, %s\n", prog, rsvp); 256 return (ERR); 257 } 258 } 259 260 /* 261 * simple copy if using specialstate, 262 * otherwise read in fs and statefile from a config file 263 */ 264 if (specialstate) 265 (void) prom_strcpy(cpr_statefile, specialstate); 266 else if (cpr_locate_statefile(cpr_statefile, cpr_filesystem) == -1) { 267 prom_printf("\n%s: cannot find cpr statefile, %s\n", 268 prog, rsvp); 269 return (ERR); 270 } 271 272 return (0); 273 } 274 275 276 static int 277 cb_open_sf(void) 278 { 279 CB_VENTRY(cb_open_sf); 280 281 sfile.fd = cpr_statefile_open(cpr_statefile, cpr_filesystem); 282 if (sfile.fd == -1) { 283 prom_printf("\n%s: can't open %s", prog, cpr_statefile); 284 if (specialstate) 285 prom_printf(" on %s", cpr_filesystem); 286 prom_printf("\n%s\n", rsvp); 287 return (ERR); 288 } 289 290 /* 291 * for block devices, seek past the disk label and bootblock 292 */ 293 if (volname) 294 (void) cpr_fs_seek(sfile.fd, CPR_SPEC_OFFSET); 295 else if (specialstate) 296 (void) prom_seek(sfile.fd, CPR_SPEC_OFFSET); 297 return (0); 298 } 299 300 301 static int 302 cb_close_sf(void) 303 { 304 CB_VENTRY(cb_close_sf); 305 306 /* 307 * close the device so the prom will free up 20+ pages 308 */ 309 (void) cpr_statefile_close(sfile.fd); 310 return (0); 311 } 312 313 314 /* 315 * to restore kernel pages, we have to open a prom device to read-in 316 * the statefile contents; a prom "open" request triggers the driver 317 * and various packages to allocate 20+ pages; unfortunately, some or 318 * all of those pages always clash with kernel pages, and we cant write 319 * to them without corrupting the prom. 320 * 321 * to solve that problem, the only real solution is to close the device 322 * to free up those pages; this means we need to open, read-in the entire 323 * statefile, and close; and to store the statefile, we need to allocate 324 * plenty of space, usually around 2 to 60 MB. 325 * 326 * the simplest alloc means is prom_alloc(), which will "claim" both 327 * virt and phys pages, and creates mappings with a "map" request; 328 * "map" also causes the prom to alloc pages, and again these clash 329 * with kernel pages... 330 * 331 * to solve the "map" problem, we just reserve virt and phys pages and 332 * manage the translations by creating our own tlb entries instead of 333 * relying on the prom. 334 * 335 * sets globals: 336 * cpr_test_mode 337 * sfile.kpages 338 * sfile.size 339 * sfile.buf 340 * sfile.low_ppn 341 * sfile.high_ppn 342 */ 343 static int 344 cb_read_statefile(void) 345 { 346 size_t alsize, len, resid; 347 physaddr_t phys, dst_phys; 348 char *str, *dst_virt; 349 int err, cnt, mmask; 350 uint_t dtlb_index; 351 ssize_t nread; 352 cdd_t cdump; 353 354 str = "cb_read_statefile"; 355 CB_VPRINTF((ent_fmt, str, entry)); 356 357 /* 358 * read-in and check cpr dump header 359 */ 360 if (cpr_read_cdump(sfile.fd, &cdump, CPR_MACHTYPE_4U) == -1) 361 return (ERR); 362 363 if (cpr_debug) 364 prom_printf("\n"); 365 cb_nbitmaps = cdump.cdd_bitmaprec; 366 cpr_test_mode = cdump.cdd_test_mode; 367 sfile.kpages = cdump.cdd_dumppgsize; 368 CPR_DEBUG(CPR_DEBUG4, "%s: total kpages %d\n", prog, sfile.kpages); 369 370 /* 371 * alloc virt and phys space with 512K alignment; 372 * alloc size should be (n * tte size); 373 */ 374 sfile.size = PAGE_ROUNDUP(cdump.cdd_filesize); 375 alsize = (cdump.cdd_filesize + MMU_PAGEOFFSET512K) & 376 MMU_PAGEMASK512K; 377 phys = 0; 378 err = cb_alloc(alsize, MMU_PAGESIZE512K, &sfile.buf, &phys); 379 CB_VPRINTF(("%s:\n alloc size 0x%lx, buf size 0x%lx\n" 380 " virt 0x%p, phys 0x%llx\n", 381 str, alsize, sfile.size, sfile.buf, phys)); 382 if (err) { 383 prom_printf("%s: cant alloc statefile buf, size 0x%lx\n%s\n", 384 str, sfile.size, rsvp); 385 return (ERR); 386 } 387 388 /* 389 * record low and high phys page numbers for sfile.buf 390 */ 391 sfile.low_ppn = ADDR_TO_PN(phys); 392 sfile.high_ppn = sfile.low_ppn + mmu_btop(sfile.size) - 1; 393 394 /* 395 * setup destination virt and phys addrs for reads; 396 * mapin-mask tells when to create a new tlb entry for the 397 * next set of reads; NB: the read and tlb method needs 398 * ((big-pagesize % read-size) == 0) 399 */ 400 dst_phys = phys; 401 mmask = (MMU_PAGESIZE512K / PROM_MAX_READ) - 1; 402 403 cnt = 0; 404 dtlb_index = cb_dents - 1; 405 if (volname) 406 (void) cpr_fs_seek(sfile.fd, CPR_SPEC_OFFSET); 407 else if (specialstate) 408 (void) prom_seek(sfile.fd, CPR_SPEC_OFFSET); 409 else 410 (void) cpr_fs_seek(sfile.fd, 0); 411 CPR_DEBUG(CPR_DEBUG1, "%s: reading statefile... ", prog); 412 for (resid = cdump.cdd_filesize; resid; resid -= len) { 413 /* 414 * do a full spin (4 spin chars) 415 * for every MB read (8 reads = 256K) 416 */ 417 if ((cnt & 0x7) == 0) 418 cb_spin(); 419 420 /* 421 * map-in statefile buf pages in 512K blocks; 422 * see MMU_PAGESIZE512K above 423 */ 424 if ((cnt & mmask) == 0) { 425 dst_virt = sfile.buf; 426 cb_mapin(dst_virt, ADDR_TO_PN(dst_phys), 427 TTE512K, TTE_HWWR_INT, dtlb_index); 428 } 429 430 cnt++; 431 432 len = min(PROM_MAX_READ, resid); 433 nread = cpr_read(sfile.fd, dst_virt, len); 434 if (nread != (ssize_t)len) { 435 prom_printf("\n%s: prom read error, " 436 "expect %ld, got %ld\n", str, len, nread); 437 return (ERR); 438 } 439 dst_virt += len; 440 dst_phys += len; 441 } 442 CPR_DEBUG(CPR_DEBUG1, " \b\n"); 443 444 /* 445 * free up any unused phys pages trailing the statefile buffer; 446 * these pages will later appear on the physavail list 447 */ 448 if (alsize > sfile.size) { 449 len = alsize - sfile.size; 450 prom_free_phys(len, phys + sfile.size); 451 CB_VPRINTF(("%s: freed %ld phys pages (0x%lx - 0x%lx)\n", 452 str, mmu_btop(len), phys + sfile.size, 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, &mdinfo); 582 } 583 check_halt("exit_to_kernel"); 584 exit_to_kernel(cookie, &mdinfo); 585 return (ERR); 586 } 587 } 588