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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/cpr.h>
30 #include <sys/promimpl.h>
31 #include "cprboot.h"
32 
33 
34 static int reset_input = 0;
35 static char kbd_input[] = "keyboard input";
36 static char null_input[] = "\" /nulldev\" input";
37 
38 
39 /*
40  * Ask prom to open a disk file given either the OBP device path, or the
41  * device path representing the target drive/partition and the fs-relative
42  * path of the file.  Handle file pathnames with or without leading '/'.
43  * if fs points to a null char, it indicates that we are opening a device.
44  */
45 /* ARGSUSED */
46 int
47 cpr_statefile_open(char *path, char *fs)
48 {
49 	char full_path[OBP_MAXPATHLEN];
50 	char *fp;
51 	int handle;
52 	int c;
53 
54 	/*
55 	 * instead of using specialstate, we use fs as the flag
56 	 */
57 	if (*fs == '\0') {	/* device open */
58 		handle = prom_open(path);
59 		/* IEEE1275 prom_open returns 0 on failure; we return -1 */
60 		return (handle ? handle : -1);
61 	}
62 
63 	/*
64 	 * IEEE 1275 prom needs "device-path,|file-path" where
65 	 * file-path can have embedded |'s.
66 	 */
67 	fp = full_path;
68 	(void) prom_strcpy(fp, fs);
69 	fp += prom_strlen(fp);
70 	*fp++ = ',';
71 	*fp++ = '|';
72 
73 	/* Skip a leading slash in file path -- we provided for it above. */
74 	if (*path == '/')
75 		path++;
76 
77 	/* Copy file path and convert separators. */
78 	while ((c = *path++) != '\0')
79 		if (c == '/')
80 			*fp++ = '|';
81 		else
82 			*fp++ = c;
83 	*fp = '\0';
84 
85 	handle = prom_open(full_path);
86 	if (verbose) {
87 		if (fp = prom_strrchr(full_path, '/'))
88 			fp++;
89 		else
90 			fp = full_path;
91 		prom_printf("cso: prom_open(\"%s\") = 0x%x\n", fp, handle);
92 	}
93 
94 	/*
95 	 * IEEE1275 prom_open returns 0 on failure; we return -1
96 	 */
97 	return (handle ? handle : -1);
98 }
99 
100 
101 /*
102  * Ask prom to open a disk file given the device path representing
103  * the target drive/partition and the fs-relative path of the file.
104  * Handle file pathnames with or without leading '/'.  if fs points
105  * to a null char, it indicates that we are opening a device.
106  */
107 /* ARGSUSED */
108 int
109 cpr_ufs_open(char *path, char *fs)
110 {
111 	CB_VENTRY(cpr_ufs_open);
112 
113 	/*
114 	 * screen invalid state, then just use the other code rather than
115 	 * duplicating it
116 	 */
117 	if (*fs == '\0') {	/* device open */
118 		prom_printf("cpr_ufs_open: NULL fs, path %s\n", path);
119 		return (ERR);
120 	}
121 	return (cpr_statefile_open(path, fs));
122 }
123 
124 
125 /*
126  * On sun4u there's no difference here, since prom groks ufs directly
127  */
128 int
129 cpr_read(int fd, caddr_t buf, size_t len)
130 {
131 	return (prom_read(fd, buf, len, 0, 0));
132 }
133 
134 
135 int
136 cpr_ufs_read(int fd, caddr_t buf, int len)
137 {
138 	return (prom_read(fd, buf, len, 0, 0));
139 }
140 
141 
142 int
143 cpr_ufs_close(int fd)
144 {
145 	CB_VPRINTF(("cpr_ufs_close 0x%x\n", fd));
146 	return (prom_close(fd));
147 }
148 
149 
150 int
151 cpr_statefile_close(int fd)
152 {
153 	return (prom_close(fd));
154 }
155 
156 
157 void
158 cb_spin(void)
159 {
160 	static int spindex = 0;
161 	static char *spin_pairs[] = { "|\b", "/\b", "-\b", "\\\b" };
162 	const size_t nspin_pairs = sizeof (spin_pairs) / sizeof (spin_pairs[0]);
163 
164 	prom_printf(spin_pairs[spindex]);
165 	spindex = (spindex + 1) % nspin_pairs;
166 }
167 
168 
169 /*
170  * translate vaddr to phys page number
171  */
172 pfn_t
173 cpr_vatopfn(caddr_t vaddr)
174 {
175 	physaddr_t paddr;
176 	int valid, mode;
177 
178 	(void) prom_translate_virt(vaddr, &valid, &paddr, &mode);
179 	if (valid != -1)
180 		return (PFN_INVALID);
181 	return (paddr >> MMU_PAGESHIFT);
182 }
183 
184 
185 /*
186  * unmap virt, then map virt to new phys;
187  * see remap definition below
188  */
189 int
190 prom_remap(size_t size, caddr_t virt, physaddr_t phys)
191 {
192 	ihandle_t immu;
193 	cell_t ci[8];
194 	int rv;
195 
196 	immu = prom_mmu_ihandle();
197 	if (immu == (ihandle_t)-1)
198 		return (ERR);
199 
200 	ci[0] = p1275_ptr2cell("call-method");	/* Service name */
201 	ci[1] = (cell_t)5;			/* #argument cells */
202 	ci[2] = (cell_t)0;			/* #result cells */
203 	ci[3] = p1275_ptr2cell("remap");	/* Arg1: Method name */
204 	ci[4] = p1275_ihandle2cell(immu);	/* Arg2: memory ihandle */
205 	ci[5] = p1275_size2cell(size);		/* remap arg0 */
206 	ci[6] = p1275_ptr2cell(virt);		/* remap arg1 */
207 	ci[7] = p1275_ull2cell_low(phys);	/* remap arg2 */
208 
209 	promif_preprom();
210 	rv = p1275_cif_handler(ci);
211 	promif_postprom();
212 
213 	if (rv)
214 		return (rv);		/* Service "call-method" failed */
215 	return (0);
216 }
217 
218 
219 /*
220  * install remap definition in /virtual-memory node;
221  * used for replacing a virt->phys mapping in one promif call;
222  * this needs to be atomic from the client's perspective to
223  * avoid faults while relocating client text.
224  */
225 void
226 install_remap(void)
227 {
228 	static char remap_def[] =
229 	    "\" /virtual-memory\" find-device "
230 	    ": remap ( phys.lo virt size -- )"
231 	    "	2dup unmap ( phys.lo virt size )"
232 	    "	0 -rot -1 map ( ) ; "
233 	    "device-end";
234 
235 	prom_interpret(remap_def, 0, 0, 0, 0, 0);
236 }
237 
238 
239 /*
240  * allocate virt and phys space without any mapping;
241  * stores virt and phys addrs at *vap and *pap
242  */
243 int
244 cb_alloc(size_t size, uint_t align, caddr_t *vap, physaddr_t *pap)
245 {
246 	physaddr_t phys;
247 	caddr_t virt;
248 
249 	virt = prom_allocate_virt(align, (size_t)align);
250 	if (virt == (caddr_t)-1)
251 		return (ERR);
252 	if (prom_allocate_phys(size, align, &phys) == -1) {
253 		prom_free_virt(size, virt);
254 		return (ERR);
255 	}
256 
257 	*vap = virt;
258 	*pap = phys;
259 	return (0);
260 }
261 
262 
263 static int
264 get_intprop(dnode_t node, caddr_t prop, void *dst)
265 {
266 	int len, glen;
267 
268 	len = sizeof (uint_t);
269 	glen = prom_getprop(node, prop, dst);
270 	if (glen != len)
271 		return (ERR);
272 
273 	return (0);
274 }
275 
276 
277 /*
278  * find cpu node for the boot processor
279  *
280  * sets globals:
281  * 	cb_mid
282  */
283 static dnode_t
284 get_cpu_node(void)
285 {
286 	static char *props[] = { "upa-portid", "portid", NULL };
287 	dnode_t node;
288 	char *str, *name, **propp;
289 	uint_t cpu_id;
290 	int err;
291 
292 	str = "get_cpu_node";
293 	name = "cpu";
294 
295 	cb_mid = getmid();
296 	for (node = prom_rootnode(); ; node = prom_nextnode(node)) {
297 		node = prom_findnode_bydevtype(node, name);
298 		if (node == OBP_NONODE) {
299 			prom_printf("\n%s: cant find node for devtype \"%s\"\n",
300 			    str, name);
301 			break;
302 		}
303 
304 		cpu_id = (uint_t)-1;
305 		for (propp = props; *propp; propp++) {
306 			err = get_intprop(node, *propp, &cpu_id);
307 			CB_VPRINTF(("    cpu node 0x%x, "
308 			    "prop \"%s\", cpu_id %d\n",
309 			    node, *propp, (int)cpu_id));
310 			if (err == 0)
311 				break;
312 		}
313 
314 		if (cpu_id == cb_mid)
315 			return (node);
316 	}
317 
318 	return (OBP_NONODE);
319 }
320 
321 
322 /*
323  * lookup prom properties
324  *
325  * sets globals:
326  *	cb_dents
327  *	cb_clock_freq
328  *	cpu_delay
329  */
330 int
331 cb_get_props(void)
332 {
333 	uint_t clock_mhz;
334 	dnode_t node;
335 	struct cb_props *cbp;
336 	static struct cb_props cpu_data[] = {
337 		"#dtlb-entries", &cb_dents,
338 		"clock-frequency", &cb_clock_freq,
339 		NULL, NULL,
340 	};
341 
342 	CB_VENTRY(cb_get_props);
343 
344 	node = get_cpu_node();
345 	if (node == OBP_NONODE)
346 		return (ERR);
347 	for (cbp = cpu_data; cbp->prop; cbp++) {
348 		if (get_intprop(node, cbp->prop, cbp->datap)) {
349 			prom_printf("\n%s: getprop error, "
350 			    "node 0x%x, prop \"%s\"\n",
351 			    prog, node, cbp->prop);
352 			return (ERR);
353 		}
354 		CB_VPRINTF(("    \"%s\" = 0x%x\n",
355 		    cbp->prop, *cbp->datap));
356 	}
357 
358 	/*
359 	 * setup cpu_delay for cb_usec_wait
360 	 */
361 	clock_mhz = (cb_clock_freq + 500000) / 1000000;
362 	cpu_delay = clock_mhz - 7;
363 	CB_VPRINTF(("    clock_mhz %d, cpu_delay %d\n",
364 	    clock_mhz, cpu_delay));
365 
366 	return (0);
367 }
368 
369 
370 /*
371  * map-in data pages
372  * size should fit tte_bit.sz
373  * rw should be 0 or TTE_HWWR_INT
374  */
375 void
376 cb_mapin(caddr_t vaddr, pfn_t ppn, uint_t size, uint_t rw, uint_t dtlb_index)
377 {
378 	tte_t tte;
379 
380 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(size) |
381 	    TTE_PFN_INTHI(ppn);
382 	tte.tte_intlo = TTE_PFN_INTLO(ppn) | TTE_LCK_INT |
383 	    TTE_CP_INT | TTE_CV_INT | TTE_PRIV_INT | rw;
384 	set_dtlb_entry(dtlb_index, vaddr, &tte);
385 }
386 
387 
388 static char *
389 prom_strstr(char *string, char *substr)
390 {
391 	char *strp, *subp, *tmp, c;
392 
393 	if (substr == NULL || *substr == '\0')
394 		return (string);
395 
396 	strp = string;
397 	subp = substr;
398 	c = *subp;
399 
400 	while (*strp) {
401 		if (*strp++ == c) {
402 			tmp = strp;
403 			while ((c = *++subp) == *strp++ && c)
404 				;
405 			if (c == '\0')
406 				return (tmp - 1);
407 			strp = tmp;
408 			subp = substr;
409 			c = *subp;
410 		}
411 	}
412 
413 	return (NULL);
414 }
415 
416 
417 static void
418 cb_set_idev(char *istr)
419 {
420 	if (reset_input) {
421 		prom_interpret(istr, 0, 0, 0, 0, 0);
422 		CB_VPRINTF(("\ncb_set_idev: reset with [%s]\n", istr));
423 	}
424 }
425 
426 
427 /*
428  * workaround for USB keyboard:
429  * USB DMA activity has been known to corrupt kernel pages while cprboot
430  * is restoring them.  to quiesce the USB chip, we craft a "null" device
431  * and temporarily use that as the prom's input device.  this effectively
432  * disables the USB keyboard until the cpr module restores the original
433  * prom and a kernel driver re-inits and takes-over control of USB.
434  *
435  * may set globals:
436  *	reset_input
437  */
438 int
439 cb_usb_setup(void)
440 {
441 	char sp[OBP_MAXPATHLEN];
442 	static char cb_nulldev[] = {
443 		"\" /\" select-dev "
444 		"new-device "
445 		"\" nulldev\" device-name "
446 		": read 2drop -2 ; "
447 		": open true ; "
448 		": close ; "
449 		": install-abort ; "
450 		": remove-abort ; "
451 		": write 2drop 0 ; "
452 		": restore ; "
453 		"finish-device "
454 		"unselect-dev"
455 	};
456 
457 	CB_VENTRY(cb_usb_setup);
458 
459 	bzero(sp, sizeof (sp));
460 	prom_interpret("stdin @ ihandle>devname swap -rot move",
461 	    (uintptr_t)sp, 0, 0, 0, 0);
462 	if (prom_strstr(sp, "usb") && prom_strstr(sp, "keyboard")) {
463 		prom_interpret(cb_nulldev, 0, 0, 0, 0, 0);
464 		reset_input = 1;
465 		cb_set_idev(null_input);
466 	}
467 
468 	return (0);
469 }
470 
471 
472 /*
473  * switch input to keyboard before entering the prom, and switch to the
474  * crafted nulldev after returning from the prom.  this occurs only when
475  * stdinpath is a USB keyboard; entering the prom is usually done only
476  * for debugging purposes - see check_halt() and above DMA comment.
477  */
478 void
479 cb_enter_mon(void)
480 {
481 	cb_set_idev(kbd_input);
482 	prom_enter_mon();
483 	cb_set_idev(null_input);
484 }
485 
486 
487 /*
488  * similar to above before exiting to the prom
489  */
490 void
491 cb_exit_to_mon(void)
492 {
493 	cb_set_idev(kbd_input);
494 	prom_exit_to_mon();
495 }
496