xref: /freebsd/sys/powerpc/ofw/ofw_machdep.c (revision 711bcba0bbfaf5ceb513e4ae6fdc7e3c3a5160a8)
1 /*-
2  * Copyright (C) 1996 Wolfgang Solfrank.
3  * Copyright (C) 1996 TooLs GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by TooLs GmbH.
17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/bus.h>
39 #include <sys/systm.h>
40 #include <sys/conf.h>
41 #include <sys/disk.h>
42 #include <sys/fcntl.h>
43 #include <sys/malloc.h>
44 #include <sys/smp.h>
45 #include <sys/stat.h>
46 
47 #include <net/ethernet.h>
48 
49 #include <dev/ofw/openfirm.h>
50 #include <dev/ofw/ofw_pci.h>
51 #include <dev/ofw/ofw_bus.h>
52 
53 #include <vm/vm.h>
54 #include <vm/vm_param.h>
55 #include <vm/vm_page.h>
56 
57 #include <machine/bus.h>
58 #include <machine/cpu.h>
59 #include <machine/md_var.h>
60 #include <machine/platform.h>
61 #include <machine/ofw_machdep.h>
62 
63 static struct mem_region OFmem[PHYS_AVAIL_SZ], OFavail[PHYS_AVAIL_SZ];
64 static struct mem_region OFfree[PHYS_AVAIL_SZ];
65 
66 extern register_t ofmsr[5];
67 extern void	*openfirmware_entry;
68 static void	*fdt;
69 int		ofw_real_mode;
70 
71 int		ofwcall(void *);
72 static void	ofw_quiesce(void);
73 static int	openfirmware(void *args);
74 
75 /*
76  * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback.
77  */
78 register_t	ofw_sprg0_save;
79 
80 static __inline void
81 ofw_sprg_prepare(void)
82 {
83 	/*
84 	 * Assume that interrupt are disabled at this point, or
85 	 * SPRG1-3 could be trashed
86 	 */
87 	__asm __volatile("mfsprg0 %0\n\t"
88 			 "mtsprg0 %1\n\t"
89 	    		 "mtsprg1 %2\n\t"
90 	    		 "mtsprg2 %3\n\t"
91 			 "mtsprg3 %4\n\t"
92 			 : "=&r"(ofw_sprg0_save)
93 			 : "r"(ofmsr[1]),
94 			 "r"(ofmsr[2]),
95 			 "r"(ofmsr[3]),
96 			 "r"(ofmsr[4]));
97 }
98 
99 static __inline void
100 ofw_sprg_restore(void)
101 {
102 	/*
103 	 * Note that SPRG1-3 contents are irrelevant. They are scratch
104 	 * registers used in the early portion of trap handling when
105 	 * interrupts are disabled.
106 	 *
107 	 * PCPU data cannot be used until this routine is called !
108 	 */
109 	__asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save));
110 }
111 
112 /*
113  * Memory region utilities: determine if two regions overlap,
114  * and merge two overlapping regions into one
115  */
116 static int
117 memr_overlap(struct mem_region *r1, struct mem_region *r2)
118 {
119 	if ((r1->mr_start + r1->mr_size) < r2->mr_start ||
120 	    (r2->mr_start + r2->mr_size) < r1->mr_start)
121 		return (FALSE);
122 
123 	return (TRUE);
124 }
125 
126 static void
127 memr_merge(struct mem_region *from, struct mem_region *to)
128 {
129 	vm_offset_t end;
130 	end = ulmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size);
131 	to->mr_start = ulmin(from->mr_start, to->mr_start);
132 	to->mr_size = end - to->mr_start;
133 }
134 
135 /*
136  * Quick sort callout for comparing memory regions.
137  */
138 static int	mr_cmp(const void *a, const void *b);
139 
140 static int
141 mr_cmp(const void *a, const void *b)
142 {
143 	const struct	mem_region *regiona;
144 	const struct	mem_region *regionb;
145 
146 	regiona = a;
147 	regionb = b;
148 	if (regiona->mr_start < regionb->mr_start)
149 		return (-1);
150 	else if (regiona->mr_start > regionb->mr_start)
151 		return (1);
152 	else
153 		return (0);
154 }
155 
156 static int
157 parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
158 {
159 	cell_t address_cells, size_cells;
160 	cell_t OFmem[4 * PHYS_AVAIL_SZ];
161 	int sz, i, j;
162 	int apple_hack_mode;
163 	phandle_t phandle;
164 
165 	sz = 0;
166 	apple_hack_mode = 0;
167 
168 	/*
169 	 * Get #address-cells from root node, defaulting to 1 if it cannot
170 	 * be found.
171 	 */
172 	phandle = OF_finddevice("/");
173 	if (OF_getprop(phandle, "#address-cells", &address_cells,
174 	    sizeof(address_cells)) < (ssize_t)sizeof(address_cells))
175 		address_cells = 1;
176 	if (OF_getprop(phandle, "#size-cells", &size_cells,
177 	    sizeof(size_cells)) < (ssize_t)sizeof(size_cells))
178 		size_cells = 1;
179 
180 	/*
181 	 * On Apple hardware, address_cells is always 1 for "available",
182 	 * even when it is explicitly set to 2. Then all memory above 4 GB
183 	 * should be added by hand to the available list. Detect Apple hardware
184 	 * by seeing if ofw_real_mode is set -- only Apple seems to use
185 	 * virtual-mode OF.
186 	 */
187 	if (strcmp(prop, "available") == 0 && !ofw_real_mode)
188 		apple_hack_mode = 1;
189 
190 	if (apple_hack_mode)
191 		address_cells = 1;
192 
193 	/*
194 	 * Get memory.
195 	 */
196 	if (node == -1 || (sz = OF_getprop(node, prop,
197 	    OFmem, sizeof(OFmem))) <= 0)
198 		panic("Physical memory map not found");
199 
200 	i = 0;
201 	j = 0;
202 	while (i < sz/sizeof(cell_t)) {
203 	      #ifndef __powerpc64__
204 		/* On 32-bit PPC, ignore regions starting above 4 GB */
205 		if (address_cells > 1 && OFmem[i] > 0) {
206 			i += address_cells + size_cells;
207 			continue;
208 		}
209 	      #endif
210 
211 		output[j].mr_start = OFmem[i++];
212 		if (address_cells == 2) {
213 			#ifdef __powerpc64__
214 			output[j].mr_start <<= 32;
215 			#endif
216 			output[j].mr_start += OFmem[i++];
217 		}
218 
219 		output[j].mr_size = OFmem[i++];
220 		if (size_cells == 2) {
221 			#ifdef __powerpc64__
222 			output[j].mr_size <<= 32;
223 			#endif
224 			output[j].mr_size += OFmem[i++];
225 		}
226 
227 	      #ifndef __powerpc64__
228 		/*
229 		 * Check for memory regions extending above 32-bit
230 		 * memory space, and restrict them to stay there.
231 		 */
232 		if (((uint64_t)output[j].mr_start +
233 		    (uint64_t)output[j].mr_size) >
234 		    BUS_SPACE_MAXADDR_32BIT) {
235 			output[j].mr_size = BUS_SPACE_MAXADDR_32BIT -
236 			    output[j].mr_start;
237 		}
238 	      #endif
239 
240 		j++;
241 	}
242 	sz = j*sizeof(output[0]);
243 
244 	#ifdef __powerpc64__
245 	if (apple_hack_mode) {
246 		/* Add in regions above 4 GB to the available list */
247 		struct mem_region himem[16];
248 		int hisz;
249 
250 		hisz = parse_ofw_memory(node, "reg", himem);
251 		for (i = 0; i < hisz/sizeof(himem[0]); i++) {
252 			if (himem[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
253 				output[j].mr_start = himem[i].mr_start;
254 				output[j].mr_size = himem[i].mr_size;
255 				j++;
256 			}
257 		}
258 		sz = j*sizeof(output[0]);
259 	}
260 	#endif
261 
262 	return (sz);
263 }
264 
265 static int
266 parse_drconf_memory(int *msz, int *asz, struct mem_region *ofmem,
267 		    struct mem_region *ofavail)
268 {
269 	phandle_t phandle;
270 	vm_offset_t base;
271 	int i, idx, len, lasz, lmsz, res;
272 	uint32_t lmb_size[2];
273 	unsigned long *dmem, flags;
274 
275 	lmsz = *msz;
276 	lasz = *asz;
277 
278 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
279 	if (phandle == -1)
280 		/* No drconf node, return. */
281 		return (0);
282 
283 	res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size));
284 	if (res == -1)
285 		return (0);
286 
287 	/* Parse the /ibm,dynamic-memory.
288 	   The first position gives the # of entries. The next two words
289  	   reflect the address of the memory block. The next four words are
290 	   the DRC index, reserved, list index and flags.
291 	   (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
292 
293 	    #el  Addr   DRC-idx  res   list-idx  flags
294 	   -------------------------------------------------
295 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
296 	   -------------------------------------------------
297 	*/
298 
299 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
300 	if (len > 0) {
301 
302 		/* We have to use a variable length array on the stack
303 		   since we have very limited stack space.
304 		*/
305 		cell_t arr[len/sizeof(cell_t)];
306 
307 		res = OF_getprop(phandle, "ibm,dynamic-memory", &arr,
308 				 sizeof(arr));
309 		if (res == -1)
310 			return (0);
311 
312 		/* Number of elements */
313 		idx = arr[0];
314 
315 		/* First address. */
316 		dmem = (void*)&arr[1];
317 
318 		for (i = 0; i < idx; i++) {
319 			base = *dmem;
320 			dmem += 2;
321 			flags = *dmem;
322 			/* Use region only if available and not reserved. */
323 			if ((flags & 0x8) && !(flags & 0x80)) {
324 				ofmem[lmsz].mr_start = base;
325 				ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
326 				ofavail[lasz].mr_start = base;
327 				ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
328 				lmsz++;
329 				lasz++;
330 			}
331 			dmem++;
332 		}
333 	}
334 
335 	*msz = lmsz;
336 	*asz = lasz;
337 
338 	return (1);
339 }
340 /*
341  * This is called during powerpc_init, before the system is really initialized.
342  * It shall provide the total and the available regions of RAM.
343  * Both lists must have a zero-size entry as terminator.
344  * The available regions need not take the kernel into account, but needs
345  * to provide space for two additional entry beyond the terminating one.
346  */
347 void
348 ofw_mem_regions(struct mem_region **memp, int *memsz,
349 		struct mem_region **availp, int *availsz)
350 {
351 	phandle_t phandle;
352 	vm_offset_t maxphysaddr;
353 	int asz, msz, fsz;
354 	int i, j, res;
355 	int still_merging;
356 	char name[31];
357 
358 	asz = msz = 0;
359 
360 	/*
361 	 * Get memory from all the /memory nodes.
362 	 */
363 	for (phandle = OF_child(OF_peer(0)); phandle != 0;
364 	    phandle = OF_peer(phandle)) {
365 		if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0)
366 			continue;
367 		if (strncmp(name, "memory", sizeof(name)) != 0)
368 			continue;
369 
370 		res = parse_ofw_memory(phandle, "reg", &OFmem[msz]);
371 		msz += res/sizeof(struct mem_region);
372 		if (OF_getproplen(phandle, "available") >= 0)
373 			res = parse_ofw_memory(phandle, "available",
374 			    &OFavail[asz]);
375 		else
376 			res = parse_ofw_memory(phandle, "reg", &OFavail[asz]);
377 		asz += res/sizeof(struct mem_region);
378 	}
379 
380 	/* Check for memory in ibm,dynamic-reconfiguration-memory */
381 	parse_drconf_memory(&msz, &asz, OFmem, OFavail);
382 
383 	qsort(OFmem, msz, sizeof(*OFmem), mr_cmp);
384 	qsort(OFavail, asz, sizeof(*OFavail), mr_cmp);
385 
386 	*memp = OFmem;
387 	*memsz = msz;
388 
389 	/*
390 	 * On some firmwares (SLOF), some memory may be marked available that
391 	 * doesn't actually exist. This manifests as an extension of the last
392 	 * available segment past the end of physical memory, so truncate that
393 	 * one.
394 	 */
395 	maxphysaddr = 0;
396 	for (i = 0; i < msz; i++)
397 		if (OFmem[i].mr_start + OFmem[i].mr_size > maxphysaddr)
398 			maxphysaddr = OFmem[i].mr_start + OFmem[i].mr_size;
399 
400 	if (OFavail[asz - 1].mr_start + OFavail[asz - 1].mr_size > maxphysaddr)
401 		OFavail[asz - 1].mr_size = maxphysaddr -
402 		    OFavail[asz - 1].mr_start;
403 
404 	/*
405 	 * OFavail may have overlapping regions - collapse these
406 	 * and copy out remaining regions to OFfree
407 	 */
408 	do {
409 		still_merging = FALSE;
410 		for (i = 0; i < asz; i++) {
411 			if (OFavail[i].mr_size == 0)
412 				continue;
413 			for (j = i+1; j < asz; j++) {
414 				if (OFavail[j].mr_size == 0)
415 					continue;
416 				if (memr_overlap(&OFavail[j], &OFavail[i])) {
417 					memr_merge(&OFavail[j], &OFavail[i]);
418 					/* mark inactive */
419 					OFavail[j].mr_size = 0;
420 					still_merging = TRUE;
421 				}
422 			}
423 		}
424 	} while (still_merging == TRUE);
425 
426 	/* evict inactive ranges */
427 	for (i = 0, fsz = 0; i < asz; i++) {
428 		if (OFavail[i].mr_size != 0) {
429 			OFfree[fsz] = OFavail[i];
430 			fsz++;
431 		}
432 	}
433 
434 	*availp = OFfree;
435 	*availsz = fsz;
436 }
437 
438 void
439 OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
440 {
441 	if (ofmsr[0] & PSL_DR)
442 		ofw_real_mode = 0;
443 	else
444 		ofw_real_mode = 1;
445 
446 	fdt = fdt_ptr;
447 
448 	#ifdef FDT_DTB_STATIC
449 	/* Check for a statically included blob */
450 	if (fdt == NULL)
451 		fdt = &fdt_static_dtb;
452 	#endif
453 }
454 
455 boolean_t
456 OF_bootstrap()
457 {
458 	boolean_t status = FALSE;
459 
460 	if (openfirmware_entry != NULL) {
461 		if (ofw_real_mode) {
462 			status = OF_install(OFW_STD_REAL, 0);
463 		} else {
464 			#ifdef __powerpc64__
465 			status = OF_install(OFW_STD_32BIT, 0);
466 			#else
467 			status = OF_install(OFW_STD_DIRECT, 0);
468 			#endif
469 		}
470 
471 		if (status != TRUE)
472 			return status;
473 
474 		OF_init(openfirmware);
475 
476 		/*
477 		 * On some machines, we need to quiesce OF to turn off
478 		 * background processes.
479 		 */
480 		ofw_quiesce();
481 	} else if (fdt != NULL) {
482 		status = OF_install(OFW_FDT, 0);
483 
484 		if (status != TRUE)
485 			return status;
486 
487 		OF_init(fdt);
488 	}
489 
490 	return (status);
491 }
492 
493 static void
494 ofw_quiesce(void)
495 {
496 	phandle_t rootnode;
497 	char model[32];
498 	struct {
499 		cell_t name;
500 		cell_t nargs;
501 		cell_t nreturns;
502 	} args;
503 
504 	/*
505 	 * Only quiesce Open Firmware on PowerMac11,2 and 12,1. It is
506 	 * necessary there to shut down a background thread doing fan
507 	 * management, and is harmful on other machines.
508 	 *
509 	 * Note: we don't need to worry about which OF module we are
510 	 * using since this is called only from very early boot, within
511 	 * OF's boot context.
512 	 */
513 
514 	rootnode = OF_finddevice("/");
515 	if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
516 		if (strcmp(model, "PowerMac11,2") == 0 ||
517 		    strcmp(model, "PowerMac12,1") == 0) {
518 			args.name = (cell_t)(uintptr_t)"quiesce";
519 			args.nargs = 0;
520 			args.nreturns = 0;
521 			openfirmware(&args);
522 		}
523 	}
524 }
525 
526 static int
527 openfirmware_core(void *args)
528 {
529 	int		result;
530 	register_t	oldmsr;
531 
532 	/*
533 	 * Turn off exceptions - we really don't want to end up
534 	 * anywhere unexpected with PCPU set to something strange
535 	 * or the stack pointer wrong.
536 	 */
537 	oldmsr = intr_disable();
538 
539 	ofw_sprg_prepare();
540 
541 #if defined(AIM) && !defined(__powerpc64__)
542 	/*
543 	 * Clear battable[] translations
544 	 */
545 	if (!(cpu_features & PPC_FEATURE_64))
546 		__asm __volatile("mtdbatu 2, %0\n"
547 				 "mtdbatu 3, %0" : : "r" (0));
548 	isync();
549 #endif
550 
551 	result = ofwcall(args);
552 	ofw_sprg_restore();
553 
554 	intr_restore(oldmsr);
555 
556 	return (result);
557 }
558 
559 #ifdef SMP
560 struct ofw_rv_args {
561 	void *args;
562 	int retval;
563 	volatile int in_progress;
564 };
565 
566 static void
567 ofw_rendezvous_dispatch(void *xargs)
568 {
569 	struct ofw_rv_args *rv_args = xargs;
570 
571 	/* NOTE: Interrupts are disabled here */
572 
573 	if (PCPU_GET(cpuid) == 0) {
574 		/*
575 		 * Execute all OF calls on CPU 0
576 		 */
577 		rv_args->retval = openfirmware_core(rv_args->args);
578 		rv_args->in_progress = 0;
579 	} else {
580 		/*
581 		 * Spin with interrupts off on other CPUs while OF has
582 		 * control of the machine.
583 		 */
584 		while (rv_args->in_progress)
585 			cpu_spinwait();
586 	}
587 }
588 #endif
589 
590 static int
591 openfirmware(void *args)
592 {
593 	int result;
594 	#ifdef SMP
595 	struct ofw_rv_args rv_args;
596 
597 	rv_args.args = args;
598 	rv_args.in_progress = 1;
599 	smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,
600 	    smp_no_rendevous_barrier, &rv_args);
601 	result = rv_args.retval;
602 	#else
603 	result = openfirmware_core(args);
604 	#endif
605 
606 	return (result);
607 }
608 
609 void
610 OF_reboot()
611 {
612 	struct {
613 		cell_t name;
614 		cell_t nargs;
615 		cell_t nreturns;
616 		cell_t arg;
617 	} args;
618 
619 	args.name = (cell_t)(uintptr_t)"interpret";
620 	args.nargs = 1;
621 	args.nreturns = 0;
622 	args.arg = (cell_t)(uintptr_t)"reset-all";
623 	openfirmware_core(&args); /* Don't do rendezvous! */
624 
625 	for (;;);	/* just in case */
626 }
627 
628 void
629 OF_getetheraddr(device_t dev, u_char *addr)
630 {
631 	phandle_t	node;
632 
633 	node = ofw_bus_get_node(dev);
634 	OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN);
635 }
636 
637 /*
638  * Return a bus handle and bus tag that corresponds to the register
639  * numbered regno for the device referenced by the package handle
640  * dev. This function is intended to be used by console drivers in
641  * early boot only. It works by mapping the address of the device's
642  * register in the address space of its parent and recursively walk
643  * the device tree upward this way.
644  */
645 static void
646 OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip)
647 {
648 	char name[16];
649 	uint32_t addr, size;
650 	int pci, res;
651 
652 	res = OF_getprop(node, "#address-cells", &addr, sizeof(addr));
653 	if (res == -1)
654 		addr = 2;
655 	res = OF_getprop(node, "#size-cells", &size, sizeof(size));
656 	if (res == -1)
657 		size = 1;
658 	pci = 0;
659 	if (addr == 3 && size == 2) {
660 		res = OF_getprop(node, "name", name, sizeof(name));
661 		if (res != -1) {
662 			name[sizeof(name) - 1] = '\0';
663 			pci = (strcmp(name, "pci") == 0) ? 1 : 0;
664 		}
665 	}
666 	if (addrp != NULL)
667 		*addrp = addr;
668 	if (sizep != NULL)
669 		*sizep = size;
670 	if (pcip != NULL)
671 		*pcip = pci;
672 }
673 
674 int
675 OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
676     bus_space_handle_t *handle)
677 {
678 	uint32_t cell[32];
679 	bus_addr_t addr, raddr, baddr;
680 	bus_size_t size, rsize;
681 	uint32_t c, nbridge, naddr, nsize;
682 	phandle_t bridge, parent;
683 	u_int spc, rspc;
684 	int pci, pcib, res;
685 
686 	/* Sanity checking. */
687 	if (dev == 0)
688 		return (EINVAL);
689 	bridge = OF_parent(dev);
690 	if (bridge == 0)
691 		return (EINVAL);
692 	if (regno < 0)
693 		return (EINVAL);
694 	if (tag == NULL || handle == NULL)
695 		return (EINVAL);
696 
697 	/* Get the requested register. */
698 	OF_get_addr_props(bridge, &naddr, &nsize, &pci);
699 	res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg",
700 	    cell, sizeof(cell));
701 	if (res == -1)
702 		return (ENXIO);
703 	if (res % sizeof(cell[0]))
704 		return (ENXIO);
705 	res /= sizeof(cell[0]);
706 	regno *= naddr + nsize;
707 	if (regno + naddr + nsize > res)
708 		return (EINVAL);
709 	spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0;
710 	addr = 0;
711 	for (c = 0; c < naddr; c++)
712 		addr = ((uint64_t)addr << 32) | cell[regno++];
713 	size = 0;
714 	for (c = 0; c < nsize; c++)
715 		size = ((uint64_t)size << 32) | cell[regno++];
716 
717 	/*
718 	 * Map the address range in the bridge's decoding window as given
719 	 * by the "ranges" property. If a node doesn't have such property
720 	 * then no mapping is done.
721 	 */
722 	parent = OF_parent(bridge);
723 	while (parent != 0) {
724 		OF_get_addr_props(parent, &nbridge, NULL, &pcib);
725 		res = OF_getprop(bridge, "ranges", cell, sizeof(cell));
726 		if (res == -1)
727 			goto next;
728 		if (res % sizeof(cell[0]))
729 			return (ENXIO);
730 		res /= sizeof(cell[0]);
731 		regno = 0;
732 		while (regno < res) {
733 			rspc = (pci)
734 			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
735 			    : ~0;
736 			if (rspc != spc) {
737 				regno += naddr + nbridge + nsize;
738 				continue;
739 			}
740 			raddr = 0;
741 			for (c = 0; c < naddr; c++)
742 				raddr = ((uint64_t)raddr << 32) | cell[regno++];
743 			rspc = (pcib)
744 			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
745 			    : ~0;
746 			baddr = 0;
747 			for (c = 0; c < nbridge; c++)
748 				baddr = ((uint64_t)baddr << 32) | cell[regno++];
749 			rsize = 0;
750 			for (c = 0; c < nsize; c++)
751 				rsize = ((uint64_t)rsize << 32) | cell[regno++];
752 			if (addr < raddr || addr >= raddr + rsize)
753 				continue;
754 			addr = addr - raddr + baddr;
755 			if (rspc != ~0)
756 				spc = rspc;
757 		}
758 
759 	next:
760 		bridge = parent;
761 		parent = OF_parent(bridge);
762 		OF_get_addr_props(bridge, &naddr, &nsize, &pci);
763 	}
764 
765 	*tag = &bs_le_tag;
766 	return (bus_space_map(*tag, addr, size, 0, handle));
767 }
768 
769