1 /*	$NetBSD: ofw_machdep.c,v 1.46 2016/07/07 06:55:38 msaitoh Exp $	*/
2 
3 /*
4  * Copyright (C) 1996 Wolfgang Solfrank.
5  * Copyright (C) 1996 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "opt_multiprocessor.h"
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.46 2016/07/07 06:55:38 msaitoh Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/buf.h>
41 #include <sys/conf.h>
42 #include <sys/device.h>
43 #include <sys/disk.h>
44 #include <sys/disklabel.h>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/kprintf.h>
48 #include <sys/malloc.h>
49 #include <sys/stat.h>
50 #include <sys/systm.h>
51 
52 #include <machine/openfirm.h>
53 #include <machine/promlib.h>
54 
55 #include <dev/ofw/ofw_pci.h>
56 
57 #include <machine/sparc64.h>
58 
59 static u_int mmuh = -1, memh = -1;
60 
61 static u_int get_mmu_handle(void);
62 static u_int get_memory_handle(void);
63 
64 static u_int
get_mmu_handle(void)65 get_mmu_handle(void)
66 {
67 	u_int chosen;
68 
69 	if ((chosen = OF_finddevice("/chosen")) == -1) {
70 		prom_printf("get_mmu_handle: cannot get /chosen\n");
71 		return -1;
72 	}
73 	if (OF_getprop(chosen, "mmu", &mmuh, sizeof(mmuh)) == -1) {
74 		prom_printf("get_mmu_handle: cannot get mmuh\n");
75 		return -1;
76 	}
77 	return mmuh;
78 }
79 
80 static u_int
get_memory_handle(void)81 get_memory_handle(void)
82 {
83 	u_int chosen;
84 
85 	if ((chosen = OF_finddevice("/chosen")) == -1) {
86 		prom_printf("get_memory_handle: cannot get /chosen\n");
87 		return -1;
88 	}
89 	if (OF_getprop(chosen, "memory", &memh, sizeof(memh)) == -1) {
90 		prom_printf("get_memory_handle: cannot get memh\n");
91 		return -1;
92 	}
93 	return memh;
94 }
95 
96 
97 /*
98  * Point prom to our sun4u trap table.  This stops the prom from mapping us.
99  */
100 int
prom_set_trap_table_sun4u(vaddr_t tba)101 prom_set_trap_table_sun4u(vaddr_t tba)
102 {
103 	struct {
104 		cell_t name;
105 		cell_t nargs;
106 		cell_t nreturns;
107 		cell_t tba;
108 	} args;
109 
110 	args.name = ADR2CELL(&"SUNW,set-trap-table");
111 	args.nargs = 1;
112 	args.nreturns = 0;
113 	args.tba = ADR2CELL(tba);
114 	return openfirmware(&args);
115 }
116 
117 #ifdef SUN4V
118 /*
119  * Point prom to our sun4v trap table.  This stops the prom from mapping us.
120  */
121 int
prom_set_trap_table_sun4v(vaddr_t tba,paddr_t mmfsa)122 prom_set_trap_table_sun4v(vaddr_t tba, paddr_t mmfsa)
123 {
124 	struct {
125 		cell_t name;
126 		cell_t nargs;
127 		cell_t nreturns;
128 		cell_t tba;
129 		cell_t mmfsa;
130 	} args;
131 
132 	args.name = ADR2CELL("SUNW,set-trap-table");
133 	args.nargs = 2;
134 	args.nreturns = 0;
135 	args.tba = ADR2CELL(tba);
136 	args.mmfsa = ADR2CELL(mmfsa);
137 	return openfirmware(&args);
138 }
139 #endif
140 
141 /*
142  * Have the prom convert from virtual to physical addresses.
143  *
144  * Only works while the prom is actively mapping us.
145  */
146 paddr_t
prom_vtop(vaddr_t vaddr)147 prom_vtop(vaddr_t vaddr)
148 {
149 	struct {
150 		cell_t name;
151 		cell_t nargs;
152 		cell_t nreturns;
153 		cell_t method;
154 		cell_t ihandle;
155 		cell_t vaddr;
156 		cell_t status;
157 		cell_t retaddr;
158 		cell_t mode;
159 		cell_t phys_hi;
160 		cell_t phys_lo;
161 	} args;
162 
163 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
164 		prom_printf("prom_vtop: cannot get mmuh\n");
165 		return 0;
166 	}
167 	args.name = ADR2CELL(&"call-method");
168 	args.nargs = 3;
169 	args.nreturns = 5;
170 	args.method = ADR2CELL(&"translate");
171 	args.ihandle = HDL2CELL(mmuh);
172 	args.vaddr = ADR2CELL(vaddr);
173 	if (openfirmware(&args) == -1)
174 		return -1;
175 #if 0
176 	prom_printf("Called \"translate\", mmuh=%x, vaddr=%x, status=%x %x,\n retaddr=%x %x, mode=%x %x, phys_hi=%x %x, phys_lo=%x %x\n",
177 		    mmuh, vaddr, (int)(args.status>>32), (int)args.status, (int)(args.retaddr>>32), (int)args.retaddr,
178 		    (int)(args.mode>>32), (int)args.mode, (int)(args.phys_hi>>32), (int)args.phys_hi,
179 		    (int)(args.phys_lo>>32), (int)args.phys_lo);
180 #endif
181 	return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
182 }
183 
184 /*
185  * Grab some address space from the prom
186  *
187  * Only works while the prom is actively mapping us.
188  */
189 vaddr_t
prom_claim_virt(vaddr_t vaddr,int len)190 prom_claim_virt(vaddr_t vaddr, int len)
191 {
192 	struct {
193 		cell_t name;
194 		cell_t nargs;
195 		cell_t nreturns;
196 		cell_t method;
197 		cell_t ihandle;
198 		cell_t align;
199 		cell_t len;
200 		cell_t vaddr;
201 		cell_t status;
202 		cell_t retaddr;
203 	} args;
204 
205 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
206 		prom_printf("prom_claim_virt: cannot get mmuh\n");
207 		return 0;
208 	}
209 	args.name = ADR2CELL(&"call-method");
210 	args.nargs = 5;
211 	args.nreturns = 2;
212 	args.method = ADR2CELL(&"claim");
213 	args.ihandle = HDL2CELL(mmuh);
214 	args.align = 0;
215 	args.len = len;
216 	args.vaddr = ADR2CELL(vaddr);
217 	if (openfirmware(&args) == -1)
218 		return -1;
219 	return (vaddr_t)args.retaddr;
220 }
221 
222 /*
223  * Request some address space from the prom
224  *
225  * Only works while the prom is actively mapping us.
226  */
227 vaddr_t
prom_alloc_virt(int len,int align)228 prom_alloc_virt(int len, int align)
229 {
230 	struct {
231 		cell_t name;
232 		cell_t nargs;
233 		cell_t nreturns;
234 		cell_t method;
235 		cell_t ihandle;
236 		cell_t align;
237 		cell_t len;
238 		cell_t status;
239 		cell_t retaddr;
240 	} args;
241 
242 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
243 		prom_printf("prom_alloc_virt: cannot get mmuh\n");
244 		return -1LL;
245 	}
246 	args.name = ADR2CELL(&"call-method");
247 	args.nargs = 4;
248 	args.nreturns = 2;
249 	args.method = ADR2CELL(&"claim");
250 	args.ihandle = HDL2CELL(mmuh);
251 	args.align = align;
252 	args.len = len;
253 	if (openfirmware(&args) != 0)
254 		return -1;
255 	return (vaddr_t)args.retaddr;
256 }
257 
258 /*
259  * Release some address space to the prom
260  *
261  * Only works while the prom is actively mapping us.
262  */
263 int
prom_free_virt(vaddr_t vaddr,int len)264 prom_free_virt(vaddr_t vaddr, int len)
265 {
266 	struct {
267 		cell_t name;
268 		cell_t nargs;
269 		cell_t nreturns;
270 		cell_t method;
271 		cell_t ihandle;
272 		cell_t len;
273 		cell_t vaddr;
274 	} args;
275 
276 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
277 		prom_printf("prom_free_virt: cannot get mmuh\n");
278 		return -1;
279 	}
280 	args.name = ADR2CELL(&"call-method");
281 	args.nargs = 4;
282 	args.nreturns = 0;
283 	args.method = ADR2CELL(&"release");
284 	args.ihandle = HDL2CELL(mmuh);
285 	args.vaddr = ADR2CELL(vaddr);
286 	args.len = len;
287 	return openfirmware(&args);
288 }
289 
290 
291 /*
292  * Unmap some address space
293  *
294  * Only works while the prom is actively mapping us.
295  */
296 int
prom_unmap_virt(vaddr_t vaddr,int len)297 prom_unmap_virt(vaddr_t vaddr, int len)
298 {
299 	struct {
300 		cell_t name;
301 		cell_t nargs;
302 		cell_t nreturns;
303 		cell_t method;
304 		cell_t ihandle;
305 		cell_t len;
306 		cell_t vaddr;
307 	} args;
308 
309 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
310 		prom_printf("prom_unmap_virt: cannot get mmuh\n");
311 		return -1;
312 	}
313 	args.name = ADR2CELL(&"call-method");
314 	args.nargs = 4;
315 	args.nreturns = 0;
316 	args.method = ADR2CELL(&"unmap");
317 	args.ihandle = HDL2CELL(mmuh);
318 	args.vaddr = ADR2CELL(vaddr);
319 	args.len = len;
320 	return openfirmware(&args);
321 }
322 
323 /*
324  * Have prom map in some memory
325  *
326  * Only works while the prom is actively mapping us.
327  */
328 int
prom_map_phys(paddr_t paddr,off_t size,vaddr_t vaddr,int mode)329 prom_map_phys(paddr_t paddr, off_t size, vaddr_t vaddr, int mode)
330 {
331 	struct {
332 		cell_t name;
333 		cell_t nargs;
334 		cell_t nreturns;
335 		cell_t method;
336 		cell_t ihandle;
337 		cell_t mode;
338 		cell_t size;
339 		cell_t vaddr;
340 		cell_t phys_hi;
341 		cell_t phys_lo;
342 		cell_t status;
343 		cell_t retaddr;
344 	} args;
345 
346 	if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
347 		prom_printf("prom_map_phys: cannot get mmuh\n");
348 		return 0;
349 	}
350 	args.name = ADR2CELL(&"call-method");
351 	args.nargs = 7;
352 	args.nreturns = 1;
353 	args.method = ADR2CELL(&"map");
354 	args.ihandle = HDL2CELL(mmuh);
355 	args.mode = mode;
356 	args.size = size;
357 	args.vaddr = ADR2CELL(vaddr);
358 	args.phys_hi = HDQ2CELL_HI(paddr);
359 	args.phys_lo = HDQ2CELL_LO(paddr);
360 
361 	if (openfirmware(&args) == -1)
362 		return -1;
363 	if (args.status)
364 		return -1;
365 	return (int)args.retaddr;
366 }
367 
368 
369 /*
370  * Request some RAM from the prom
371  *
372  * Only works while the prom is actively mapping us.
373  */
374 paddr_t
prom_alloc_phys(int len,int align)375 prom_alloc_phys(int len, int align)
376 {
377 	struct {
378 		cell_t name;
379 		cell_t nargs;
380 		cell_t nreturns;
381 		cell_t method;
382 		cell_t ihandle;
383 		cell_t align;
384 		cell_t len;
385 		cell_t status;
386 		cell_t phys_hi;
387 		cell_t phys_lo;
388 	} args;
389 
390 	if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
391 		prom_printf("prom_alloc_phys: cannot get memh\n");
392 		return -1;
393 	}
394 	args.name = ADR2CELL(&"call-method");
395 	args.nargs = 4;
396 	args.nreturns = 3;
397 	args.method = ADR2CELL(&"claim");
398 	args.ihandle = HDL2CELL(memh);
399 	args.align = align;
400 	args.len = len;
401 	if (openfirmware(&args) != 0)
402 		return -1;
403 	return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
404 }
405 
406 /*
407  * Request some specific RAM from the prom
408  *
409  * Only works while the prom is actively mapping us.
410  */
411 paddr_t
prom_claim_phys(paddr_t phys,int len)412 prom_claim_phys(paddr_t phys, int len)
413 {
414 	struct {
415 		cell_t name;
416 		cell_t nargs;
417 		cell_t nreturns;
418 		cell_t method;
419 		cell_t ihandle;
420 		cell_t align;
421 		cell_t len;
422 		cell_t phys_hi;
423 		cell_t phys_lo;
424 		cell_t status;
425 		cell_t rphys_hi;
426 		cell_t rphys_lo;
427 	} args;
428 
429 	if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
430 		prom_printf("prom_claim_phys: cannot get memh\n");
431 		return -1;
432 	}
433 	args.name = ADR2CELL(&"call-method");
434 	args.nargs = 6;
435 	args.nreturns = 3;
436 	args.method = ADR2CELL(&"claim");
437 	args.ihandle = HDL2CELL(memh);
438 	args.align = 0;
439 	args.len = len;
440 	args.phys_hi = HDQ2CELL_HI(phys);
441 	args.phys_lo = HDQ2CELL_LO(phys);
442 	if (openfirmware(&args) != 0)
443 		return -1;
444 	return (paddr_t)CELL2HDQ(args.rphys_hi, args.rphys_lo);
445 }
446 
447 /*
448  * Free some RAM to prom
449  *
450  * Only works while the prom is actively mapping us.
451  */
452 int
prom_free_phys(paddr_t phys,int len)453 prom_free_phys(paddr_t phys, int len)
454 {
455 	struct {
456 		cell_t name;
457 		cell_t nargs;
458 		cell_t nreturns;
459 		cell_t method;
460 		cell_t ihandle;
461 		cell_t len;
462 		cell_t phys_hi;
463 		cell_t phys_lo;
464 	} args;
465 
466 	if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
467 		prom_printf("prom_free_phys: cannot get memh\n");
468 		return -1;
469 	}
470 	args.name = ADR2CELL(&"call-method");
471 	args.nargs = 5;
472 	args.nreturns = 0;
473 	args.method = ADR2CELL(&"release");
474 	args.ihandle = HDL2CELL(memh);
475 	args.len = len;
476 	args.phys_hi = HDQ2CELL_HI(phys);
477 	args.phys_lo = HDQ2CELL_LO(phys);
478 	return openfirmware(&args);
479 }
480 
481 /*
482  * Get the msgbuf from the prom.  Only works once.
483  *
484  * Only works while the prom is actively mapping us.
485  */
486 paddr_t
prom_get_msgbuf(int len,int align)487 prom_get_msgbuf(int len, int align)
488 {
489 	struct {
490 		cell_t name;
491 		cell_t nargs;
492 		cell_t nreturns;
493 		cell_t method;
494 		cell_t ihandle;
495 		cell_t align;
496 		cell_t len;
497 		cell_t id;
498 		cell_t status;
499 		cell_t phys_hi;
500 		cell_t phys_lo;
501 	} args;
502 	paddr_t addr;
503 
504 	if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
505 		prom_printf("prom_get_msgbuf: cannot get memh\n");
506 		return -1;
507 	}
508 	if (OF_test("test-method") == 0) {
509 		if (OF_test_method(OF_instance_to_package(memh),
510 		    "SUNW,retain") == 0) {
511 			args.name = ADR2CELL(&"call-method");
512 			args.nargs = 5;
513 			args.nreturns = 3;
514 			args.method = ADR2CELL(&"SUNW,retain");
515 			args.id = ADR2CELL(&"msgbuf");
516 			args.ihandle = HDL2CELL(memh);
517 			args.len = len;
518 			args.align = align;
519 			args.status = -1;
520 			if (openfirmware(&args) == 0 && args.status == 0) {
521 				return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
522 			} else prom_printf("prom_get_msgbuf: SUNW,retain failed\n");
523 		} else prom_printf("prom_get_msgbuf: test-method failed\n");
524 	} else prom_printf("prom_get_msgbuf: test failed\n");
525 	/* Allocate random memory -- page zero avail?*/
526 	addr = prom_claim_phys(0x000, len);
527 	prom_printf("prom_get_msgbuf: allocated new buf at %08x\n", (int)addr);
528 	if (addr == -1) {
529 		prom_printf("prom_get_msgbuf: cannot get allocate physmem\n");
530 		return -1;
531 	}
532 	prom_printf("prom_get_msgbuf: claiming new buf at %08x\n", (int)addr);
533 	{ int i; for (i=0; i<200000000; i++); }
534 	return addr; /* Kluge till we go 64-bit */
535 }
536 
537 #ifdef MULTIPROCESSOR
538 /*
539  * Start secondary cpu identified by node, arrange 'func' as the entry.
540  */
541 void
prom_startcpu(u_int cpu,void * func,u_long arg)542 prom_startcpu(u_int cpu, void *func, u_long arg)
543 {
544         static struct {
545                 cell_t  name;
546                 cell_t  nargs;
547                 cell_t  nreturns;
548                 cell_t  cpu;
549                 cell_t  func;
550                 cell_t  arg;
551         } args;
552 
553 	args.name = ADR2CELL(&"SUNW,start-cpu");
554 	args.nargs = 3;
555 	args.nreturns = 0;
556         args.cpu = cpu;
557         args.func = (cell_t)(u_long)func;
558         args.arg = (cell_t)arg;
559 
560         openfirmware(&args);
561 }
562 
563 /*
564  * Start secondary cpu identified by cpuid, arrange 'func' as the entry.
565  * Returns -1 in case the openfirmware method is not available.
566  * Otherwise the result value from the openfirmware call is returned.
567  */
568 int
prom_startcpu_by_cpuid(u_int cpu,void * func,u_long arg)569 prom_startcpu_by_cpuid(u_int cpu, void *func, u_long arg)
570 {
571 	static struct {
572 		cell_t  name;
573 		cell_t  nargs;
574 		cell_t  nreturns;
575 		cell_t  cpu;
576 		cell_t  func;
577 		cell_t  arg;
578 		cell_t	status;
579 	} args;
580 
581 	if (OF_test("SUNW,start-cpu-by-cpuid") != 0)
582 		return -1;
583 
584 	args.name = ADR2CELL("SUNW,start-cpu-by-cpuid");
585 	args.nargs = 3;
586 	args.nreturns = 1;
587 	args.cpu = cpu;
588 	args.func = ADR2CELL(func);
589 	args.arg = arg;
590 
591 	return openfirmware(&args);
592 }
593 
594 /*
595  * Stop the calling cpu.
596  */
597 void
prom_stopself(void)598 prom_stopself(void)
599 {
600 	extern void openfirmware_exit(void*);
601 	static struct {
602 		cell_t  name;
603 		cell_t  nargs;
604 		cell_t  nreturns;
605 	} args;
606 
607 	args.name = ADR2CELL(&"SUNW,stop-self");
608 	args.nargs = 0;
609 	args.nreturns = 0;
610 
611 	openfirmware_exit(&args);
612 	panic("prom_stopself: failed.");
613 }
614 
615 bool
prom_has_stopself(void)616 prom_has_stopself(void)
617 {
618 	return OF_test("SUNW,stop-self") == 0;
619 }
620 
621 int
prom_stop_other(u_int id)622 prom_stop_other(u_int id)
623 {
624 	static struct {
625 		cell_t  name;
626 		cell_t  nargs;
627 		cell_t  nreturns;
628 		cell_t	cpuid;
629 		cell_t	result;
630 	} args;
631 
632 	args.name = ADR2CELL(&"SUNW,stop-cpu-by-cpuid");
633 	args.nargs = 1;
634 	args.nreturns = 1;
635 	args.cpuid = id;
636 	args.result = 0;
637 
638 	if (openfirmware(&args) == -1)
639 		return -1;
640 	return args.result;
641 }
642 
643 bool
prom_has_stop_other(void)644 prom_has_stop_other(void)
645 {
646 	return OF_test("SUNW,stop-cpu-by-cpuid") == 0;
647 }
648 #endif
649 
650 #ifdef DEBUG
651 int ofmapintrdebug = 0;
652 #define	DPRINTF(x)	if (ofmapintrdebug) printf x
653 #else
654 #define DPRINTF(x)
655 #endif
656 
657 
658 /*
659  * Recursively hunt for a property.
660  */
661 int
OF_searchprop(int node,const char * prop,void * sbuf,int buflen)662 OF_searchprop(int node, const char *prop, void *sbuf, int buflen)
663 {
664 	int len;
665 
666 	for( ; node; node = OF_parent(node)) {
667 		len = OF_getprop(node, prop, sbuf, buflen);
668 		if (len >= 0)
669 			return (len);
670 	}
671 	/* Error -- not found */
672 	return (-1);
673 }
674 
675 
676 /*
677  * Compare a sequence of cells with a mask,
678  *  return 1 if they match and 0 if they don't.
679  */
680 static int compare_cells (int *cell1, int *cell2, int *mask, int ncells);
681 static int
compare_cells(int * cell1,int * cell2,int * mask,int ncells)682 compare_cells(int *cell1, int *cell2, int *mask, int ncells)
683 {
684 	int i;
685 
686 	for (i=0; i<ncells; i++) {
687 		DPRINTF(("src %x ^ dest %x -> %x & mask %x -> %x\n",
688 			cell1[i], cell2[i], (cell1[i] ^ cell2[i]),
689 			mask[i], ((cell1[i] ^ cell2[i]) & mask[i])));
690 		if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
691 			return (0);
692 	}
693 	return (1);
694 }
695 
696 /*
697  * Find top pci bus host controller for a node.
698  */
699 static int
find_pci_host_node(int node)700 find_pci_host_node(int node)
701 {
702 	char dev_type[16];
703 	int pch = 0;
704 	int len;
705 
706 	for (; node; node = OF_parent(node)) {
707 		len = OF_getprop(node, "device_type",
708 				 &dev_type, sizeof(dev_type));
709 		if (len <= 0)
710 			continue;
711 		if (!strcmp(dev_type, "pci") ||
712 		    !strcmp(dev_type, "pciex"))
713 			pch = node;
714 	}
715 	return pch;
716 }
717 
718 /*
719  * Follow the OFW algorithm and return an interrupt specifier.
720  *
721  * Pass in the interrupt specifier you want mapped and the node
722  * you want it mapped from.  validlen is the number of cells in
723  * the interrupt specifier, and buflen is the number of cells in
724  * the buffer.
725  */
726 int
OF_mapintr(int node,int * interrupt,int validlen,int buflen)727 OF_mapintr(int node, int *interrupt, int validlen, int buflen)
728 {
729 	int i, len;
730 	int address_cells, size_cells, interrupt_cells, interrupt_map_len;
731 	int static_interrupt_map[256];
732 	int interrupt_map_mask[10];
733 	int *interrupt_map = &static_interrupt_map[0];
734 	int maplen = sizeof static_interrupt_map;
735 	int *free_map = NULL;
736 	int reg[10];
737 	char dev_type[32];
738 	int phc_node;
739 	int rc = -1;
740 
741 	phc_node = find_pci_host_node(node);
742 
743 	/*
744 	 * On machines with psycho PCI controllers, we don't need to map
745 	 * interrupts if they are already fully specified (0x20 to 0x3f
746 	 * for onboard devices and IGN 0x7c0 for psycho0/psycho1).
747 	 */
748 	if (*interrupt & 0x20 || *interrupt & 0x7c0) {
749 		char model[40];
750 
751 		if (OF_getprop(phc_node, "model", &model, sizeof(model)) > 10
752 		    && !strcmp(model, "SUNW,psycho")) {
753 			DPRINTF(("OF_mapintr: interrupt %x already mapped\n",
754 			    *interrupt));
755 			return validlen;
756 		}
757 	}
758 
759 	/*
760 	 * If there is no interrupt map in the bus node, we
761 	 * need to convert the slot address to its parent
762 	 * bus format, and hunt up the parent bus to see if
763 	 * we need to remap.
764 	 *
765 	 * The specification for interrupt mapping is borken.
766 	 * You are supposed to query the interrupt parent in
767 	 * the interrupt-map specification to determine the
768 	 * number of address and interrupt cells, but we need
769 	 * to know how many address and interrupt cells to skip
770 	 * to find the phandle...
771 	 *
772 	 */
773 	if ((len = OF_getprop(node, "reg", &reg, sizeof(reg))) <= 0) {
774 		printf("OF_mapintr: no reg property?\n");
775 		return (-1);
776 	}
777 
778 	while (node) {
779 #ifdef DEBUG
780 		char name[40];
781 
782 		if (ofmapintrdebug) {
783 			OF_getprop(node, "name", &name, sizeof(name));
784 			printf("Node %s (%x), host %x\n", name,
785 			       node, phc_node);
786 		}
787 #endif
788 
789  retry_map:
790 		if ((interrupt_map_len = OF_getprop(node,
791 			"interrupt-map", interrupt_map, maplen)) <= 0) {
792 
793 			/* Swizzle interrupt if this is a PCI bridge. */
794 			if (((len = OF_getprop(node, "device_type", &dev_type,
795 					      sizeof(dev_type))) > 0) &&
796 			    (!strcmp(dev_type, "pci") ||
797 			     !strcmp(dev_type, "pciex")) &&
798 			    (node != phc_node)) {
799 #ifdef DEBUG
800 				int ointerrupt = *interrupt;
801 #endif
802 
803 				*interrupt = ((*interrupt +
804 				    OFW_PCI_PHYS_HI_DEVICE(reg[0]) - 1) & 3) + 1;
805 				DPRINTF(("OF_mapintr: interrupt %x -> %x, reg[0] %x\n",
806 					 ointerrupt, *interrupt, reg[0]));
807 			}
808 
809 			/* Get reg for next level compare. */
810 			reg[0] = 0;
811 			OF_getprop(node, "reg", &reg, sizeof(reg));
812 
813 			node = OF_parent(node);
814 			continue;
815 		}
816 		if (interrupt_map_len > maplen) {
817 			DPRINTF(("interrupt_map_len %d > maplen %d, "
818 				 "allocating\n", interrupt_map_len, maplen));
819 			KASSERT(!free_map);
820 			free_map = malloc(interrupt_map_len, M_DEVBUF,
821 					  M_NOWAIT);
822 			if (!free_map) {
823 				interrupt_map_len = sizeof static_interrupt_map;
824 			} else {
825 				interrupt_map = free_map;
826 				maplen = interrupt_map_len;
827 				goto retry_map;
828 			}
829 		}
830 		/* Convert from bytes to cells. */
831 		interrupt_map_len = interrupt_map_len/sizeof(int);
832 		if ((len = (OF_searchprop(node, "#address-cells",
833 			&address_cells, sizeof(address_cells)))) <= 0) {
834 			/* How should I know. */
835 			address_cells = 2;
836 		}
837 		DPRINTF(("#address-cells = %d len %d ", address_cells, len));
838 		if ((len = OF_searchprop(node, "#size-cells", &size_cells,
839 			sizeof(size_cells))) <= 0) {
840 			/* How should I know. */
841 			size_cells = 2;
842 		}
843 		DPRINTF(("#size-cells = %d len %d ", size_cells, len));
844 		if ((len = OF_getprop(node, "#interrupt-cells", &interrupt_cells,
845 			sizeof(interrupt_cells))) <= 0) {
846 			/* How should I know. */
847 			interrupt_cells = 1;
848 		}
849 		DPRINTF(("#interrupt-cells = %d, len %d\n", interrupt_cells,
850 			len));
851 		if ((len = OF_getprop(node, "interrupt-map-mask", &interrupt_map_mask,
852 			sizeof(interrupt_map_mask))) <= 0) {
853 			/* Create a mask that masks nothing. */
854 			for (i = 0; i<(address_cells + interrupt_cells); i++)
855 				interrupt_map_mask[i] = -1;
856 		}
857 #ifdef DEBUG
858 		DPRINTF(("interrupt-map-mask len %d = ", len));
859 		for (i=0; i<(address_cells + interrupt_cells); i++)
860 			DPRINTF(("%x.", interrupt_map_mask[i]));
861 		DPRINTF(("reg = "));
862 		for (i=0; i<(address_cells); i++)
863 			DPRINTF(("%x.", reg[i]));
864 		DPRINTF(("interrupts = "));
865 		for (i=0; i<(interrupt_cells); i++)
866 			DPRINTF(("%x.", interrupt[i]));
867 
868 #endif
869 
870 		/* finally we can attempt the compare */
871 		i = 0;
872 		while (i < interrupt_map_len + address_cells + interrupt_cells) {
873 			int pintr_cells;
874 			int *imap = &interrupt_map[i];
875 			int *parent = &imap[address_cells + interrupt_cells];
876 
877 #ifdef DEBUG
878 			DPRINTF(("\ninterrupt-map addr "));
879 			for (len = 0; len < address_cells; len++)
880 				DPRINTF(("%x.", imap[len]));
881 			DPRINTF((" intr "));
882 			for (; len < (address_cells+interrupt_cells); len++)
883 				DPRINTF(("%x.", imap[len]));
884 			DPRINTF(("\nnode %x vs parent %x\n",
885 				imap[len], *parent));
886 #endif
887 
888 			/* Find out how many cells we'll need to skip. */
889 			if ((len = OF_searchprop(*parent, "#interrupt-cells",
890 				&pintr_cells, sizeof(pintr_cells))) < 0) {
891 				pintr_cells = interrupt_cells;
892 			}
893 			DPRINTF(("pintr_cells = %d len %d\n", pintr_cells, len));
894 
895 			if (compare_cells(imap, reg,
896 				interrupt_map_mask, address_cells) &&
897 				compare_cells(&imap[address_cells],
898 					interrupt,
899 					&interrupt_map_mask[address_cells],
900 					interrupt_cells))
901 			{
902 				/* Bingo! */
903 				if (buflen < pintr_cells) {
904 					/* Error -- ran out of storage. */
905 					if (free_map)
906 						free(free_map, M_DEVBUF);
907 					return (-1);
908 				}
909 				node = *parent;
910 				parent++;
911 #ifdef DEBUG
912 				DPRINTF(("Match! using "));
913 				for (len = 0; len < pintr_cells; len++)
914 					DPRINTF(("%x.", parent[len]));
915 				DPRINTF(("\n"));
916 #endif
917 				for (i = 0; i < pintr_cells; i++)
918 					interrupt[i] = parent[i];
919 				rc = validlen = pintr_cells;
920 				if (node == phc_node)
921 					return(rc);
922 				break;
923 			}
924 			/* Move on to the next interrupt_map entry. */
925 #ifdef DEBUG
926 			DPRINTF(("skip %d cells:",
927 				address_cells + interrupt_cells +
928 				pintr_cells + 1));
929 			for (len = 0; len < (address_cells +
930 				interrupt_cells + pintr_cells + 1); len++)
931 				DPRINTF(("%x.", imap[len]));
932 #endif
933 			i += address_cells + interrupt_cells + pintr_cells + 1;
934 		}
935 
936 		/* Get reg for the next level search. */
937 		if ((len = OF_getprop(node, "reg", &reg, sizeof(reg))) <= 0) {
938 			DPRINTF(("OF_mapintr: no reg property?\n"));
939 		} else {
940 			DPRINTF(("reg len %d\n", len));
941 		}
942 
943 		if (free_map) {
944 			free(free_map, M_DEVBUF);
945 			free_map = NULL;
946 		}
947 		node = OF_parent(node);
948 	}
949 	return (rc);
950 }
951