xref: /illumos-gate/usr/src/psm/stand/cpr/sparcv9/sun4u/util.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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
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
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
120 cb_unmountroot()
121 {
122 	(void) prom_close(cb_rih);
123 	cb_rih = OBP_BADNODE;
124 	return (0);
125 }
126 
127 int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
540 cb_exit_to_mon(void)
541 {
542 	cb_set_idev(kbd_input);
543 	prom_exit_to_mon();
544 }
545