diff --git a/sys/arch/powerpc/powerpc/ofw_machdep.c b/sys/arch/powerpc/powerpc/ofw_machdep.c index a6d1e427be95..c59974bd3e8e 100644 --- a/sys/arch/powerpc/powerpc/ofw_machdep.c +++ b/sys/arch/powerpc/powerpc/ofw_machdep.c @@ -1,4 +1,4 @@ -/* $NetBSD: ofw_machdep.c,v 1.19 2012/02/01 09:54:03 matt Exp $ */ +/* $NetBSD: ofw_machdep.c,v 1.20 2013/04/01 20:14:42 macallan Exp $ */ /* * Copyright (C) 1996 Wolfgang Solfrank. @@ -32,7 +32,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.19 2012/02/01 09:54:03 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.20 2013/04/01 20:14:42 macallan Exp $"); #include #include @@ -50,6 +50,12 @@ __KERNEL_RCSID(0, "$NetBSD: ofw_machdep.c,v 1.19 2012/02/01 09:54:03 matt Exp $" #include #include +#ifdef DEBUG +#define DPRINTF aprint_error +#else +#define DPRINTF while(0) printf +#endif + #define OFMEM_REGIONS 32 static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3]; @@ -63,74 +69,136 @@ static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3]; void mem_regions(struct mem_region **memp, struct mem_region **availp) { - int phandle, i, cnt, regcnt; - struct mem_region_avail { - paddr_t start; - paddr_t size; - } OFavail_G5[OFMEM_REGIONS + 3] __attribute((unused)); + int phandle, i, cnt, regcnt, acells, scells; + int numregs; + uint32_t regs[OFMEM_REGIONS * 4]; /* 2 values + 2 for 64bit */ - /* - * Get memory. - */ + DPRINTF("calling mem_regions\n"); + /* determine acell size */ + if ((phandle = OF_finddevice("/")) == -1) + goto error; + cnt = OF_getprop(phandle, "#address-cells", &acells, sizeof(int)); + if (cnt <= 0) + acells = 1; + + /* determine scell size */ + if ((phandle = OF_finddevice("/")) == -1) + goto error; + cnt = OF_getprop(phandle, "#size-cells", &scells, sizeof(int)); + if (cnt <= 0) + scells = 1; + + /* Get memory */ if ((phandle = OF_finddevice("/memory")) == -1) goto error; - memset(OFmem, 0, sizeof OFmem); - regcnt = OF_getprop(phandle, "reg", - OFmem, sizeof OFmem[0] * OFMEM_REGIONS); + memset(regs, 0, sizeof(regs)); + regcnt = OF_getprop(phandle, "reg", regs, + sizeof(regs[0]) * OFMEM_REGIONS * 4); if (regcnt <= 0) goto error; - /* Remove zero sized entry in the returned data. */ - regcnt /= sizeof OFmem[0]; - for (i = 0; i < regcnt; ) - if (OFmem[i].size == 0) { - memmove(&OFmem[i], &OFmem[i + 1], - (regcnt - i) * sizeof OFmem[0]); - regcnt--; - } else - i++; + /* how many mem regions did we get? */ + numregs = regcnt / (sizeof(uint32_t)*(acells+scells)); + DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n", + regcnt, numregs, acells, scells); -#if defined (PMAC_G5) - /* XXXSL: the G5 implementation of OFW is defines the /memory reg/available - * properties differently. Try to fix it up here with minimal damage to the - * rest of the code - */ - { - int count; - memset(OFavail_G5, 0, sizeof OFavail_G5); - count = OF_getprop(phandle, "available", - OFavail_G5, sizeof OFavail_G5[0] * OFMEM_REGIONS); + /* move the data into OFmem */ + memset(OFmem, 0, sizeof(OFmem)); + for (i=0, cnt=0; i <= numregs; i++) { + uint64_t addr, size; - if (count <= 0) - goto error; + if (acells > 1) + memcpy(&addr, ®s[i * (acells + scells)], + sizeof(int32_t) * acells); + else + addr = regs[i * (acells + scells)]; - count /= sizeof OFavail_G5[0]; - cnt = count * sizeof(OFavail[0]); + if (scells > 1) + memcpy(&size, ®s[i * (acells + scells) + acells], + sizeof(int32_t) * scells); + else + size = regs[i * (acells + scells) + acells]; - for (i = 0; i < count; i++ ) - { - OFavail[i].start_hi = 0; - OFavail[i].start = OFavail_G5[i].start; - OFavail[i].size = OFavail_G5[i].size; + /* skip entry of 0 size */ + if (size == 0) + continue; +#ifndef _LP64 + if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF || + (addr + size) > 0xFFFFFFFF) { + aprint_error("Base addr of %llx or size of %llx too" + " large for 32 bit OS. Skipping.", addr, size); + continue; } - } -#else - memset(OFavail, 0, sizeof OFavail); - cnt = OF_getprop(phandle, "available", - OFavail, sizeof OFavail[0] * OFMEM_REGIONS); #endif - if (cnt <= 0) + OFmem[cnt].start = addr; + OFmem[cnt].size = size; + aprint_normal("mem region %d start=%llx size=%llx\n", + cnt, addr, size); + cnt++; + } + + DPRINTF("available\n"); + + /* now do the same thing again, for the available counts */ + memset(regs, 0, sizeof(regs)); + regcnt = OF_getprop(phandle, "available", regs, + sizeof(regs[0]) * OFMEM_REGIONS * 4); + if (regcnt <= 0) goto error; - cnt /= sizeof OFavail[0]; - for (i = 0; i < cnt; ) { - if (OFavail[i].size == 0) { - memmove(&OFavail[i], &OFavail[i + 1], - (cnt - i) * sizeof OFavail[0]); - cnt--; - } else - i++; + DPRINTF("%08x %08x %08x %08x\n", regs[0], regs[1], regs[2], regs[3]); + + /* + * some(?) G5s have messed up 'available' properties which don't obey + * #address-cells. Try to detect this here. + * XXX this needs a better test + */ + if (((regcnt >> 2) % (acells + scells)) != 0) { + aprint_normal("messed up 'available' property detected\n"); + acells = 1; + } + + /* how many mem regions did we get? */ + numregs = regcnt / (sizeof(uint32_t) * (acells + scells)); + DPRINTF("regcnt=%d num=%d acell=%d scell=%d\n", + regcnt, numregs, acells, scells); + + DPRINTF("to OF_avail\n"); + + /* move the data into OFavail */ + memset(OFavail, 0, sizeof(OFavail)); + for (i=0, cnt=0; i <= numregs; i++) { + uint64_t addr, size; + + DPRINTF("%d\n", i); + if (acells > 1) + memcpy(&addr, ®s[i * (acells + scells)], + sizeof(int32_t) * acells); + else + addr = regs[i * (acells + scells)]; + + if (scells > 1) + memcpy(&size, ®s[i * (acells + scells) + acells], + sizeof(int32_t) * scells); + else + size = regs[i * (acells + scells) + acells]; + /* skip entry of 0 size */ + if (size == 0) + continue; +#ifndef _LP64 + if (addr > 0xFFFFFFFF || size > 0xFFFFFFFF || + (addr+size) > 0xFFFFFFFF) { + aprint_error("Base addr of %llx or size of %llx too" + " large for 32 bit OS. Skipping.", addr, size); + continue; + } +#endif + OFavail[cnt].start = addr; + OFavail[cnt].size = size; + aprint_normal("avail region %d start=%llx size=%llx\n", + cnt, addr, size); + cnt++; } if (strncmp(model_name, "Pegasos", 7) == 0) {