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