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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/cpr.h> 30 #include <sys/promimpl.h> 31 #include "cprboot.h" 32 33 34 static int reset_input = 0; 35 static char kbd_input[] = "keyboard input"; 36 static char null_input[] = "\" /nulldev\" input"; 37 38 39 /* 40 * Ask prom to open a disk file given either the OBP device path, or the 41 * device path representing the target drive/partition and the fs-relative 42 * path of the file. Handle file pathnames with or without leading '/'. 43 * if fs points to a null char, it indicates that we are opening a device. 44 */ 45 /* ARGSUSED */ 46 int 47 cpr_statefile_open(char *path, char *fs) 48 { 49 char full_path[OBP_MAXPATHLEN]; 50 char *fp; 51 int handle; 52 int c; 53 54 /* 55 * instead of using specialstate, we use fs as the flag 56 */ 57 if (*fs == '\0') { /* device open */ 58 handle = prom_open(path); 59 /* IEEE1275 prom_open returns 0 on failure; we return -1 */ 60 return (handle ? handle : -1); 61 } 62 63 /* 64 * IEEE 1275 prom needs "device-path,|file-path" where 65 * file-path can have embedded |'s. 66 */ 67 fp = full_path; 68 (void) prom_strcpy(fp, fs); 69 fp += prom_strlen(fp); 70 *fp++ = ','; 71 *fp++ = '|'; 72 73 /* Skip a leading slash in file path -- we provided for it above. */ 74 if (*path == '/') 75 path++; 76 77 /* Copy file path and convert separators. */ 78 while ((c = *path++) != '\0') 79 if (c == '/') 80 *fp++ = '|'; 81 else 82 *fp++ = c; 83 *fp = '\0'; 84 85 handle = prom_open(full_path); 86 if (verbose) { 87 if (fp = prom_strrchr(full_path, '/')) 88 fp++; 89 else 90 fp = full_path; 91 prom_printf("cso: prom_open(\"%s\") = 0x%x\n", fp, handle); 92 } 93 94 /* 95 * IEEE1275 prom_open returns 0 on failure; we return -1 96 */ 97 return (handle ? handle : -1); 98 } 99 100 101 /* 102 * Ask prom to open a disk file given the device path representing 103 * the target drive/partition and the fs-relative path of the file. 104 * Handle file pathnames with or without leading '/'. if fs points 105 * to a null char, it indicates that we are opening a device. 106 */ 107 /* ARGSUSED */ 108 int 109 cpr_ufs_open(char *path, char *fs) 110 { 111 CB_VENTRY(cpr_ufs_open); 112 113 /* 114 * screen invalid state, then just use the other code rather than 115 * duplicating it 116 */ 117 if (*fs == '\0') { /* device open */ 118 prom_printf("cpr_ufs_open: NULL fs, path %s\n", path); 119 return (ERR); 120 } 121 return (cpr_statefile_open(path, fs)); 122 } 123 124 125 /* 126 * On sun4u there's no difference here, since prom groks ufs directly 127 */ 128 int 129 cpr_read(int fd, caddr_t buf, size_t len) 130 { 131 return (prom_read(fd, buf, len, 0, 0)); 132 } 133 134 135 int 136 cpr_ufs_read(int fd, caddr_t buf, int len) 137 { 138 return (prom_read(fd, buf, len, 0, 0)); 139 } 140 141 142 int 143 cpr_ufs_close(int fd) 144 { 145 CB_VPRINTF(("cpr_ufs_close 0x%x\n", fd)); 146 return (prom_close(fd)); 147 } 148 149 150 int 151 cpr_statefile_close(int fd) 152 { 153 return (prom_close(fd)); 154 } 155 156 157 void 158 cb_spin(void) 159 { 160 static int spindex = 0; 161 static char *spin_pairs[] = { "|\b", "/\b", "-\b", "\\\b" }; 162 const size_t nspin_pairs = sizeof (spin_pairs) / sizeof (spin_pairs[0]); 163 164 prom_printf(spin_pairs[spindex]); 165 spindex = (spindex + 1) % nspin_pairs; 166 } 167 168 169 /* 170 * translate vaddr to phys page number 171 */ 172 pfn_t 173 cpr_vatopfn(caddr_t vaddr) 174 { 175 physaddr_t paddr; 176 int valid, mode; 177 178 (void) prom_translate_virt(vaddr, &valid, &paddr, &mode); 179 if (valid != -1) 180 return (PFN_INVALID); 181 return (paddr >> MMU_PAGESHIFT); 182 } 183 184 185 /* 186 * unmap virt, then map virt to new phys; 187 * see remap definition below 188 */ 189 int 190 prom_remap(size_t size, caddr_t virt, physaddr_t phys) 191 { 192 ihandle_t immu; 193 cell_t ci[8]; 194 int rv; 195 196 immu = prom_mmu_ihandle(); 197 if (immu == (ihandle_t)-1) 198 return (ERR); 199 200 ci[0] = p1275_ptr2cell("call-method"); /* Service name */ 201 ci[1] = (cell_t)5; /* #argument cells */ 202 ci[2] = (cell_t)0; /* #result cells */ 203 ci[3] = p1275_ptr2cell("remap"); /* Arg1: Method name */ 204 ci[4] = p1275_ihandle2cell(immu); /* Arg2: memory ihandle */ 205 ci[5] = p1275_size2cell(size); /* remap arg0 */ 206 ci[6] = p1275_ptr2cell(virt); /* remap arg1 */ 207 ci[7] = p1275_ull2cell_low(phys); /* remap arg2 */ 208 209 promif_preprom(); 210 rv = p1275_cif_handler(ci); 211 promif_postprom(); 212 213 if (rv) 214 return (rv); /* Service "call-method" failed */ 215 return (0); 216 } 217 218 219 /* 220 * install remap definition in /virtual-memory node; 221 * used for replacing a virt->phys mapping in one promif call; 222 * this needs to be atomic from the client's perspective to 223 * avoid faults while relocating client text. 224 */ 225 void 226 install_remap(void) 227 { 228 static char remap_def[] = 229 "\" /virtual-memory\" find-device " 230 ": remap ( phys.lo virt size -- )" 231 " 2dup unmap ( phys.lo virt size )" 232 " 0 -rot -1 map ( ) ; " 233 "device-end"; 234 235 prom_interpret(remap_def, 0, 0, 0, 0, 0); 236 } 237 238 239 /* 240 * allocate virt and phys space without any mapping; 241 * stores virt and phys addrs at *vap and *pap 242 */ 243 int 244 cb_alloc(size_t size, uint_t align, caddr_t *vap, physaddr_t *pap) 245 { 246 physaddr_t phys; 247 caddr_t virt; 248 249 virt = prom_allocate_virt(align, (size_t)align); 250 if (virt == (caddr_t)-1) 251 return (ERR); 252 if (prom_allocate_phys(size, align, &phys) == -1) { 253 prom_free_virt(size, virt); 254 return (ERR); 255 } 256 257 *vap = virt; 258 *pap = phys; 259 return (0); 260 } 261 262 263 static int 264 get_intprop(dnode_t node, caddr_t prop, void *dst) 265 { 266 int len, glen; 267 268 len = sizeof (uint_t); 269 glen = prom_getprop(node, prop, dst); 270 if (glen != len) 271 return (ERR); 272 273 return (0); 274 } 275 276 277 /* 278 * find cpu node for the boot processor 279 * 280 * sets globals: 281 * cb_mid 282 */ 283 static dnode_t 284 get_cpu_node(void) 285 { 286 static char *props[] = { "upa-portid", "portid", NULL }; 287 dnode_t node; 288 char *str, *name, **propp; 289 uint_t cpu_id; 290 int err; 291 292 str = "get_cpu_node"; 293 name = "cpu"; 294 295 cb_mid = getmid(); 296 for (node = prom_rootnode(); ; node = prom_nextnode(node)) { 297 node = prom_findnode_bydevtype(node, name); 298 if (node == OBP_NONODE) { 299 prom_printf("\n%s: cant find node for devtype \"%s\"\n", 300 str, name); 301 break; 302 } 303 304 cpu_id = (uint_t)-1; 305 for (propp = props; *propp; propp++) { 306 err = get_intprop(node, *propp, &cpu_id); 307 CB_VPRINTF((" cpu node 0x%x, " 308 "prop \"%s\", cpu_id %d\n", 309 node, *propp, (int)cpu_id)); 310 if (err == 0) 311 break; 312 } 313 314 if (cpu_id == cb_mid) 315 return (node); 316 } 317 318 return (OBP_NONODE); 319 } 320 321 322 /* 323 * lookup prom properties 324 * 325 * sets globals: 326 * cb_dents 327 * cb_clock_freq 328 * cpu_delay 329 */ 330 int 331 cb_get_props(void) 332 { 333 uint_t clock_mhz; 334 dnode_t node; 335 struct cb_props *cbp; 336 static struct cb_props cpu_data[] = { 337 "#dtlb-entries", &cb_dents, 338 "clock-frequency", &cb_clock_freq, 339 NULL, NULL, 340 }; 341 342 CB_VENTRY(cb_get_props); 343 344 node = get_cpu_node(); 345 if (node == OBP_NONODE) 346 return (ERR); 347 for (cbp = cpu_data; cbp->prop; cbp++) { 348 if (get_intprop(node, cbp->prop, cbp->datap)) { 349 prom_printf("\n%s: getprop error, " 350 "node 0x%x, prop \"%s\"\n", 351 prog, node, cbp->prop); 352 return (ERR); 353 } 354 CB_VPRINTF((" \"%s\" = 0x%x\n", 355 cbp->prop, *cbp->datap)); 356 } 357 358 /* 359 * setup cpu_delay for cb_usec_wait 360 */ 361 clock_mhz = (cb_clock_freq + 500000) / 1000000; 362 cpu_delay = clock_mhz - 7; 363 CB_VPRINTF((" clock_mhz %d, cpu_delay %d\n", 364 clock_mhz, cpu_delay)); 365 366 return (0); 367 } 368 369 370 /* 371 * map-in data pages 372 * size should fit tte_bit.sz 373 * rw should be 0 or TTE_HWWR_INT 374 */ 375 void 376 cb_mapin(caddr_t vaddr, pfn_t ppn, uint_t size, uint_t rw, uint_t dtlb_index) 377 { 378 tte_t tte; 379 380 tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(size) | 381 TTE_PFN_INTHI(ppn); 382 tte.tte_intlo = TTE_PFN_INTLO(ppn) | TTE_LCK_INT | 383 TTE_CP_INT | TTE_CV_INT | TTE_PRIV_INT | rw; 384 set_dtlb_entry(dtlb_index, vaddr, &tte); 385 } 386 387 388 static char * 389 prom_strstr(char *string, char *substr) 390 { 391 char *strp, *subp, *tmp, c; 392 393 if (substr == NULL || *substr == '\0') 394 return (string); 395 396 strp = string; 397 subp = substr; 398 c = *subp; 399 400 while (*strp) { 401 if (*strp++ == c) { 402 tmp = strp; 403 while ((c = *++subp) == *strp++ && c) 404 ; 405 if (c == '\0') 406 return (tmp - 1); 407 strp = tmp; 408 subp = substr; 409 c = *subp; 410 } 411 } 412 413 return (NULL); 414 } 415 416 417 static void 418 cb_set_idev(char *istr) 419 { 420 if (reset_input) { 421 prom_interpret(istr, 0, 0, 0, 0, 0); 422 CB_VPRINTF(("\ncb_set_idev: reset with [%s]\n", istr)); 423 } 424 } 425 426 427 /* 428 * workaround for USB keyboard: 429 * USB DMA activity has been known to corrupt kernel pages while cprboot 430 * is restoring them. to quiesce the USB chip, we craft a "null" device 431 * and temporarily use that as the prom's input device. this effectively 432 * disables the USB keyboard until the cpr module restores the original 433 * prom and a kernel driver re-inits and takes-over control of USB. 434 * 435 * may set globals: 436 * reset_input 437 */ 438 int 439 cb_usb_setup(void) 440 { 441 char sp[OBP_MAXPATHLEN]; 442 static char cb_nulldev[] = { 443 "\" /\" select-dev " 444 "new-device " 445 "\" nulldev\" device-name " 446 ": read 2drop -2 ; " 447 ": open true ; " 448 ": close ; " 449 ": install-abort ; " 450 ": remove-abort ; " 451 ": write 2drop 0 ; " 452 ": restore ; " 453 "finish-device " 454 "unselect-dev" 455 }; 456 457 CB_VENTRY(cb_usb_setup); 458 459 bzero(sp, sizeof (sp)); 460 prom_interpret("stdin @ ihandle>devname swap -rot move", 461 (uintptr_t)sp, 0, 0, 0, 0); 462 if (prom_strstr(sp, "usb") && prom_strstr(sp, "keyboard")) { 463 prom_interpret(cb_nulldev, 0, 0, 0, 0, 0); 464 reset_input = 1; 465 cb_set_idev(null_input); 466 } 467 468 return (0); 469 } 470 471 472 /* 473 * switch input to keyboard before entering the prom, and switch to the 474 * crafted nulldev after returning from the prom. this occurs only when 475 * stdinpath is a USB keyboard; entering the prom is usually done only 476 * for debugging purposes - see check_halt() and above DMA comment. 477 */ 478 void 479 cb_enter_mon(void) 480 { 481 cb_set_idev(kbd_input); 482 prom_enter_mon(); 483 cb_set_idev(null_input); 484 } 485 486 487 /* 488 * similar to above before exiting to the prom 489 */ 490 void 491 cb_exit_to_mon(void) 492 { 493 cb_set_idev(kbd_input); 494 prom_exit_to_mon(); 495 } 496