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