physmem.c revision 261643
1261643Sian/*- 2261643Sian * Copyright (c) 2014 Ian Lepore <ian@freebsd.org> 3261643Sian * All rights excluded. 4261643Sian * 5261643Sian * Redistribution and use in source and binary forms, with or without 6261643Sian * modification, are permitted provided that the following conditions 7261643Sian * are met: 8261643Sian * 1. Redistributions of source code must retain the above copyright 9261643Sian * notice, this list of conditions and the following disclaimer. 10261643Sian * 2. Redistributions in binary form must reproduce the above copyright 11261643Sian * notice, this list of conditions and the following disclaimer in the 12261643Sian * documentation and/or other materials provided with the distribution. 13261643Sian * 14261643Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15261643Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16261643Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17261643Sian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18261643Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19261643Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20261643Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21261643Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22261643Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23261643Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24261643Sian * SUCH DAMAGE. 25261643Sian */ 26261643Sian 27261643Sian#include <sys/cdefs.h> 28261643Sian__FBSDID("$FreeBSD: head/sys/arm/arm/physmem.c 261643 2014-02-08 23:54:16Z ian $"); 29261643Sian 30261643Sian#include "opt_ddb.h" 31261643Sian 32261643Sian/* 33261643Sian * Routines for describing and initializing anything related to physical memory. 34261643Sian */ 35261643Sian 36261643Sian#include <sys/param.h> 37261643Sian#include <sys/systm.h> 38261643Sian#include <vm/vm.h> 39261643Sian#include <machine/physmem.h> 40261643Sian 41261643Sian/* 42261643Sian * These structures are used internally to keep track of regions of physical 43261643Sian * ram, and regions within the physical ram that need to be excluded. An 44261643Sian * exclusion region can be excluded from crash dumps, from the vm pool of pages 45261643Sian * that can be allocated, or both, depending on the exclusion flags associated 46261643Sian * with the region. 47261643Sian */ 48261643Sian#define MAX_HWCNT 10 49261643Sian#define MAX_EXCNT 10 50261643Sian 51261643Sianstruct region { 52261643Sian vm_offset_t addr; 53261643Sian vm_size_t size; 54261643Sian uint32_t flags; 55261643Sian}; 56261643Sian 57261643Sianstatic struct region hwregions[MAX_HWCNT]; 58261643Sianstatic struct region exregions[MAX_EXCNT]; 59261643Sian 60261643Sianstatic size_t hwcnt; 61261643Sianstatic size_t excnt; 62261643Sian 63261643Sian/* 64261643Sian * These "avail lists" are globals used to communicate physical memory layout to 65261643Sian * other parts of the kernel. Within the arrays, each value is the starting 66261643Sian * address of a contiguous area of physical address space. The values at even 67261643Sian * indexes are areas that contain usable memory and the values at odd indexes 68261643Sian * are areas that aren't usable. Each list is terminated by a pair of zero 69261643Sian * entries. 70261643Sian * 71261643Sian * dump_avail tells the dump code what regions to include in a crash dump, and 72261643Sian * phys_avail is the way we hand all the remaining physical ram we haven't used 73261643Sian * in early kernel init over to the vm system for allocation management. 74261643Sian * 75261643Sian * We size these arrays to hold twice as many available regions as we allow for 76261643Sian * hardware memory regions, to allow for the fact that exclusions can split a 77261643Sian * hardware region into two or more available regions. In the real world there 78261643Sian * will typically be one or two hardware regions and two or three exclusions. 79261643Sian * 80261643Sian * Each available region in this list occupies two array slots (the start of the 81261643Sian * available region and the start of the unavailable region that follows it). 82261643Sian */ 83261643Sian#define MAX_AVAIL_REGIONS (MAX_HWCNT * 2) 84261643Sian#define MAX_AVAIL_ENTRIES (MAX_AVAIL_REGIONS * 2) 85261643Sian 86261643Sianvm_paddr_t phys_avail[MAX_AVAIL_ENTRIES + 2]; /* +2 to allow for a pair */ 87261643Sianvm_paddr_t dump_avail[MAX_AVAIL_ENTRIES + 2]; /* of zeroes to terminate. */ 88261643Sian 89261643Sian/* This is the total number of hardware pages, excluded or not. */ 90261643Sianlong realmem; 91261643Sian 92261643Sian/* 93261643Sian * Print the contents of the physical and excluded region tables using the 94261643Sian * provided printf-like output function (which will be either printf or 95261643Sian * db_printf). 96261643Sian */ 97261643Sianstatic void 98261643Sianphysmem_dump_tables(int (*prfunc)(const char *, ...)) 99261643Sian{ 100261643Sian int flags, i; 101261643Sian uintmax_t addr, size; 102261643Sian const unsigned int mbyte = 1024 * 1024; 103261643Sian 104261643Sian prfunc("Physical memory chunk(s):\n"); 105261643Sian for (i = 0; i < hwcnt; ++i) { 106261643Sian addr = hwregions[i].addr; 107261643Sian size = hwregions[i].size; 108261643Sian prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages)\n", addr, 109261643Sian addr + size - 1, size / mbyte, size / PAGE_SIZE); 110261643Sian } 111261643Sian 112261643Sian prfunc("Excluded memory regions:\n"); 113261643Sian for (i = 0; i < excnt; ++i) { 114261643Sian addr = exregions[i].addr; 115261643Sian size = exregions[i].size; 116261643Sian flags = exregions[i].flags; 117261643Sian prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages) %s %s\n", 118261643Sian addr, addr + size - 1, size / mbyte, size / PAGE_SIZE, 119261643Sian (flags & EXFLAG_NOALLOC) ? "NoAlloc" : "", 120261643Sian (flags & EXFLAG_NODUMP) ? "NoDump" : ""); 121261643Sian } 122261643Sian} 123261643Sian 124261643Sian/* 125261643Sian * Print the contents of the static mapping table. Used for bootverbose. 126261643Sian */ 127261643Sianvoid 128261643Sianarm_physmem_print_tables() 129261643Sian{ 130261643Sian 131261643Sian physmem_dump_tables(printf); 132261643Sian} 133261643Sian 134261643Sian/* 135261643Sian * Walk the list of hardware regions, processing it against the list of 136261643Sian * exclusions that contain the given exflags, and generating an "avail list". 137261643Sian * 138261643Sian * Updates the kernel global 'realmem' with the sum of all pages in hw regions. 139261643Sian * 140261643Sian * Returns the number of pages of non-excluded memory added to the avail list. 141261643Sian */ 142261643Sianstatic long 143261643Sianregions_to_avail(vm_paddr_t *avail, uint32_t exflags) 144261643Sian{ 145261643Sian size_t acnt, exi, hwi; 146261643Sian vm_paddr_t end, start, xend, xstart; 147261643Sian long availmem; 148261643Sian const struct region *exp, *hwp; 149261643Sian 150261643Sian realmem = 0; 151261643Sian availmem = 0; 152261643Sian acnt = 0; 153261643Sian for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) { 154261643Sian start = hwp->addr; 155261643Sian end = hwp->size + start; 156261643Sian realmem += arm32_btop(end - start); 157261643Sian for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) { 158261643Sian xstart = exp->addr; 159261643Sian xend = exp->size + xstart; 160261643Sian /* 161261643Sian * If the excluded region ends before this hw region, 162261643Sian * continue checking with the next excluded region. 163261643Sian */ 164261643Sian if (xend <= start) 165261643Sian continue; 166261643Sian /* 167261643Sian * If the excluded region begins after this hw region 168261643Sian * we're done because both lists are sorted. 169261643Sian */ 170261643Sian if (xstart >= end) 171261643Sian break; 172261643Sian /* 173261643Sian * If the excluded region completely covers this hw 174261643Sian * region, shrink this hw region to zero size. 175261643Sian */ 176261643Sian if ((start >= xstart) && (end <= xend)) { 177261643Sian start = xend; 178261643Sian end = xend; 179261643Sian break; 180261643Sian } 181261643Sian /* 182261643Sian * If the excluded region falls wholly within this hw 183261643Sian * region without abutting or overlapping the beginning 184261643Sian * or end, create an available entry from the leading 185261643Sian * fragment, then adjust the start of this hw region to 186261643Sian * the end of the excluded region, and continue checking 187261643Sian * the next excluded region because another exclusion 188261643Sian * could affect the remainder of this hw region. 189261643Sian */ 190261643Sian if ((xstart > start) && (xend < end)) { 191261643Sian avail[acnt++] = start; 192261643Sian avail[acnt++] = xstart; 193261643Sian availmem += arm32_btop(xstart - start); 194261643Sian start = xend; 195261643Sian continue; 196261643Sian } 197261643Sian /* 198261643Sian * If excluded region partially overlaps this region, 199261643Sian * trim the excluded portion off the appropriate end. 200261643Sian */ 201261643Sian if ((xstart >= start) && (xstart <= end)) { 202261643Sian end = xstart; 203261643Sian } else if ((xend >= start) && (xend <= end)) { 204261643Sian start = xend; 205261643Sian } 206261643Sian } 207261643Sian /* 208261643Sian * If the trimming actions above left a non-zero size, create an 209261643Sian * available entry for it. 210261643Sian */ 211261643Sian if (end > start) { 212261643Sian avail[acnt++] = start; 213261643Sian avail[acnt++] = end; 214261643Sian availmem += arm32_btop(end - start); 215261643Sian } 216261643Sian if (acnt >= MAX_AVAIL_ENTRIES) 217261643Sian panic("Not enough space in the dump/phys_avail arrays"); 218261643Sian } 219261643Sian 220261643Sian return (availmem); 221261643Sian} 222261643Sian 223261643Sian/* 224261643Sian * Insertion-sort a new entry into a regions list; sorted by start address. 225261643Sian */ 226261643Sianstatic void 227261643Sianinsert_region(struct region *regions, size_t rcnt, vm_offset_t addr, 228261643Sian vm_size_t size, uint32_t flags) 229261643Sian{ 230261643Sian size_t i; 231261643Sian struct region *ep, *rp; 232261643Sian 233261643Sian ep = regions + rcnt; 234261643Sian for (i = 0, rp = regions; i < rcnt; ++i, ++rp) { 235261643Sian if (addr < rp->addr) { 236261643Sian bcopy(rp, rp + 1, (ep - rp) * sizeof(*rp)); 237261643Sian break; 238261643Sian } 239261643Sian } 240261643Sian rp->addr = addr; 241261643Sian rp->size = size; 242261643Sian rp->flags = flags; 243261643Sian} 244261643Sian 245261643Sian/* 246261643Sian * Add a hardware memory region. 247261643Sian */ 248261643Sianvoid 249261643Sianarm_physmem_hardware_region(vm_offset_t pa, vm_size_t sz) 250261643Sian{ 251261643Sian vm_offset_t adj; 252261643Sian 253261643Sian /* 254261643Sian * Filter out the page at PA 0x00000000. The VM can't handle it, as 255261643Sian * pmap_extract() == 0 means failure. 256261643Sian */ 257261643Sian if (pa == 0) { 258261643Sian pa = PAGE_SIZE; 259261643Sian sz -= PAGE_SIZE; 260261643Sian } 261261643Sian 262261643Sian /* 263261643Sian * Round the starting address up to a page boundary, and truncate the 264261643Sian * ending page down to a page boundary. 265261643Sian */ 266261643Sian adj = round_page(pa) - pa; 267261643Sian pa = round_page(pa); 268261643Sian sz = trunc_page(sz - adj); 269261643Sian 270261643Sian if (hwcnt < nitems(hwregions)) 271261643Sian insert_region(hwregions, hwcnt++, pa, sz, 0); 272261643Sian} 273261643Sian 274261643Sian/* 275261643Sian * Add an exclusion region. 276261643Sian */ 277261643Sianvoid arm_physmem_exclude_region(vm_offset_t pa, vm_size_t sz, uint32_t exflags) 278261643Sian{ 279261643Sian vm_offset_t adj; 280261643Sian 281261643Sian /* 282261643Sian * Truncate the starting address down to a page boundary, and round the 283261643Sian * ending page up to a page boundary. 284261643Sian */ 285261643Sian adj = pa - trunc_page(pa); 286261643Sian pa = trunc_page(pa); 287261643Sian sz = round_page(sz + adj); 288261643Sian 289261643Sian if (excnt < nitems(exregions)) 290261643Sian insert_region(exregions, excnt++, pa, sz, exflags); 291261643Sian} 292261643Sian 293261643Sian/* 294261643Sian * Process all the regions added earlier into the global avail lists. 295261643Sian */ 296261643Sianvoid 297261643Sianarm_physmem_init_kernel_globals(void) 298261643Sian{ 299261643Sian 300261643Sian regions_to_avail(dump_avail, EXFLAG_NODUMP); 301261643Sian physmem = regions_to_avail(phys_avail, EXFLAG_NOALLOC); 302261643Sian} 303261643Sian 304261643Sian#ifdef DDB 305261643Sian#include <ddb/ddb.h> 306261643Sian 307261643SianDB_SHOW_COMMAND(physmem, db_show_physmem) 308261643Sian{ 309261643Sian 310261643Sian physmem_dump_tables(db_printf); 311261643Sian} 312261643Sian 313261643Sian#endif /* DDB */ 314261643Sian 315