xref: /illumos-gate/usr/src/psm/stand/cpr/sparcv9/sun4u/util.c (revision e38a713ad4e0a9c42f8cccd9350412b2c6ccccdb)
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 2007 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 /* ARGSUSED */
49 int
50 cpr_statefile_open(char *path, char *fs_dev)
51 {
52 	int plen, dlen;
53 	int handle;
54 	char fs_pkg[OBP_MAXPATHLEN];
55 	char fs_name[OBP_MAXDRVNAME];
56 
57 	/*
58 	 * instead of using specialstate, we use fs as the flag
59 	 */
60 	if (*fs_dev == '\0') {	/* device open */
61 		statefile_special = 1;
62 		handle = prom_open(path);
63 		/* IEEE1275 prom_open returns 0 on failure; we return -1 */
64 		return (handle ? handle : -1);
65 	}
66 
67 	/*
68 	 * No cif for $open-package, so we have to use interpret
69 	 */
70 	if (prom_getprop(chosen, "fs-package", fs_pkg) == -1) {
71 		prom_printf("Missing fs-package name\n");
72 		return (-1);
73 	}
74 	plen = prom_strlen(fs_pkg);
75 	dlen = prom_strlen(fs_dev);
76 	prom_interpret("$open-package swap l!", plen, (uintptr_t)fs_pkg,
77 	    dlen, (uintptr_t)fs_dev, (uintptr_t)&cb_rih);
78 	if (cb_rih == OBP_BADNODE || cb_rih == 0) {
79 		prom_printf("Can't open %s\n", fs_pkg);
80 		return (-1);
81 	}
82 
83 	/*
84 	 * Prepend '/' if it's not there already
85 	 */
86 	if (*path != '/') {
87 		(void) prom_sprintf(fs_name, "/%s", path);
88 		return (cpr_fs_open(fs_name));
89 	} else
90 		return (cpr_fs_open(path));
91 }
92 
93 /*
94  * Mount root fs so we can read statefile, etc
95  *
96  * sets global
97  *	cb_rih
98  */
99 int
100 cb_mountroot()
101 {
102 
103 	chosen = prom_chosennode();
104 	if (chosen == OBP_BADNODE) {
105 		prom_printf("Missing chosen node\n");
106 		return (ERR);
107 	}
108 	if (prom_getprop(chosen, "bootfs", (caddr_t)&cb_rih) == -1) {
109 		prom_printf("Missing bootfs ihandle\n");
110 		return (ERR);
111 	}
112 	return (0);
113 }
114 
115 /*
116  * Unmount root
117  */
118 int
119 cb_unmountroot()
120 {
121 	(void) prom_close(cb_rih);
122 	cb_rih = OBP_BADNODE;
123 	return (0);
124 }
125 
126 /*
127  * Ask prom to open a disk file.
128  */
129 /* ARGSUSED */
130 int
131 cpr_fs_open(char *path)
132 {
133 
134 	CB_VENTRY(cpr_fs_open);
135 
136 	if (cb_rih == OBP_BADNODE)
137 		return (-1);
138 	return (prom_fopen(cb_rih, path));
139 }
140 
141 
142 /*
143  * Direct read if using block special,
144  * otherwise use fs read
145  */
146 int
147 cpr_read(int fd, caddr_t buf, size_t len)
148 {
149 	if (!statefile_special)
150 		return (cpr_fs_read(fd, buf, len));
151 	else
152 		return (prom_read(fd, buf, len, 0, 0));
153 }
154 
155 
156 int
157 cpr_fs_read(int fd, caddr_t buf, int len)
158 {
159 	if (cb_rih == OBP_BADNODE)
160 		return (-1);
161 	return (prom_fread(cb_rih, fd, buf, len));
162 }
163 
164 
165 int
166 cpr_fs_close(int fd)
167 {
168 	CB_VPRINTF(("cpr_fs_close 0x%x\n", fd));
169 
170 	if (cb_rih == OBP_BADNODE)
171 		return (-1);
172 	prom_fclose(cb_rih, fd);
173 	return (0);
174 }
175 
176 int
177 cpr_fs_seek(int fd, offset_t off)
178 {
179 	if (cb_rih == OBP_BADNODE)
180 		return (-1);
181 	return (prom_fseek(cb_rih, fd, off));
182 }
183 
184 
185 int
186 cpr_statefile_close(int fd)
187 {
188 	if (statefile_special) {
189 		statefile_special = 0;
190 		return (prom_close(fd));
191 	} else
192 		return (cpr_fs_close(fd));
193 }
194 
195 
196 void
197 cb_spin(void)
198 {
199 	static int spindex = 0;
200 	static char *spin_pairs[] = { "|\b", "/\b", "-\b", "\\\b" };
201 	const size_t nspin_pairs = sizeof (spin_pairs) / sizeof (spin_pairs[0]);
202 
203 	prom_printf(spin_pairs[spindex]);
204 	spindex = (spindex + 1) % nspin_pairs;
205 }
206 
207 
208 /*
209  * translate vaddr to phys page number
210  */
211 pfn_t
212 cpr_vatopfn(caddr_t vaddr)
213 {
214 	physaddr_t paddr;
215 	int valid, mode;
216 
217 	(void) prom_translate_virt(vaddr, &valid, &paddr, &mode);
218 	if (valid != -1)
219 		return (PFN_INVALID);
220 	return (paddr >> MMU_PAGESHIFT);
221 }
222 
223 
224 /*
225  * unmap virt, then map virt to new phys;
226  * see remap definition below
227  */
228 int
229 prom_remap(size_t size, caddr_t virt, physaddr_t phys)
230 {
231 	ihandle_t immu;
232 	cell_t ci[8];
233 	int rv;
234 
235 	immu = prom_mmu_ihandle();
236 	if (immu == (ihandle_t)-1)
237 		return (ERR);
238 
239 	ci[0] = p1275_ptr2cell("call-method");	/* Service name */
240 	ci[1] = (cell_t)5;			/* #argument cells */
241 	ci[2] = (cell_t)0;			/* #result cells */
242 	ci[3] = p1275_ptr2cell("remap");	/* Arg1: Method name */
243 	ci[4] = p1275_ihandle2cell(immu);	/* Arg2: memory ihandle */
244 	ci[5] = p1275_size2cell(size);		/* remap arg0 */
245 	ci[6] = p1275_ptr2cell(virt);		/* remap arg1 */
246 	ci[7] = p1275_ull2cell_low(phys);	/* remap arg2 */
247 
248 	promif_preprom();
249 	rv = p1275_cif_handler(ci);
250 	promif_postprom();
251 
252 	if (rv)
253 		return (rv);		/* Service "call-method" failed */
254 	return (0);
255 }
256 
257 
258 /*
259  * install remap definition in /virtual-memory node;
260  * used for replacing a virt->phys mapping in one promif call;
261  * this needs to be atomic from the client's perspective to
262  * avoid faults while relocating client text.
263  */
264 void
265 install_remap(void)
266 {
267 	static char remap_def[] =
268 	    "\" /virtual-memory\" find-device "
269 	    ": remap ( phys.lo virt size -- )"
270 	    "	2dup unmap ( phys.lo virt size )"
271 	    "	0 -rot -1 map ( ) ; "
272 	    "device-end";
273 
274 	prom_interpret(remap_def, 0, 0, 0, 0, 0);
275 }
276 
277 
278 /*
279  * allocate virt and phys space without any mapping;
280  * stores virt and phys addrs at *vap and *pap
281  */
282 int
283 cb_alloc(size_t size, uint_t align, caddr_t *vap, physaddr_t *pap)
284 {
285 	physaddr_t phys;
286 	caddr_t virt;
287 
288 	virt = prom_allocate_virt(align, (size_t)align);
289 	if (virt == (caddr_t)-1)
290 		return (ERR);
291 	if (prom_allocate_phys(size, align, &phys) == -1) {
292 		prom_free_virt(size, virt);
293 		return (ERR);
294 	}
295 
296 	*vap = virt;
297 	*pap = phys;
298 	return (0);
299 }
300 
301 
302 static int
303 get_intprop(pnode_t node, caddr_t prop, void *dst)
304 {
305 	int len, glen;
306 
307 	len = sizeof (uint_t);
308 	glen = prom_getprop(node, prop, dst);
309 	if (glen != len)
310 		return (ERR);
311 
312 	return (0);
313 }
314 
315 
316 /*
317  * find cpu node for the boot processor
318  *
319  * sets globals:
320  * 	cb_mid
321  */
322 static pnode_t
323 get_cpu_node(void)
324 {
325 	static char *props[] = { "upa-portid", "portid", NULL };
326 	pnode_t node;
327 	char *str, *name, **propp;
328 	uint_t cpu_id;
329 	int err;
330 
331 	str = "get_cpu_node";
332 	name = "cpu";
333 
334 	cb_mid = getmid();
335 	for (node = prom_rootnode(); ; node = prom_nextnode(node)) {
336 		node = prom_findnode_bydevtype(node, name);
337 		if (node == OBP_NONODE) {
338 			prom_printf("\n%s: cant find node for devtype \"%s\"\n",
339 			    str, name);
340 			break;
341 		}
342 
343 		cpu_id = (uint_t)-1;
344 		for (propp = props; *propp; propp++) {
345 			err = get_intprop(node, *propp, &cpu_id);
346 			CB_VPRINTF(("    cpu node 0x%x, "
347 			    "prop \"%s\", cpu_id %d\n",
348 			    node, *propp, (int)cpu_id));
349 			if (err == 0)
350 				break;
351 		}
352 
353 		if (cpu_id == cb_mid)
354 			return (node);
355 	}
356 
357 	return (OBP_NONODE);
358 }
359 
360 
361 /*
362  * lookup prom properties
363  *
364  * sets globals:
365  *	cb_dents
366  *	cb_clock_freq
367  *	cpu_delay
368  */
369 int
370 cb_get_props(void)
371 {
372 	uint_t clock_mhz;
373 	pnode_t node;
374 	struct cb_props *cbp;
375 	static struct cb_props cpu_data[] = {
376 		"#dtlb-entries", &cb_dents,
377 		"clock-frequency", &cb_clock_freq,
378 		NULL, NULL,
379 	};
380 
381 	CB_VENTRY(cb_get_props);
382 
383 	node = get_cpu_node();
384 	if (node == OBP_NONODE)
385 		return (ERR);
386 	for (cbp = cpu_data; cbp->prop; cbp++) {
387 		if (get_intprop(node, cbp->prop, cbp->datap)) {
388 			prom_printf("\n%s: getprop error, "
389 			    "node 0x%x, prop \"%s\"\n",
390 			    prog, node, cbp->prop);
391 			return (ERR);
392 		}
393 		CB_VPRINTF(("    \"%s\" = 0x%x\n",
394 		    cbp->prop, *cbp->datap));
395 	}
396 
397 	/*
398 	 * setup cpu_delay for cb_usec_wait
399 	 */
400 	clock_mhz = (cb_clock_freq + 500000) / 1000000;
401 	cpu_delay = clock_mhz - 7;
402 	CB_VPRINTF(("    clock_mhz %d, cpu_delay %d\n",
403 	    clock_mhz, cpu_delay));
404 
405 	return (0);
406 }
407 
408 
409 /*
410  * map-in data pages
411  * size should fit tte_bit.sz
412  * rw should be 0 or TTE_HWWR_INT
413  */
414 void
415 cb_mapin(caddr_t vaddr, pfn_t ppn, uint_t size, uint_t rw, uint_t dtlb_index)
416 {
417 	tte_t tte;
418 
419 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(size) |
420 	    TTE_PFN_INTHI(ppn);
421 	tte.tte_intlo = TTE_PFN_INTLO(ppn) | TTE_LCK_INT |
422 	    TTE_CP_INT | TTE_CV_INT | TTE_PRIV_INT | rw;
423 	set_dtlb_entry(dtlb_index, vaddr, &tte);
424 }
425 
426 
427 static char *
428 prom_strstr(char *string, char *substr)
429 {
430 	char *strp, *subp, *tmp, c;
431 
432 	if (substr == NULL || *substr == '\0')
433 		return (string);
434 
435 	strp = string;
436 	subp = substr;
437 	c = *subp;
438 
439 	while (*strp) {
440 		if (*strp++ == c) {
441 			tmp = strp;
442 			while ((c = *++subp) == *strp++ && c)
443 				;
444 			if (c == '\0')
445 				return (tmp - 1);
446 			strp = tmp;
447 			subp = substr;
448 			c = *subp;
449 		}
450 	}
451 
452 	return (NULL);
453 }
454 
455 
456 static void
457 cb_set_idev(char *istr)
458 {
459 	if (reset_input) {
460 		prom_interpret(istr, 0, 0, 0, 0, 0);
461 		CB_VPRINTF(("\ncb_set_idev: reset with [%s]\n", istr));
462 	}
463 }
464 
465 
466 /*
467  * workaround for USB keyboard:
468  * USB DMA activity has been known to corrupt kernel pages while cprboot
469  * is restoring them.  to quiesce the USB chip, we craft a "null" device
470  * and temporarily use that as the prom's input device.  this effectively
471  * disables the USB keyboard until the cpr module restores the original
472  * prom and a kernel driver re-inits and takes-over control of USB.
473  *
474  * may set globals:
475  *	reset_input
476  */
477 int
478 cb_usb_setup(void)
479 {
480 	char sp[OBP_MAXPATHLEN];
481 	static char cb_nulldev[] = {
482 		"\" /\" select-dev "
483 		"new-device "
484 		"\" nulldev\" device-name "
485 		": read 2drop -2 ; "
486 		": open true ; "
487 		": close ; "
488 		": install-abort ; "
489 		": remove-abort ; "
490 		": write 2drop 0 ; "
491 		": restore ; "
492 		"finish-device "
493 		"unselect-dev"
494 	};
495 
496 	CB_VENTRY(cb_usb_setup);
497 
498 	bzero(sp, sizeof (sp));
499 	prom_interpret("stdin @ ihandle>devname swap -rot move",
500 	    (uintptr_t)sp, 0, 0, 0, 0);
501 	if (prom_strstr(sp, "usb") && prom_strstr(sp, "keyboard")) {
502 		prom_interpret(cb_nulldev, 0, 0, 0, 0, 0);
503 		reset_input = 1;
504 		cb_set_idev(null_input);
505 	}
506 
507 	return (0);
508 }
509 
510 
511 /*
512  * switch input to keyboard before entering the prom, and switch to the
513  * crafted nulldev after returning from the prom.  this occurs only when
514  * stdinpath is a USB keyboard; entering the prom is usually done only
515  * for debugging purposes - see check_halt() and above DMA comment.
516  */
517 void
518 cb_enter_mon(void)
519 {
520 	cb_set_idev(kbd_input);
521 	prom_enter_mon();
522 	cb_set_idev(null_input);
523 }
524 
525 
526 /*
527  * similar to above before exiting to the prom
528  */
529 void
530 cb_exit_to_mon(void)
531 {
532 	cb_set_idev(kbd_input);
533 	prom_exit_to_mon();
534 }
535