xref: /illumos-gate/usr/src/psm/stand/cpr/sparcv9/sun4u/util.c (revision 587644a8567e6a9533f88401daa59cbd78c4632f)
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