1 /* $NetBSD: ofw_machdep.c,v 1.51 2022/05/14 07:11:23 hgutch 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.51 2022/05/14 07:11:23 hgutch 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 } args;
343
344 if (mmuh == -1 && ((mmuh = get_mmu_handle()) == -1)) {
345 prom_printf("prom_map_phys: cannot get mmuh\n");
346 return 0;
347 }
348 args.name = ADR2CELL(&"call-method");
349 args.nargs = 7;
350 args.nreturns = 0;
351 args.method = ADR2CELL(&"map");
352 args.ihandle = HDL2CELL(mmuh);
353 args.mode = mode;
354 args.size = size;
355 args.vaddr = ADR2CELL(vaddr);
356 args.phys_hi = HDQ2CELL_HI(paddr);
357 args.phys_lo = HDQ2CELL_LO(paddr);
358
359 if (openfirmware(&args) == -1)
360 return -1;
361 return 0;
362 }
363
364
365 /*
366 * Request some RAM from the prom
367 *
368 * Only works while the prom is actively mapping us.
369 */
370 paddr_t
prom_alloc_phys(int len,int align)371 prom_alloc_phys(int len, int align)
372 {
373 struct {
374 cell_t name;
375 cell_t nargs;
376 cell_t nreturns;
377 cell_t method;
378 cell_t ihandle;
379 cell_t align;
380 cell_t len;
381 cell_t status;
382 cell_t phys_hi;
383 cell_t phys_lo;
384 } args;
385
386 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
387 prom_printf("prom_alloc_phys: cannot get memh\n");
388 return -1;
389 }
390 args.name = ADR2CELL(&"call-method");
391 args.nargs = 4;
392 args.nreturns = 3;
393 args.method = ADR2CELL(&"claim");
394 args.ihandle = HDL2CELL(memh);
395 args.align = align;
396 args.len = len;
397 if (openfirmware(&args) != 0)
398 return -1;
399 return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
400 }
401
402 /*
403 * Request some specific RAM from the prom
404 *
405 * Only works while the prom is actively mapping us.
406 */
407 paddr_t
prom_claim_phys(paddr_t phys,int len)408 prom_claim_phys(paddr_t phys, int len)
409 {
410 struct {
411 cell_t name;
412 cell_t nargs;
413 cell_t nreturns;
414 cell_t method;
415 cell_t ihandle;
416 cell_t align;
417 cell_t len;
418 cell_t phys_hi;
419 cell_t phys_lo;
420 cell_t status;
421 cell_t rphys_hi;
422 cell_t rphys_lo;
423 } args;
424
425 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
426 prom_printf("prom_claim_phys: cannot get memh\n");
427 return -1;
428 }
429 args.name = ADR2CELL(&"call-method");
430 args.nargs = 6;
431 args.nreturns = 3;
432 args.method = ADR2CELL(&"claim");
433 args.ihandle = HDL2CELL(memh);
434 args.align = 0;
435 args.len = len;
436 args.phys_hi = HDQ2CELL_HI(phys);
437 args.phys_lo = HDQ2CELL_LO(phys);
438 if (openfirmware(&args) != 0)
439 return -1;
440 return (paddr_t)CELL2HDQ(args.rphys_hi, args.rphys_lo);
441 }
442
443 /*
444 * Free some RAM to prom
445 *
446 * Only works while the prom is actively mapping us.
447 */
448 int
prom_free_phys(paddr_t phys,int len)449 prom_free_phys(paddr_t phys, int len)
450 {
451 struct {
452 cell_t name;
453 cell_t nargs;
454 cell_t nreturns;
455 cell_t method;
456 cell_t ihandle;
457 cell_t len;
458 cell_t phys_hi;
459 cell_t phys_lo;
460 } args;
461
462 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
463 prom_printf("prom_free_phys: cannot get memh\n");
464 return -1;
465 }
466 args.name = ADR2CELL(&"call-method");
467 args.nargs = 5;
468 args.nreturns = 0;
469 args.method = ADR2CELL(&"release");
470 args.ihandle = HDL2CELL(memh);
471 args.len = len;
472 args.phys_hi = HDQ2CELL_HI(phys);
473 args.phys_lo = HDQ2CELL_LO(phys);
474 return openfirmware(&args);
475 }
476
477 /*
478 * Get the msgbuf from the prom. Only works once.
479 *
480 * Only works while the prom is actively mapping us.
481 */
482 paddr_t
prom_get_msgbuf(int len,int align)483 prom_get_msgbuf(int len, int align)
484 {
485 struct {
486 cell_t name;
487 cell_t nargs;
488 cell_t nreturns;
489 cell_t method;
490 cell_t ihandle;
491 cell_t align;
492 cell_t len;
493 cell_t id;
494 cell_t status;
495 cell_t phys_hi;
496 cell_t phys_lo;
497 } args;
498 paddr_t addr;
499
500 if (memh == -1 && ((memh = get_memory_handle()) == -1)) {
501 prom_printf("prom_get_msgbuf: cannot get memh\n");
502 return -1;
503 }
504 if (OF_test("test-method") == 0) {
505 if (OF_test_method(OF_instance_to_package(memh),
506 "SUNW,retain") == 0) {
507 args.name = ADR2CELL(&"call-method");
508 args.nargs = 5;
509 args.nreturns = 3;
510 args.method = ADR2CELL(&"SUNW,retain");
511 args.id = ADR2CELL(&"msgbuf");
512 args.ihandle = HDL2CELL(memh);
513 args.len = len;
514 args.align = align;
515 args.status = -1;
516 if (openfirmware(&args) == 0 && args.status == 0) {
517 return (paddr_t)CELL2HDQ(args.phys_hi, args.phys_lo);
518 } else prom_printf("prom_get_msgbuf: SUNW,retain failed\n");
519 } else prom_printf("prom_get_msgbuf: test-method failed\n");
520 } else prom_printf("prom_get_msgbuf: test failed\n");
521 /* Allocate random memory -- page zero avail?*/
522 addr = prom_claim_phys(0x000, len);
523 prom_printf("prom_get_msgbuf: allocated new buf at %08x\n", (int)addr);
524 if (addr == -1) {
525 prom_printf("prom_get_msgbuf: cannot get allocate physmem\n");
526 return -1;
527 }
528 prom_printf("prom_get_msgbuf: claiming new buf at %08x\n", (int)addr);
529 { int i; for (i=0; i<200000000; i++); }
530 return addr; /* Kluge till we go 64-bit */
531 }
532
533 #ifdef MULTIPROCESSOR
534 /*
535 * Start secondary cpu identified by node, arrange 'func' as the entry.
536 */
537 void
prom_startcpu(u_int cpu,void * func,u_long arg)538 prom_startcpu(u_int cpu, void *func, u_long arg)
539 {
540 static struct {
541 cell_t name;
542 cell_t nargs;
543 cell_t nreturns;
544 cell_t cpu;
545 cell_t func;
546 cell_t arg;
547 } args;
548
549 args.name = ADR2CELL(&"SUNW,start-cpu");
550 args.nargs = 3;
551 args.nreturns = 0;
552 args.cpu = cpu;
553 args.func = (cell_t)(u_long)func;
554 args.arg = (cell_t)arg;
555
556 openfirmware(&args);
557 }
558
559 /*
560 * Start secondary cpu identified by cpuid, arrange 'func' as the entry.
561 * Returns -1 in case the openfirmware method is not available.
562 * Otherwise the result value from the openfirmware call is returned.
563 */
564 int
prom_startcpu_by_cpuid(u_int cpu,void * func,u_long arg)565 prom_startcpu_by_cpuid(u_int cpu, void *func, u_long arg)
566 {
567 static struct {
568 cell_t name;
569 cell_t nargs;
570 cell_t nreturns;
571 cell_t cpu;
572 cell_t func;
573 cell_t arg;
574 cell_t status;
575 } args;
576
577 if (OF_test("SUNW,start-cpu-by-cpuid") != 0)
578 return -1;
579
580 args.name = ADR2CELL("SUNW,start-cpu-by-cpuid");
581 args.nargs = 3;
582 args.nreturns = 1;
583 args.cpu = cpu;
584 args.func = ADR2CELL(func);
585 args.arg = arg;
586
587 return openfirmware(&args);
588 }
589
590 /*
591 * Stop the calling cpu.
592 */
593 void
prom_stopself(void)594 prom_stopself(void)
595 {
596 extern void openfirmware_exit(void*);
597 static struct {
598 cell_t name;
599 cell_t nargs;
600 cell_t nreturns;
601 } args;
602
603 args.name = ADR2CELL(&"SUNW,stop-self");
604 args.nargs = 0;
605 args.nreturns = 0;
606
607 openfirmware_exit(&args);
608 panic("prom_stopself: failed.");
609 }
610
611 bool
prom_has_stopself(void)612 prom_has_stopself(void)
613 {
614 return OF_test("SUNW,stop-self") == 0;
615 }
616
617 int
prom_stop_other(u_int id)618 prom_stop_other(u_int id)
619 {
620 static struct {
621 cell_t name;
622 cell_t nargs;
623 cell_t nreturns;
624 cell_t cpuid;
625 cell_t result;
626 } args;
627
628 args.name = ADR2CELL(&"SUNW,stop-cpu-by-cpuid");
629 args.nargs = 1;
630 args.nreturns = 1;
631 args.cpuid = id;
632 args.result = 0;
633
634 if (openfirmware(&args) == -1)
635 return -1;
636 return args.result;
637 }
638
639 bool
prom_has_stop_other(void)640 prom_has_stop_other(void)
641 {
642 return OF_test("SUNW,stop-cpu-by-cpuid") == 0;
643 }
644 #endif
645
646 uint64_t
prom_set_sun4v_api_version(uint64_t api_group,uint64_t major,uint64_t minor,uint64_t * supported_minor)647 prom_set_sun4v_api_version(uint64_t api_group, uint64_t major,
648 uint64_t minor, uint64_t *supported_minor)
649 {
650 static struct {
651 cell_t name;
652 cell_t nargs;
653 cell_t nreturns;
654 cell_t api_group;
655 cell_t major;
656 cell_t minor;
657 cell_t status;
658 cell_t supported_minor;
659 } args;
660
661 args.name = ADR2CELL("SUNW,set-sun4v-api-version");
662 args.nargs = 3;
663 args.nreturns = 2;
664 args.api_group = api_group;
665 args.major = major;
666 args.minor = minor;
667 args.status = -1;
668 args.supported_minor = -1;
669
670 openfirmware(&args);
671
672 *supported_minor = args.supported_minor;
673 return (uint64_t)args.status;
674 }
675 #if 1
676 uint64_t
prom_get_sun4v_api_version(uint64_t api_group,uint64_t * major,uint64_t * minor)677 prom_get_sun4v_api_version(uint64_t api_group, uint64_t* major, uint64_t* minor)
678 {
679 static struct {
680 cell_t name;
681 cell_t nargs;
682 cell_t nreturns;
683 cell_t api_group;
684 cell_t status;
685 cell_t major;
686 cell_t minor;
687 } args;
688
689 args.name = ADR2CELL("SUNW,get-sun4v-api-version");
690 args.nargs = 1;
691 args.nreturns = 3;
692 args.api_group = api_group;
693 args.status = -1;
694 args.major = -1;
695 args.minor = -1;
696
697 openfirmware(&args);
698
699 *major = args.major;
700 *minor = args.minor;
701 return (uint64_t)args.status;
702 }
703 #endif
704 void
prom_sun4v_soft_state_supported(void)705 prom_sun4v_soft_state_supported(void)
706 {
707 static struct {
708 cell_t name;
709 cell_t nargs;
710 cell_t nreturns;
711 } args;
712
713 args.name = ADR2CELL("SUNW,soft-state-supported");
714 args.nargs = 0;
715 args.nreturns = 0;
716
717 openfirmware(&args);
718 }
719
720 #ifdef DEBUG
721 int ofmapintrdebug = 0;
722 #define DPRINTF(x) if (ofmapintrdebug) printf x
723 #else
724 #define DPRINTF(x)
725 #endif
726
727
728 /*
729 * Recursively hunt for a property.
730 */
731 int
OF_searchprop(int node,const char * prop,void * sbuf,int buflen)732 OF_searchprop(int node, const char *prop, void *sbuf, int buflen)
733 {
734 int len;
735
736 for( ; node; node = OF_parent(node)) {
737 len = OF_getprop(node, prop, sbuf, buflen);
738 if (len >= 0)
739 return (len);
740 }
741 /* Error -- not found */
742 return (-1);
743 }
744
745
746 /*
747 * Compare a sequence of cells with a mask,
748 * return 1 if they match and 0 if they don't.
749 */
750 static int compare_cells (int *cell1, int *cell2, int *mask, int ncells);
751 static int
compare_cells(int * cell1,int * cell2,int * mask,int ncells)752 compare_cells(int *cell1, int *cell2, int *mask, int ncells)
753 {
754 int i;
755
756 for (i=0; i<ncells; i++) {
757 DPRINTF(("src %x ^ dest %x -> %x & mask %x -> %x\n",
758 cell1[i], cell2[i], (cell1[i] ^ cell2[i]),
759 mask[i], ((cell1[i] ^ cell2[i]) & mask[i])));
760 if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
761 return (0);
762 }
763 return (1);
764 }
765
766 /*
767 * Find top pci bus host controller for a node.
768 */
769 static int
find_pci_host_node(int node)770 find_pci_host_node(int node)
771 {
772 char dev_type[16];
773 int pch = 0;
774 int len;
775
776 for (; node; node = OF_parent(node)) {
777 len = OF_getprop(node, "device_type",
778 &dev_type, sizeof(dev_type));
779 if (len <= 0)
780 continue;
781 if (!strcmp(dev_type, "pci") ||
782 !strcmp(dev_type, "pciex"))
783 pch = node;
784 }
785 return pch;
786 }
787
788 /*
789 * Follow the OFW algorithm and return an interrupt specifier.
790 *
791 * Pass in the interrupt specifier you want mapped and the node
792 * you want it mapped from. validlen is the number of cells in
793 * the interrupt specifier, and buflen is the number of cells in
794 * the buffer.
795 */
796 int
OF_mapintr(int node,int * interrupt,int validlen,int buflen)797 OF_mapintr(int node, int *interrupt, int validlen, int buflen)
798 {
799 int i, len;
800 int address_cells, size_cells, interrupt_cells, interrupt_map_len;
801 int static_interrupt_map[256];
802 int interrupt_map_mask[10];
803 int *interrupt_map = &static_interrupt_map[0];
804 int maplen = sizeof static_interrupt_map;
805 int *free_map = NULL;
806 int reg[10];
807 char dev_type[32];
808 int phc_node;
809 int rc = -1;
810
811 phc_node = find_pci_host_node(node);
812
813 /*
814 * On machines with psycho PCI controllers, we don't need to map
815 * interrupts if they are already fully specified (0x20 to 0x3f
816 * for onboard devices and IGN 0x7c0 for psycho0/psycho1).
817 */
818 if (*interrupt & 0x20 || *interrupt & 0x7c0) {
819 char model[40];
820
821 if (OF_getprop(phc_node, "model", &model, sizeof(model)) > 10
822 && !strcmp(model, "SUNW,psycho")) {
823 DPRINTF(("OF_mapintr: interrupt %x already mapped\n",
824 *interrupt));
825 return validlen;
826 }
827 }
828
829 /*
830 * If there is no interrupt map in the bus node, we
831 * need to convert the slot address to its parent
832 * bus format, and hunt up the parent bus to see if
833 * we need to remap.
834 *
835 * The specification for interrupt mapping is borken.
836 * You are supposed to query the interrupt parent in
837 * the interrupt-map specification to determine the
838 * number of address and interrupt cells, but we need
839 * to know how many address and interrupt cells to skip
840 * to find the phandle...
841 *
842 */
843 if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
844 printf("OF_mapintr: no reg property?\n");
845 return (-1);
846 }
847
848 while (node) {
849 #ifdef DEBUG
850 char name[40];
851
852 if (ofmapintrdebug) {
853 OF_getprop(node, "name", &name, sizeof(name));
854 printf("Node %s (%x), host %x\n", name,
855 node, phc_node);
856 }
857 #endif
858
859 retry_map:
860 if ((interrupt_map_len = OF_getprop(node,
861 "interrupt-map", interrupt_map, maplen)) <= 0) {
862
863 /* Swizzle interrupt if this is a PCI bridge. */
864 if (((len = OF_getprop(node, "device_type", &dev_type,
865 sizeof(dev_type))) > 0) &&
866 (!strcmp(dev_type, "pci") ||
867 !strcmp(dev_type, "pciex")) &&
868 (node != phc_node)) {
869 #ifdef DEBUG
870 int ointerrupt = *interrupt;
871 #endif
872
873 *interrupt = ((*interrupt +
874 OFW_PCI_PHYS_HI_DEVICE(reg[0]) - 1) & 3) + 1;
875 DPRINTF(("OF_mapintr: interrupt %x -> %x, reg[0] %x\n",
876 ointerrupt, *interrupt, reg[0]));
877 }
878
879 /* Get reg for next level compare. */
880 reg[0] = 0;
881 OF_getprop(node, "reg", ®, sizeof(reg));
882
883 node = OF_parent(node);
884 continue;
885 }
886 if (interrupt_map_len > maplen) {
887 DPRINTF(("interrupt_map_len %d > maplen %d, "
888 "allocating\n", interrupt_map_len, maplen));
889 KASSERT(!free_map);
890 free_map = malloc(interrupt_map_len, M_DEVBUF,
891 M_NOWAIT);
892 if (!free_map) {
893 interrupt_map_len = sizeof static_interrupt_map;
894 } else {
895 interrupt_map = free_map;
896 maplen = interrupt_map_len;
897 goto retry_map;
898 }
899 }
900 /* Convert from bytes to cells. */
901 interrupt_map_len = interrupt_map_len/sizeof(int);
902 if ((len = (OF_searchprop(node, "#address-cells",
903 &address_cells, sizeof(address_cells)))) <= 0) {
904 /* How should I know. */
905 address_cells = 2;
906 }
907 DPRINTF(("#address-cells = %d len %d ", address_cells, len));
908 if ((len = OF_searchprop(node, "#size-cells", &size_cells,
909 sizeof(size_cells))) <= 0) {
910 /* How should I know. */
911 size_cells = 2;
912 }
913 DPRINTF(("#size-cells = %d len %d ", size_cells, len));
914 if ((len = OF_getprop(node, "#interrupt-cells", &interrupt_cells,
915 sizeof(interrupt_cells))) <= 0) {
916 /* How should I know. */
917 interrupt_cells = 1;
918 }
919 DPRINTF(("#interrupt-cells = %d, len %d\n", interrupt_cells,
920 len));
921 if ((len = OF_getprop(node, "interrupt-map-mask", &interrupt_map_mask,
922 sizeof(interrupt_map_mask))) <= 0) {
923 /* Create a mask that masks nothing. */
924 for (i = 0; i<(address_cells + interrupt_cells); i++)
925 interrupt_map_mask[i] = -1;
926 }
927 #ifdef DEBUG
928 DPRINTF(("interrupt-map-mask len %d = ", len));
929 for (i=0; i<(address_cells + interrupt_cells); i++)
930 DPRINTF(("%x.", interrupt_map_mask[i]));
931 DPRINTF(("reg = "));
932 for (i=0; i<(address_cells); i++)
933 DPRINTF(("%x.", reg[i]));
934 DPRINTF(("interrupts = "));
935 for (i=0; i<(interrupt_cells); i++)
936 DPRINTF(("%x.", interrupt[i]));
937
938 #endif
939
940 /* finally we can attempt the compare */
941 i = 0;
942 while (i < interrupt_map_len + address_cells + interrupt_cells) {
943 int pintr_cells;
944 int *imap = &interrupt_map[i];
945 int *parent = &imap[address_cells + interrupt_cells];
946
947 #ifdef DEBUG
948 DPRINTF(("\ninterrupt-map addr "));
949 for (len = 0; len < address_cells; len++)
950 DPRINTF(("%x.", imap[len]));
951 DPRINTF((" intr "));
952 for (; len < (address_cells+interrupt_cells); len++)
953 DPRINTF(("%x.", imap[len]));
954 DPRINTF(("\nnode %x vs parent %x\n",
955 imap[len], *parent));
956 #endif
957
958 /* Find out how many cells we'll need to skip. */
959 if ((len = OF_searchprop(*parent, "#interrupt-cells",
960 &pintr_cells, sizeof(pintr_cells))) < 0) {
961 pintr_cells = interrupt_cells;
962 }
963 DPRINTF(("pintr_cells = %d len %d\n", pintr_cells, len));
964
965 if (compare_cells(imap, reg,
966 interrupt_map_mask, address_cells) &&
967 compare_cells(&imap[address_cells],
968 interrupt,
969 &interrupt_map_mask[address_cells],
970 interrupt_cells))
971 {
972 /* Bingo! */
973 if (buflen < pintr_cells) {
974 /* Error -- ran out of storage. */
975 if (free_map)
976 free(free_map, M_DEVBUF);
977 return (-1);
978 }
979 node = *parent;
980 parent++;
981 #ifdef DEBUG
982 DPRINTF(("Match! using "));
983 for (len = 0; len < pintr_cells; len++)
984 DPRINTF(("%x.", parent[len]));
985 DPRINTF(("\n"));
986 #endif
987 for (i = 0; i < pintr_cells; i++)
988 interrupt[i] = parent[i];
989 rc = validlen = pintr_cells;
990 if (node == phc_node)
991 return(rc);
992 break;
993 }
994 /* Move on to the next interrupt_map entry. */
995 #ifdef DEBUG
996 DPRINTF(("skip %d cells:",
997 address_cells + interrupt_cells +
998 pintr_cells + 1));
999 for (len = 0; len < (address_cells +
1000 interrupt_cells + pintr_cells + 1); len++)
1001 DPRINTF(("%x.", imap[len]));
1002 #endif
1003 i += address_cells + interrupt_cells + pintr_cells + 1;
1004 }
1005
1006 /* Get reg for the next level search. */
1007 if ((len = OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) {
1008 DPRINTF(("OF_mapintr: no reg property?\n"));
1009 } else {
1010 DPRINTF(("reg len %d\n", len));
1011 }
1012
1013 if (free_map) {
1014 free(free_map, M_DEVBUF);
1015 free_map = NULL;
1016 }
1017 node = OF_parent(node);
1018 }
1019 return (rc);
1020 }
1021