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