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
cpr_statefile_open(char * path,char * fs_dev)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
cb_mountroot()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
cb_unmountroot()122 cb_unmountroot()
123 {
124 (void) prom_close(cb_rih);
125 cb_rih = OBP_BADNODE;
126 return (0);
127 }
128
129 int
cpr_fs_volopen(char * path)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
cpr_fs_open(char * path)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
cpr_read(int fd,caddr_t buf,size_t len)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
cpr_fs_read(int fd,caddr_t buf,int len)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
cpr_fs_close(int fd)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
cpr_fs_seek(int fd,offset_t off)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
cpr_statefile_close(int fd)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
cb_spin(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
cpr_vatopfn(caddr_t vaddr)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
prom_remap(size_t size,caddr_t virt,physaddr_t phys)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
install_remap(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
cb_alloc(size_t size,uint_t align,caddr_t * vap,physaddr_t * pap)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
get_intprop(pnode_t node,caddr_t prop,void * dst)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
get_cpu_node(void)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
cb_get_props(void)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
cb_mapin(caddr_t vaddr,pfn_t ppn,uint_t size,uint_t rw,uint_t dtlb_index)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 *
prom_strstr(char * string,char * substr)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
cb_set_idev(char * istr)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
cb_usb_setup(void)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
cb_enter_mon(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
cb_exit_to_mon(void)543 cb_exit_to_mon(void)
544 {
545 cb_set_idev(kbd_input);
546 prom_exit_to_mon();
547 }
548