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 #include <sys/cpr.h> 27 #include <sys/promimpl.h> 28 #include "cprboot.h" 29 30 31 static ihandle_t cb_rih; 32 static pnode_t chosen; 33 34 static int statefile_special; 35 36 static int reset_input = 0; 37 static char kbd_input[] = "keyboard input"; 38 static char null_input[] = "\" /nulldev\" input"; 39 40 /* 41 * Ask prom to open a disk file given either the OBP device path, or the 42 * device path representing the target drive/partition and the fs-relative 43 * path of the file. Handle file pathnames with or without leading '/'. 44 * if fs points to a null char, it indicates that we are opening a device. 45 */ 46 int 47 cpr_statefile_open(char *path, char *fs_dev) 48 { 49 int plen, dlen; 50 int handle; 51 char fs_pkg[OBP_MAXPATHLEN]; 52 char fs_name[OBP_MAXDRVNAME]; 53 54 /* 55 * instead of using specialstate, we use fs as the flag 56 */ 57 if (*fs_dev == '\0') { /* device open */ 58 statefile_special = 1; 59 handle = prom_open(path); 60 /* IEEE1275 prom_open returns 0 on failure; we return -1 */ 61 return (handle ? handle : -1); 62 } 63 64 /* 65 * No cif for $open-package, so we have to use interpret 66 */ 67 if (prom_getprop(chosen, "fs-package", fs_pkg) == -1) { 68 prom_printf("Missing fs-package name\n"); 69 return (-1); 70 } 71 plen = prom_strlen(fs_pkg); 72 dlen = prom_strlen(fs_dev); 73 prom_interpret("$open-package swap l!", plen, (uintptr_t)fs_pkg, 74 dlen, (uintptr_t)fs_dev, (uintptr_t)&cb_rih); 75 if (cb_rih == OBP_BADNODE || cb_rih == 0) { 76 prom_printf("Can't open %s\n", fs_pkg); 77 return (-1); 78 } 79 80 if (volname) { 81 return (cpr_fs_volopen(volname)); 82 } 83 84 /* 85 * Prepend '/' if it's not there already 86 */ 87 if (*path != '/') { 88 (void) prom_sprintf(fs_name, "/%s", path); 89 return (cpr_fs_open(fs_name)); 90 } else 91 return (cpr_fs_open(path)); 92 } 93 94 /* 95 * Mount root fs so we can read statefile, etc 96 * 97 * sets global 98 * cb_rih 99 */ 100 int 101 cb_mountroot() 102 { 103 104 chosen = prom_chosennode(); 105 if (chosen == OBP_BADNODE) { 106 prom_printf("Missing chosen node\n"); 107 return (ERR); 108 } 109 if (prom_getprop(chosen, "bootfs", (caddr_t)&cb_rih) == -1) { 110 prom_printf("Missing bootfs ihandle\n"); 111 return (ERR); 112 } 113 return (0); 114 } 115 116 /* 117 * Unmount root 118 */ 119 int 120 cb_unmountroot() 121 { 122 (void) prom_close(cb_rih); 123 cb_rih = OBP_BADNODE; 124 return (0); 125 } 126 127 int 128 cpr_fs_volopen(char *path) 129 { 130 131 CB_VENTRY(cpr_fs_volopen); 132 133 if (cb_rih == OBP_BADNODE) 134 return (-1); 135 return (prom_volopen(cb_rih, path)); 136 } 137 138 /* 139 * Ask prom to open a disk file. 140 */ 141 int 142 cpr_fs_open(char *path) 143 { 144 145 CB_VENTRY(cpr_fs_open); 146 147 if (cb_rih == OBP_BADNODE) 148 return (-1); 149 return (prom_fopen(cb_rih, path)); 150 } 151 152 153 /* 154 * Direct read if using block special, 155 * otherwise use fs read 156 */ 157 int 158 cpr_read(int fd, caddr_t buf, size_t len) 159 { 160 if (!statefile_special || volname) 161 return (cpr_fs_read(fd, buf, len)); 162 else 163 return (prom_read(fd, buf, len, 0, 0)); 164 } 165 166 167 int 168 cpr_fs_read(int fd, caddr_t buf, int len) 169 { 170 if (cb_rih == OBP_BADNODE) 171 return (-1); 172 return (prom_fread(cb_rih, fd, buf, len)); 173 } 174 175 176 int 177 cpr_fs_close(int fd) 178 { 179 CB_VPRINTF(("cpr_fs_close 0x%x\n", fd)); 180 181 if (cb_rih == OBP_BADNODE) 182 return (-1); 183 prom_fclose(cb_rih, fd); 184 return (0); 185 } 186 187 int 188 cpr_fs_seek(int fd, offset_t off) 189 { 190 if (cb_rih == OBP_BADNODE) 191 return (-1); 192 return (prom_fseek(cb_rih, fd, off)); 193 } 194 195 196 int 197 cpr_statefile_close(int fd) 198 { 199 if (statefile_special) { 200 statefile_special = 0; 201 return (prom_close(fd)); 202 } else 203 return (cpr_fs_close(fd)); 204 } 205 206 207 void 208 cb_spin(void) 209 { 210 static int spindex = 0; 211 static char *spin_pairs[] = { "|\b", "/\b", "-\b", "\\\b" }; 212 const size_t nspin_pairs = sizeof (spin_pairs) / sizeof (spin_pairs[0]); 213 214 prom_printf(spin_pairs[spindex]); 215 spindex = (spindex + 1) % nspin_pairs; 216 } 217 218 219 /* 220 * translate vaddr to phys page number 221 */ 222 pfn_t 223 cpr_vatopfn(caddr_t vaddr) 224 { 225 physaddr_t paddr; 226 int valid, mode; 227 228 (void) prom_translate_virt(vaddr, &valid, &paddr, &mode); 229 if (valid != -1) 230 return (PFN_INVALID); 231 return (paddr >> MMU_PAGESHIFT); 232 } 233 234 235 /* 236 * unmap virt, then map virt to new phys; 237 * see remap definition below 238 */ 239 int 240 prom_remap(size_t size, caddr_t virt, physaddr_t phys) 241 { 242 ihandle_t immu; 243 cell_t ci[8]; 244 int rv; 245 246 immu = prom_mmu_ihandle(); 247 if (immu == (ihandle_t)-1) 248 return (ERR); 249 250 ci[0] = p1275_ptr2cell("call-method"); /* Service name */ 251 ci[1] = (cell_t)5; /* #argument cells */ 252 ci[2] = (cell_t)0; /* #result cells */ 253 ci[3] = p1275_ptr2cell("remap"); /* Arg1: Method name */ 254 ci[4] = p1275_ihandle2cell(immu); /* Arg2: memory ihandle */ 255 ci[5] = p1275_size2cell(size); /* remap arg0 */ 256 ci[6] = p1275_ptr2cell(virt); /* remap arg1 */ 257 ci[7] = p1275_ull2cell_low(phys); /* remap arg2 */ 258 259 promif_preprom(); 260 rv = p1275_cif_handler(ci); 261 promif_postprom(); 262 263 if (rv) 264 return (rv); /* Service "call-method" failed */ 265 return (0); 266 } 267 268 269 /* 270 * install remap definition in /virtual-memory node; 271 * used for replacing a virt->phys mapping in one promif call; 272 * this needs to be atomic from the client's perspective to 273 * avoid faults while relocating client text. 274 */ 275 void 276 install_remap(void) 277 { 278 static char remap_def[] = 279 "\" /virtual-memory\" find-device " 280 ": remap ( phys.lo virt size -- )" 281 " 2dup unmap ( phys.lo virt size )" 282 " 0 -rot -1 map ( ) ; " 283 "device-end"; 284 285 prom_interpret(remap_def, 0, 0, 0, 0, 0); 286 } 287 288 289 /* 290 * allocate virt and phys space without any mapping; 291 * stores virt and phys addrs at *vap and *pap 292 */ 293 int 294 cb_alloc(size_t size, uint_t align, caddr_t *vap, physaddr_t *pap) 295 { 296 physaddr_t phys; 297 caddr_t virt; 298 299 virt = prom_allocate_virt(align, (size_t)align); 300 if (virt == (caddr_t)-1) 301 return (ERR); 302 if (prom_allocate_phys(size, align, &phys) == -1) { 303 prom_free_virt(size, virt); 304 return (ERR); 305 } 306 307 *vap = virt; 308 *pap = phys; 309 return (0); 310 } 311 312 313 static int 314 get_intprop(pnode_t node, caddr_t prop, void *dst) 315 { 316 int len, glen; 317 318 len = sizeof (uint_t); 319 glen = prom_getprop(node, prop, dst); 320 if (glen != len) 321 return (ERR); 322 323 return (0); 324 } 325 326 327 /* 328 * find cpu node for the boot processor 329 * 330 * sets globals: 331 * cb_mid 332 */ 333 static pnode_t 334 get_cpu_node(void) 335 { 336 static char *props[] = { "upa-portid", "portid", NULL }; 337 pnode_t node; 338 char *str, *name, **propp; 339 uint_t cpu_id; 340 int err; 341 342 str = "get_cpu_node"; 343 name = "cpu"; 344 345 cb_mid = getmid(); 346 for (node = prom_rootnode(); ; node = prom_nextnode(node)) { 347 node = prom_findnode_bydevtype(node, name); 348 if (node == OBP_NONODE) { 349 prom_printf("\n%s: cant find node for devtype \"%s\"\n", 350 str, name); 351 break; 352 } 353 354 cpu_id = (uint_t)-1; 355 for (propp = props; *propp; propp++) { 356 err = get_intprop(node, *propp, &cpu_id); 357 CB_VPRINTF((" cpu node 0x%x, " 358 "prop \"%s\", cpu_id %d\n", 359 node, *propp, (int)cpu_id)); 360 if (err == 0) 361 break; 362 } 363 364 if (cpu_id == cb_mid) 365 return (node); 366 } 367 368 return (OBP_NONODE); 369 } 370 371 372 /* 373 * lookup prom properties 374 * 375 * sets globals: 376 * cb_dents 377 * cb_clock_freq 378 * cpu_delay 379 */ 380 int 381 cb_get_props(void) 382 { 383 uint_t clock_mhz; 384 pnode_t node; 385 struct cb_props *cbp; 386 static struct cb_props cpu_data[] = { 387 "#dtlb-entries", &cb_dents, 388 "clock-frequency", &cb_clock_freq, 389 NULL, NULL, 390 }; 391 392 CB_VENTRY(cb_get_props); 393 394 node = get_cpu_node(); 395 if (node == OBP_NONODE) 396 return (ERR); 397 for (cbp = cpu_data; cbp->prop; cbp++) { 398 if (get_intprop(node, cbp->prop, cbp->datap)) { 399 prom_printf("\n%s: getprop error, " 400 "node 0x%x, prop \"%s\"\n", 401 prog, node, cbp->prop); 402 return (ERR); 403 } 404 CB_VPRINTF((" \"%s\" = 0x%x\n", 405 cbp->prop, *cbp->datap)); 406 } 407 408 /* 409 * setup cpu_delay for cb_usec_wait 410 */ 411 clock_mhz = (cb_clock_freq + 500000) / 1000000; 412 cpu_delay = clock_mhz - 7; 413 CB_VPRINTF((" clock_mhz %d, cpu_delay %d\n", 414 clock_mhz, cpu_delay)); 415 416 return (0); 417 } 418 419 420 /* 421 * map-in data pages 422 * size should fit tte_bit.sz 423 * rw should be 0 or TTE_HWWR_INT 424 */ 425 void 426 cb_mapin(caddr_t vaddr, pfn_t ppn, uint_t size, uint_t rw, uint_t dtlb_index) 427 { 428 tte_t tte; 429 430 tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(size) | 431 TTE_PFN_INTHI(ppn); 432 tte.tte_intlo = TTE_PFN_INTLO(ppn) | TTE_LCK_INT | 433 TTE_CP_INT | TTE_CV_INT | TTE_PRIV_INT | rw; 434 set_dtlb_entry(dtlb_index, vaddr, &tte); 435 } 436 437 438 static char * 439 prom_strstr(char *string, char *substr) 440 { 441 char *strp, *subp, *tmp, c; 442 443 if (substr == NULL || *substr == '\0') 444 return (string); 445 446 strp = string; 447 subp = substr; 448 c = *subp; 449 450 while (*strp) { 451 if (*strp++ == c) { 452 tmp = strp; 453 while ((c = *++subp) == *strp++ && c) 454 ; 455 if (c == '\0') 456 return (tmp - 1); 457 strp = tmp; 458 subp = substr; 459 c = *subp; 460 } 461 } 462 463 return (NULL); 464 } 465 466 467 static void 468 cb_set_idev(char *istr) 469 { 470 if (reset_input) { 471 prom_interpret(istr, 0, 0, 0, 0, 0); 472 CB_VPRINTF(("\ncb_set_idev: reset with [%s]\n", istr)); 473 } 474 } 475 476 477 /* 478 * workaround for USB keyboard: 479 * USB DMA activity has been known to corrupt kernel pages while cprboot 480 * is restoring them. to quiesce the USB chip, we craft a "null" device 481 * and temporarily use that as the prom's input device. this effectively 482 * disables the USB keyboard until the cpr module restores the original 483 * prom and a kernel driver re-inits and takes-over control of USB. 484 * 485 * may set globals: 486 * reset_input 487 */ 488 int 489 cb_usb_setup(void) 490 { 491 char sp[OBP_MAXPATHLEN]; 492 static char cb_nulldev[] = 493 "\" /\" select-dev " 494 "new-device " 495 "\" nulldev\" device-name " 496 ": read 2drop -2 ; " 497 ": open true ; " 498 ": close ; " 499 ": install-abort ; " 500 ": remove-abort ; " 501 ": write 2drop 0 ; " 502 ": restore ; " 503 "finish-device " 504 "unselect-dev"; 505 506 CB_VENTRY(cb_usb_setup); 507 508 bzero(sp, sizeof (sp)); 509 prom_interpret("stdin @ ihandle>devname swap -rot move", 510 (uintptr_t)sp, 0, 0, 0, 0); 511 if (prom_strstr(sp, "usb") && prom_strstr(sp, "keyboard")) { 512 prom_interpret(cb_nulldev, 0, 0, 0, 0, 0); 513 reset_input = 1; 514 cb_set_idev(null_input); 515 } 516 517 return (0); 518 } 519 520 521 /* 522 * switch input to keyboard before entering the prom, and switch to the 523 * crafted nulldev after returning from the prom. this occurs only when 524 * stdinpath is a USB keyboard; entering the prom is usually done only 525 * for debugging purposes - see check_halt() and above DMA comment. 526 */ 527 void 528 cb_enter_mon(void) 529 { 530 cb_set_idev(kbd_input); 531 prom_enter_mon(); 532 cb_set_idev(null_input); 533 } 534 535 536 /* 537 * similar to above before exiting to the prom 538 */ 539 void 540 cb_exit_to_mon(void) 541 { 542 cb_set_idev(kbd_input); 543 prom_exit_to_mon(); 544 } 545