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