149439183SMitchell Horne /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
349439183SMitchell Horne *
449439183SMitchell Horne * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
549439183SMitchell Horne * All rights reserved.
649439183SMitchell Horne *
749439183SMitchell Horne * Redistribution and use in source and binary forms, with or without
849439183SMitchell Horne * modification, are permitted provided that the following conditions
949439183SMitchell Horne * are met:
1049439183SMitchell Horne * 1. Redistributions of source code must retain the above copyright
1149439183SMitchell Horne * notice, this list of conditions and the following disclaimer.
1249439183SMitchell Horne * 2. Redistributions in binary form must reproduce the above copyright
1349439183SMitchell Horne * notice, this list of conditions and the following disclaimer in the
1449439183SMitchell Horne * documentation and/or other materials provided with the distribution.
1549439183SMitchell Horne *
1649439183SMitchell Horne * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1749439183SMitchell Horne * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1849439183SMitchell Horne * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1949439183SMitchell Horne * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2049439183SMitchell Horne * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2149439183SMitchell Horne * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2249439183SMitchell Horne * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2349439183SMitchell Horne * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2449439183SMitchell Horne * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2549439183SMitchell Horne * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2649439183SMitchell Horne * SUCH DAMAGE.
2749439183SMitchell Horne */
2849439183SMitchell Horne
2949439183SMitchell Horne #include <sys/cdefs.h>
308c99dfedSAndrew Turner #ifdef _KERNEL
3149439183SMitchell Horne #include "opt_acpi.h"
3249439183SMitchell Horne #include "opt_ddb.h"
338c99dfedSAndrew Turner #endif
3449439183SMitchell Horne
3549439183SMitchell Horne /*
3649439183SMitchell Horne * Routines for describing and initializing anything related to physical memory.
3749439183SMitchell Horne */
3849439183SMitchell Horne
3949439183SMitchell Horne #include <sys/param.h>
4049439183SMitchell Horne #include <sys/systm.h>
41e6cf1a08SMitchell Horne #include <sys/bus.h>
4218ce865aSOleksandr Tymoshenko #include <sys/kernel.h>
43e6cf1a08SMitchell Horne #include <sys/module.h>
4449439183SMitchell Horne #include <sys/physmem.h>
458c99dfedSAndrew Turner
468c99dfedSAndrew Turner #ifdef _KERNEL
4749439183SMitchell Horne #include <vm/vm.h>
4849439183SMitchell Horne #include <vm/vm_param.h>
4949439183SMitchell Horne #include <vm/vm_page.h>
5049439183SMitchell Horne #include <vm/vm_phys.h>
516f3b523cSKonstantin Belousov #include <vm/vm_dumpset.h>
52e6cf1a08SMitchell Horne
5349439183SMitchell Horne #include <machine/md_var.h>
54e6cf1a08SMitchell Horne #include <machine/resource.h>
558c99dfedSAndrew Turner #else
568c99dfedSAndrew Turner #include <stdarg.h>
578c99dfedSAndrew Turner #include <stdio.h>
588c99dfedSAndrew Turner #include <string.h>
598c99dfedSAndrew Turner #endif
6049439183SMitchell Horne
6149439183SMitchell Horne /*
6249439183SMitchell Horne * These structures are used internally to keep track of regions of physical
6349439183SMitchell Horne * ram, and regions within the physical ram that need to be excluded. An
6449439183SMitchell Horne * exclusion region can be excluded from crash dumps, from the vm pool of pages
6549439183SMitchell Horne * that can be allocated, or both, depending on the exclusion flags associated
6649439183SMitchell Horne * with the region.
6749439183SMitchell Horne */
6849439183SMitchell Horne #ifdef DEV_ACPI
6949439183SMitchell Horne #define MAX_HWCNT 32 /* ACPI needs more regions */
7049439183SMitchell Horne #define MAX_EXCNT 32
7149439183SMitchell Horne #else
7249439183SMitchell Horne #define MAX_HWCNT 16
7349439183SMitchell Horne #define MAX_EXCNT 16
7449439183SMitchell Horne #endif
7549439183SMitchell Horne
7649439183SMitchell Horne #if defined(__arm__)
7749439183SMitchell Horne #define MAX_PHYS_ADDR 0xFFFFFFFFull
7841e6d209SAndrew Turner #elif defined(__aarch64__) || defined(__amd64__) || defined(__riscv)
7949439183SMitchell Horne #define MAX_PHYS_ADDR 0xFFFFFFFFFFFFFFFFull
8049439183SMitchell Horne #endif
8149439183SMitchell Horne
8249439183SMitchell Horne struct region {
8349439183SMitchell Horne vm_paddr_t addr;
8449439183SMitchell Horne vm_size_t size;
8549439183SMitchell Horne uint32_t flags;
8649439183SMitchell Horne };
8749439183SMitchell Horne
8849439183SMitchell Horne static struct region hwregions[MAX_HWCNT];
8949439183SMitchell Horne static struct region exregions[MAX_EXCNT];
9049439183SMitchell Horne
9149439183SMitchell Horne static size_t hwcnt;
9249439183SMitchell Horne static size_t excnt;
9349439183SMitchell Horne
9449439183SMitchell Horne /*
9549439183SMitchell Horne * realmem is the total number of hardware pages, excluded or not.
9649439183SMitchell Horne * Maxmem is one greater than the last physical page number.
9749439183SMitchell Horne */
9849439183SMitchell Horne long realmem;
9949439183SMitchell Horne long Maxmem;
10049439183SMitchell Horne
1018c99dfedSAndrew Turner #ifndef _KERNEL
1028c99dfedSAndrew Turner static void
panic(const char * fmt,...)1038c99dfedSAndrew Turner panic(const char *fmt, ...)
1048c99dfedSAndrew Turner {
1058c99dfedSAndrew Turner va_list va;
1068c99dfedSAndrew Turner
1078c99dfedSAndrew Turner va_start(va, fmt);
1088c99dfedSAndrew Turner vfprintf(stderr, fmt, va);
1098c99dfedSAndrew Turner fprintf(stderr, "\n");
1108c99dfedSAndrew Turner va_end(va);
1118c99dfedSAndrew Turner __builtin_trap();
1128c99dfedSAndrew Turner }
1138c99dfedSAndrew Turner #endif
1148c99dfedSAndrew Turner
11549439183SMitchell Horne /*
11649439183SMitchell Horne * Print the contents of the physical and excluded region tables using the
11749439183SMitchell Horne * provided printf-like output function (which will be either printf or
11849439183SMitchell Horne * db_printf).
11949439183SMitchell Horne */
12049439183SMitchell Horne static void
1217b5cb32fSKyle Evans physmem_dump_tables(int (*prfunc)(const char *, ...) __printflike(1, 2))
12249439183SMitchell Horne {
123f461b955SAndrew Turner size_t i;
124f461b955SAndrew Turner int flags;
12549439183SMitchell Horne uintmax_t addr, size;
12649439183SMitchell Horne const unsigned int mbyte = 1024 * 1024;
12749439183SMitchell Horne
12849439183SMitchell Horne prfunc("Physical memory chunk(s):\n");
12949439183SMitchell Horne for (i = 0; i < hwcnt; ++i) {
13049439183SMitchell Horne addr = hwregions[i].addr;
13149439183SMitchell Horne size = hwregions[i].size;
13249439183SMitchell Horne prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages)\n", addr,
13349439183SMitchell Horne addr + size - 1, size / mbyte, size / PAGE_SIZE);
13449439183SMitchell Horne }
13549439183SMitchell Horne
13649439183SMitchell Horne prfunc("Excluded memory regions:\n");
13749439183SMitchell Horne for (i = 0; i < excnt; ++i) {
13849439183SMitchell Horne addr = exregions[i].addr;
13949439183SMitchell Horne size = exregions[i].size;
14049439183SMitchell Horne flags = exregions[i].flags;
14149439183SMitchell Horne prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages) %s %s\n",
14249439183SMitchell Horne addr, addr + size - 1, size / mbyte, size / PAGE_SIZE,
14349439183SMitchell Horne (flags & EXFLAG_NOALLOC) ? "NoAlloc" : "",
14449439183SMitchell Horne (flags & EXFLAG_NODUMP) ? "NoDump" : "");
14549439183SMitchell Horne }
14649439183SMitchell Horne
14749439183SMitchell Horne #ifdef DEBUG
14849439183SMitchell Horne prfunc("Avail lists:\n");
14949439183SMitchell Horne for (i = 0; phys_avail[i] != 0; ++i) {
150c32946d8SKyle Evans prfunc(" phys_avail[%zu] 0x%08jx\n", i,
1517b5cb32fSKyle Evans (uintmax_t)phys_avail[i]);
15249439183SMitchell Horne }
15349439183SMitchell Horne for (i = 0; dump_avail[i] != 0; ++i) {
154c32946d8SKyle Evans prfunc(" dump_avail[%zu] 0x%08jx\n", i,
1557b5cb32fSKyle Evans (uintmax_t)dump_avail[i]);
15649439183SMitchell Horne }
15749439183SMitchell Horne #endif
15849439183SMitchell Horne }
15949439183SMitchell Horne
16049439183SMitchell Horne /*
16149439183SMitchell Horne * Print the contents of the static mapping table. Used for bootverbose.
16249439183SMitchell Horne */
16349439183SMitchell Horne void
physmem_print_tables(void)16449439183SMitchell Horne physmem_print_tables(void)
16549439183SMitchell Horne {
16649439183SMitchell Horne
16749439183SMitchell Horne physmem_dump_tables(printf);
16849439183SMitchell Horne }
16949439183SMitchell Horne
17049439183SMitchell Horne /*
17149439183SMitchell Horne * Walk the list of hardware regions, processing it against the list of
17249439183SMitchell Horne * exclusions that contain the given exflags, and generating an "avail list".
17349439183SMitchell Horne *
17418ce865aSOleksandr Tymoshenko * If maxphyssz is not zero it sets upper limit, in bytes, for the total
17518ce865aSOleksandr Tymoshenko * "avail list" size. Walk stops once the limit is reached and the last region
17618ce865aSOleksandr Tymoshenko * is cut short if necessary.
17718ce865aSOleksandr Tymoshenko *
17849439183SMitchell Horne * Updates the value at *pavail with the sum of all pages in all hw regions.
17949439183SMitchell Horne *
180*f45213c7SMark Johnston * Returns the number of entries in the avail list, which is twice the number
181*f45213c7SMark Johnston * of returned regions.
18249439183SMitchell Horne */
18349439183SMitchell Horne static size_t
regions_to_avail(vm_paddr_t * avail,uint32_t exflags,size_t maxavail,uint64_t maxphyssz,long * pavail,long * prealmem)18449439183SMitchell Horne regions_to_avail(vm_paddr_t *avail, uint32_t exflags, size_t maxavail,
18518ce865aSOleksandr Tymoshenko uint64_t maxphyssz, long *pavail, long *prealmem)
18649439183SMitchell Horne {
18749439183SMitchell Horne size_t acnt, exi, hwi;
188d8bff5b6SAndrew Turner uint64_t adj, end, start, xend, xstart;
18949439183SMitchell Horne long availmem, totalmem;
19049439183SMitchell Horne const struct region *exp, *hwp;
19118ce865aSOleksandr Tymoshenko uint64_t availsz;
19249439183SMitchell Horne
193191e6a60SMitchell Horne bzero(avail, maxavail * sizeof(vm_paddr_t));
194191e6a60SMitchell Horne
19549439183SMitchell Horne totalmem = 0;
19649439183SMitchell Horne availmem = 0;
19718ce865aSOleksandr Tymoshenko availsz = 0;
19849439183SMitchell Horne acnt = 0;
19949439183SMitchell Horne for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) {
200d8bff5b6SAndrew Turner adj = round_page(hwp->addr) - hwp->addr;
201d8bff5b6SAndrew Turner start = round_page(hwp->addr);
202d8bff5b6SAndrew Turner end = trunc_page(hwp->size + adj) + start;
20349439183SMitchell Horne totalmem += atop((vm_offset_t)(end - start));
20449439183SMitchell Horne for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) {
20549439183SMitchell Horne /*
20649439183SMitchell Horne * If the excluded region does not match given flags,
20749439183SMitchell Horne * continue checking with the next excluded region.
20849439183SMitchell Horne */
20949439183SMitchell Horne if ((exp->flags & exflags) == 0)
21049439183SMitchell Horne continue;
21149439183SMitchell Horne xstart = exp->addr;
21249439183SMitchell Horne xend = exp->size + xstart;
21349439183SMitchell Horne /*
21449439183SMitchell Horne * If the excluded region ends before this hw region,
21549439183SMitchell Horne * continue checking with the next excluded region.
21649439183SMitchell Horne */
21749439183SMitchell Horne if (xend <= start)
21849439183SMitchell Horne continue;
21949439183SMitchell Horne /*
22049439183SMitchell Horne * If the excluded region begins after this hw region
22149439183SMitchell Horne * we're done because both lists are sorted.
22249439183SMitchell Horne */
22349439183SMitchell Horne if (xstart >= end)
22449439183SMitchell Horne break;
22549439183SMitchell Horne /*
22649439183SMitchell Horne * If the excluded region completely covers this hw
22749439183SMitchell Horne * region, shrink this hw region to zero size.
22849439183SMitchell Horne */
22949439183SMitchell Horne if ((start >= xstart) && (end <= xend)) {
23049439183SMitchell Horne start = xend;
23149439183SMitchell Horne end = xend;
23249439183SMitchell Horne break;
23349439183SMitchell Horne }
23449439183SMitchell Horne /*
23549439183SMitchell Horne * If the excluded region falls wholly within this hw
23649439183SMitchell Horne * region without abutting or overlapping the beginning
23749439183SMitchell Horne * or end, create an available entry from the leading
23849439183SMitchell Horne * fragment, then adjust the start of this hw region to
23949439183SMitchell Horne * the end of the excluded region, and continue checking
24049439183SMitchell Horne * the next excluded region because another exclusion
24149439183SMitchell Horne * could affect the remainder of this hw region.
24249439183SMitchell Horne */
24349439183SMitchell Horne if ((xstart > start) && (xend < end)) {
24418ce865aSOleksandr Tymoshenko
24518ce865aSOleksandr Tymoshenko if ((maxphyssz != 0) &&
24618ce865aSOleksandr Tymoshenko (availsz + xstart - start > maxphyssz)) {
24718ce865aSOleksandr Tymoshenko xstart = maxphyssz + start - availsz;
24818ce865aSOleksandr Tymoshenko }
24918ce865aSOleksandr Tymoshenko if (xstart <= start)
25018ce865aSOleksandr Tymoshenko continue;
25149439183SMitchell Horne if (acnt > 0 &&
25249439183SMitchell Horne avail[acnt - 1] == (vm_paddr_t)start) {
25349439183SMitchell Horne avail[acnt - 1] = (vm_paddr_t)xstart;
25449439183SMitchell Horne } else {
25549439183SMitchell Horne avail[acnt++] = (vm_paddr_t)start;
25649439183SMitchell Horne avail[acnt++] = (vm_paddr_t)xstart;
25749439183SMitchell Horne }
25818ce865aSOleksandr Tymoshenko availsz += (xstart - start);
25949439183SMitchell Horne availmem += atop((vm_offset_t)(xstart - start));
26049439183SMitchell Horne start = xend;
26149439183SMitchell Horne continue;
26249439183SMitchell Horne }
26349439183SMitchell Horne /*
26449439183SMitchell Horne * We know the excluded region overlaps either the start
26549439183SMitchell Horne * or end of this hardware region (but not both), trim
26649439183SMitchell Horne * the excluded portion off the appropriate end.
26749439183SMitchell Horne */
26849439183SMitchell Horne if (xstart <= start)
26949439183SMitchell Horne start = xend;
27049439183SMitchell Horne else
27149439183SMitchell Horne end = xstart;
27249439183SMitchell Horne }
27349439183SMitchell Horne /*
27449439183SMitchell Horne * If the trimming actions above left a non-zero size, create an
27549439183SMitchell Horne * available entry for it.
27649439183SMitchell Horne */
27749439183SMitchell Horne if (end > start) {
27818ce865aSOleksandr Tymoshenko if ((maxphyssz != 0) &&
27918ce865aSOleksandr Tymoshenko (availsz + end - start > maxphyssz)) {
28018ce865aSOleksandr Tymoshenko end = maxphyssz + start - availsz;
28118ce865aSOleksandr Tymoshenko }
28218ce865aSOleksandr Tymoshenko if (end <= start)
28318ce865aSOleksandr Tymoshenko break;
28418ce865aSOleksandr Tymoshenko
28549439183SMitchell Horne if (acnt > 0 && avail[acnt - 1] == (vm_paddr_t)start) {
28649439183SMitchell Horne avail[acnt - 1] = (vm_paddr_t)end;
28749439183SMitchell Horne } else {
28849439183SMitchell Horne avail[acnt++] = (vm_paddr_t)start;
28949439183SMitchell Horne avail[acnt++] = (vm_paddr_t)end;
29049439183SMitchell Horne }
29118ce865aSOleksandr Tymoshenko availsz += end - start;
29249439183SMitchell Horne availmem += atop((vm_offset_t)(end - start));
29349439183SMitchell Horne }
29449439183SMitchell Horne if (acnt >= maxavail)
29549439183SMitchell Horne panic("Not enough space in the dump/phys_avail arrays");
29649439183SMitchell Horne }
29749439183SMitchell Horne
29849439183SMitchell Horne if (pavail != NULL)
29949439183SMitchell Horne *pavail = availmem;
30049439183SMitchell Horne if (prealmem != NULL)
30149439183SMitchell Horne *prealmem = totalmem;
30249439183SMitchell Horne return (acnt);
30349439183SMitchell Horne }
30449439183SMitchell Horne
30549439183SMitchell Horne /*
3067771f2a0SKyle Evans * Check if the region at idx can be merged with the region above it.
3077771f2a0SKyle Evans */
3087771f2a0SKyle Evans static size_t
merge_upper_regions(struct region * regions,size_t rcnt,size_t idx)3097771f2a0SKyle Evans merge_upper_regions(struct region *regions, size_t rcnt, size_t idx)
3107771f2a0SKyle Evans {
3117771f2a0SKyle Evans struct region *lower, *upper;
3127771f2a0SKyle Evans vm_paddr_t lend, uend;
3137771f2a0SKyle Evans size_t i, mergecnt, movecnt;
3147771f2a0SKyle Evans
3157771f2a0SKyle Evans lower = ®ions[idx];
3167771f2a0SKyle Evans lend = lower->addr + lower->size;
3177771f2a0SKyle Evans
3187771f2a0SKyle Evans /*
3197771f2a0SKyle Evans * Continue merging in upper entries as long as we have entries to
3207771f2a0SKyle Evans * merge; the new block could have spanned more than one, although one
3217771f2a0SKyle Evans * is likely the common case.
3227771f2a0SKyle Evans */
3237771f2a0SKyle Evans for (i = idx + 1; i < rcnt; i++) {
3247771f2a0SKyle Evans upper = ®ions[i];
3257771f2a0SKyle Evans if (lend < upper->addr || lower->flags != upper->flags)
3267771f2a0SKyle Evans break;
3277771f2a0SKyle Evans
3287771f2a0SKyle Evans uend = upper->addr + upper->size;
3297771f2a0SKyle Evans if (uend > lend) {
3307771f2a0SKyle Evans lower->size += uend - lend;
3317771f2a0SKyle Evans lend = lower->addr + lower->size;
3327771f2a0SKyle Evans }
3337771f2a0SKyle Evans
3347771f2a0SKyle Evans if (uend >= lend) {
3357771f2a0SKyle Evans /*
3367771f2a0SKyle Evans * If we didn't move past the end of the upper region,
3377771f2a0SKyle Evans * then we don't need to bother checking for another
3387771f2a0SKyle Evans * merge because it would have been done already. Just
3397771f2a0SKyle Evans * increment i once more to maintain the invariant that
3407771f2a0SKyle Evans * i is one past the last entry merged.
3417771f2a0SKyle Evans */
3427771f2a0SKyle Evans i++;
3437771f2a0SKyle Evans break;
3447771f2a0SKyle Evans }
3457771f2a0SKyle Evans }
3467771f2a0SKyle Evans
3477771f2a0SKyle Evans /*
3487771f2a0SKyle Evans * We merged in the entries from [idx + 1, i); physically move the tail
3497771f2a0SKyle Evans * end at [i, rcnt) if we need to.
3507771f2a0SKyle Evans */
3517771f2a0SKyle Evans mergecnt = i - (idx + 1);
3527771f2a0SKyle Evans if (mergecnt > 0) {
3537771f2a0SKyle Evans movecnt = rcnt - i;
3547771f2a0SKyle Evans if (movecnt == 0) {
3557771f2a0SKyle Evans /* Merged all the way to the end, just decrease rcnt. */
3567771f2a0SKyle Evans rcnt = idx + 1;
3577771f2a0SKyle Evans } else {
3587771f2a0SKyle Evans memmove(®ions[idx + 1], ®ions[idx + mergecnt + 1],
3597771f2a0SKyle Evans movecnt * sizeof(*regions));
3607771f2a0SKyle Evans rcnt -= mergecnt;
3617771f2a0SKyle Evans }
3627771f2a0SKyle Evans }
3637771f2a0SKyle Evans return (rcnt);
3647771f2a0SKyle Evans }
3657771f2a0SKyle Evans
3667771f2a0SKyle Evans /*
36749439183SMitchell Horne * Insertion-sort a new entry into a regions list; sorted by start address.
36849439183SMitchell Horne */
36949439183SMitchell Horne static size_t
insert_region(struct region * regions,size_t rcnt,vm_paddr_t addr,vm_size_t size,uint32_t flags)37049439183SMitchell Horne insert_region(struct region *regions, size_t rcnt, vm_paddr_t addr,
37149439183SMitchell Horne vm_size_t size, uint32_t flags)
37249439183SMitchell Horne {
37349439183SMitchell Horne size_t i;
3747771f2a0SKyle Evans vm_paddr_t nend, rend;
37549439183SMitchell Horne struct region *ep, *rp;
37649439183SMitchell Horne
3777771f2a0SKyle Evans nend = addr + size;
37849439183SMitchell Horne ep = regions + rcnt;
37949439183SMitchell Horne for (i = 0, rp = regions; i < rcnt; ++i, ++rp) {
3807771f2a0SKyle Evans rend = rp->addr + rp->size;
381cc0fe048SKyle Evans if (flags == rp->flags) {
3827771f2a0SKyle Evans if (addr <= rp->addr && nend >= rp->addr) {
3837771f2a0SKyle Evans /*
3847771f2a0SKyle Evans * New mapping overlaps at the beginning, shift
3857771f2a0SKyle Evans * for any difference in the beginning then
3867771f2a0SKyle Evans * shift if the new mapping extends past.
3877771f2a0SKyle Evans */
3887771f2a0SKyle Evans rp->size += rp->addr - addr;
38949439183SMitchell Horne rp->addr = addr;
3907771f2a0SKyle Evans if (nend > rend) {
3917771f2a0SKyle Evans rp->size += nend - rend;
3927771f2a0SKyle Evans rcnt = merge_upper_regions(regions,
3937771f2a0SKyle Evans rcnt, i);
3947771f2a0SKyle Evans }
39549439183SMitchell Horne return (rcnt);
3967771f2a0SKyle Evans } else if (addr <= rend && nend > rp->addr) {
3977771f2a0SKyle Evans /*
3987771f2a0SKyle Evans * New mapping is either entirely contained
3997771f2a0SKyle Evans * within or it's overlapping at the end.
4007771f2a0SKyle Evans */
4017771f2a0SKyle Evans if (nend > rend) {
4027771f2a0SKyle Evans rp->size += nend - rend;
4037771f2a0SKyle Evans rcnt = merge_upper_regions(regions,
4047771f2a0SKyle Evans rcnt, i);
4057771f2a0SKyle Evans }
40649439183SMitchell Horne return (rcnt);
40749439183SMitchell Horne }
408cc0fe048SKyle Evans } else if ((flags != 0) && (rp->flags != 0)) {
409cc0fe048SKyle Evans /*
410cc0fe048SKyle Evans * If we're duplicating an entry that already exists
411cc0fe048SKyle Evans * exactly, just upgrade its flags as needed. We could
412cc0fe048SKyle Evans * do more if we find that we have differently specified
413cc0fe048SKyle Evans * flags clipping existing excluding regions, but that's
414cc0fe048SKyle Evans * probably rare.
415cc0fe048SKyle Evans */
416cc0fe048SKyle Evans if (addr == rp->addr && nend == rend) {
417cc0fe048SKyle Evans rp->flags |= flags;
418cc0fe048SKyle Evans return (rcnt);
41949439183SMitchell Horne }
420cc0fe048SKyle Evans }
421cc0fe048SKyle Evans
42249439183SMitchell Horne if (addr < rp->addr) {
42349439183SMitchell Horne bcopy(rp, rp + 1, (ep - rp) * sizeof(*rp));
42449439183SMitchell Horne break;
42549439183SMitchell Horne }
42649439183SMitchell Horne }
42749439183SMitchell Horne rp->addr = addr;
42849439183SMitchell Horne rp->size = size;
42949439183SMitchell Horne rp->flags = flags;
43049439183SMitchell Horne rcnt++;
43149439183SMitchell Horne
43249439183SMitchell Horne return (rcnt);
43349439183SMitchell Horne }
43449439183SMitchell Horne
43549439183SMitchell Horne /*
43649439183SMitchell Horne * Add a hardware memory region.
43749439183SMitchell Horne */
43849439183SMitchell Horne void
physmem_hardware_region(uint64_t pa,uint64_t sz)43949439183SMitchell Horne physmem_hardware_region(uint64_t pa, uint64_t sz)
44049439183SMitchell Horne {
44149439183SMitchell Horne /*
44249439183SMitchell Horne * Filter out the page at PA 0x00000000. The VM can't handle it, as
44349439183SMitchell Horne * pmap_extract() == 0 means failure.
44449439183SMitchell Horne */
44549439183SMitchell Horne if (pa == 0) {
44649439183SMitchell Horne if (sz <= PAGE_SIZE)
44749439183SMitchell Horne return;
44849439183SMitchell Horne pa = PAGE_SIZE;
44949439183SMitchell Horne sz -= PAGE_SIZE;
45049439183SMitchell Horne } else if (pa > MAX_PHYS_ADDR) {
45149439183SMitchell Horne /* This range is past usable memory, ignore it */
45249439183SMitchell Horne return;
45349439183SMitchell Horne }
45449439183SMitchell Horne
45549439183SMitchell Horne /*
45649439183SMitchell Horne * Also filter out the page at the end of the physical address space --
45749439183SMitchell Horne * if addr is non-zero and addr+size is zero we wrapped to the next byte
45849439183SMitchell Horne * beyond what vm_paddr_t can express. That leads to a NULL pointer
45949439183SMitchell Horne * deref early in startup; work around it by leaving the last page out.
46049439183SMitchell Horne *
46149439183SMitchell Horne * XXX This just in: subtract out a whole megabyte, not just 1 page.
46249439183SMitchell Horne * Reducing the size by anything less than 1MB results in the NULL
46349439183SMitchell Horne * pointer deref in _vm_map_lock_read(). Better to give up a megabyte
46449439183SMitchell Horne * than leave some folks with an unusable system while we investigate.
46549439183SMitchell Horne */
46649439183SMitchell Horne if ((pa + sz) > (MAX_PHYS_ADDR - 1024 * 1024)) {
46749439183SMitchell Horne sz = MAX_PHYS_ADDR - pa + 1;
46849439183SMitchell Horne if (sz <= 1024 * 1024)
46949439183SMitchell Horne return;
47049439183SMitchell Horne sz -= 1024 * 1024;
47149439183SMitchell Horne }
47249439183SMitchell Horne
47349439183SMitchell Horne if (sz > 0 && hwcnt < nitems(hwregions))
47449439183SMitchell Horne hwcnt = insert_region(hwregions, hwcnt, pa, sz, 0);
47549439183SMitchell Horne }
47649439183SMitchell Horne
47749439183SMitchell Horne /*
47849439183SMitchell Horne * Add an exclusion region.
47949439183SMitchell Horne */
48049439183SMitchell Horne void
physmem_exclude_region(vm_paddr_t pa,vm_size_t sz,uint32_t exflags)48149439183SMitchell Horne physmem_exclude_region(vm_paddr_t pa, vm_size_t sz, uint32_t exflags)
48249439183SMitchell Horne {
48349439183SMitchell Horne vm_offset_t adj;
48449439183SMitchell Horne
48549439183SMitchell Horne /*
48649439183SMitchell Horne * Truncate the starting address down to a page boundary, and round the
48749439183SMitchell Horne * ending page up to a page boundary.
48849439183SMitchell Horne */
48949439183SMitchell Horne adj = pa - trunc_page(pa);
49049439183SMitchell Horne pa = trunc_page(pa);
49149439183SMitchell Horne sz = round_page(sz + adj);
49249439183SMitchell Horne
49349439183SMitchell Horne if (excnt >= nitems(exregions))
49449439183SMitchell Horne panic("failed to exclude region %#jx-%#jx", (uintmax_t)pa,
49549439183SMitchell Horne (uintmax_t)(pa + sz));
49649439183SMitchell Horne excnt = insert_region(exregions, excnt, pa, sz, exflags);
49749439183SMitchell Horne }
49849439183SMitchell Horne
49949439183SMitchell Horne size_t
physmem_avail(vm_paddr_t * avail,size_t maxavail)50049439183SMitchell Horne physmem_avail(vm_paddr_t *avail, size_t maxavail)
50149439183SMitchell Horne {
50249439183SMitchell Horne
50318ce865aSOleksandr Tymoshenko return (regions_to_avail(avail, EXFLAG_NOALLOC, maxavail, 0, NULL, NULL));
50449439183SMitchell Horne }
50549439183SMitchell Horne
506deb1e3b7SWarner Losh bool
physmem_excluded(vm_paddr_t pa,vm_size_t sz)507deb1e3b7SWarner Losh physmem_excluded(vm_paddr_t pa, vm_size_t sz)
508deb1e3b7SWarner Losh {
509deb1e3b7SWarner Losh const struct region *exp;
510deb1e3b7SWarner Losh size_t exi;
511deb1e3b7SWarner Losh
512deb1e3b7SWarner Losh for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) {
513deb1e3b7SWarner Losh if (pa < exp->addr || pa + sz > exp->addr + exp->size)
514deb1e3b7SWarner Losh continue;
515deb1e3b7SWarner Losh return (true);
516deb1e3b7SWarner Losh }
517deb1e3b7SWarner Losh return (false);
518deb1e3b7SWarner Losh }
519deb1e3b7SWarner Losh
5208c99dfedSAndrew Turner #ifdef _KERNEL
52149439183SMitchell Horne /*
52249439183SMitchell Horne * Process all the regions added earlier into the global avail lists.
52349439183SMitchell Horne *
52449439183SMitchell Horne * Updates the kernel global 'physmem' with the number of physical pages
52549439183SMitchell Horne * available for use (all pages not in any exclusion region).
52649439183SMitchell Horne *
52749439183SMitchell Horne * Updates the kernel global 'Maxmem' with the page number one greater then the
52849439183SMitchell Horne * last page of physical memory in the system.
52949439183SMitchell Horne */
53049439183SMitchell Horne void
physmem_init_kernel_globals(void)53149439183SMitchell Horne physmem_init_kernel_globals(void)
53249439183SMitchell Horne {
53349439183SMitchell Horne size_t nextidx;
53418ce865aSOleksandr Tymoshenko u_long hwphyssz;
53549439183SMitchell Horne
53618ce865aSOleksandr Tymoshenko hwphyssz = 0;
53718ce865aSOleksandr Tymoshenko TUNABLE_ULONG_FETCH("hw.physmem", &hwphyssz);
53818ce865aSOleksandr Tymoshenko
53918ce865aSOleksandr Tymoshenko regions_to_avail(dump_avail, EXFLAG_NODUMP, PHYS_AVAIL_ENTRIES,
54018ce865aSOleksandr Tymoshenko hwphyssz, NULL, NULL);
54149439183SMitchell Horne nextidx = regions_to_avail(phys_avail, EXFLAG_NOALLOC,
54218ce865aSOleksandr Tymoshenko PHYS_AVAIL_ENTRIES, hwphyssz, &physmem, &realmem);
54349439183SMitchell Horne if (nextidx == 0)
54449439183SMitchell Horne panic("No memory entries in phys_avail");
54549439183SMitchell Horne Maxmem = atop(phys_avail[nextidx - 1]);
54649439183SMitchell Horne }
54749439183SMitchell Horne
54849439183SMitchell Horne #ifdef DDB
54949439183SMitchell Horne #include <ddb/ddb.h>
55049439183SMitchell Horne
DB_SHOW_COMMAND_FLAGS(physmem,db_show_physmem,DB_CMD_MEMSAFE)551c84c5e00SMitchell Horne DB_SHOW_COMMAND_FLAGS(physmem, db_show_physmem, DB_CMD_MEMSAFE)
55249439183SMitchell Horne {
55349439183SMitchell Horne
55449439183SMitchell Horne physmem_dump_tables(db_printf);
55549439183SMitchell Horne }
55649439183SMitchell Horne
55749439183SMitchell Horne #endif /* DDB */
558e6cf1a08SMitchell Horne
559e6cf1a08SMitchell Horne /*
560e6cf1a08SMitchell Horne * ram pseudo driver - this reserves I/O space resources corresponding to physical
561e6cf1a08SMitchell Horne * memory regions.
562e6cf1a08SMitchell Horne */
563e6cf1a08SMitchell Horne
564e6cf1a08SMitchell Horne static void
ram_identify(driver_t * driver,device_t parent)565e6cf1a08SMitchell Horne ram_identify(driver_t *driver, device_t parent)
566e6cf1a08SMitchell Horne {
567e6cf1a08SMitchell Horne
568e6cf1a08SMitchell Horne if (resource_disabled("ram", 0))
569e6cf1a08SMitchell Horne return;
570e6cf1a08SMitchell Horne if (BUS_ADD_CHILD(parent, 0, "ram", 0) == NULL)
571e6cf1a08SMitchell Horne panic("ram_identify");
572e6cf1a08SMitchell Horne }
573e6cf1a08SMitchell Horne
574e6cf1a08SMitchell Horne static int
ram_probe(device_t dev)575e6cf1a08SMitchell Horne ram_probe(device_t dev)
576e6cf1a08SMitchell Horne {
577e6cf1a08SMitchell Horne
578e6cf1a08SMitchell Horne device_quiet(dev);
579e6cf1a08SMitchell Horne device_set_desc(dev, "System RAM");
580e6cf1a08SMitchell Horne return (BUS_PROBE_SPECIFIC);
581e6cf1a08SMitchell Horne }
582e6cf1a08SMitchell Horne
583e6cf1a08SMitchell Horne static int
ram_attach(device_t dev)584e6cf1a08SMitchell Horne ram_attach(device_t dev)
585e6cf1a08SMitchell Horne {
586e6cf1a08SMitchell Horne vm_paddr_t avail_list[PHYS_AVAIL_COUNT];
587e6cf1a08SMitchell Horne rman_res_t start, end;
588e6cf1a08SMitchell Horne int rid, i;
589e6cf1a08SMitchell Horne
590e6cf1a08SMitchell Horne rid = 0;
591e6cf1a08SMitchell Horne
592e6cf1a08SMitchell Horne /* Get the avail list. */
593e6cf1a08SMitchell Horne regions_to_avail(avail_list, EXFLAG_NOALLOC | EXFLAG_NODUMP,
594e6cf1a08SMitchell Horne PHYS_AVAIL_COUNT, 0, NULL, NULL);
595e6cf1a08SMitchell Horne
596e6cf1a08SMitchell Horne /* Reserve all memory regions. */
597e6cf1a08SMitchell Horne for (i = 0; avail_list[i + 1] != 0; i += 2) {
598e6cf1a08SMitchell Horne start = avail_list[i];
599e6cf1a08SMitchell Horne end = avail_list[i + 1];
600e6cf1a08SMitchell Horne
601e6cf1a08SMitchell Horne if (bootverbose)
602e6cf1a08SMitchell Horne device_printf(dev,
603e6cf1a08SMitchell Horne "reserving memory region: %jx-%jx\n",
604e6cf1a08SMitchell Horne (uintmax_t)start, (uintmax_t)end);
605e6cf1a08SMitchell Horne
606e6cf1a08SMitchell Horne if (bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, start, end,
607e6cf1a08SMitchell Horne end - start, 0) == NULL)
608e6cf1a08SMitchell Horne panic("ram_attach: resource %d failed to attach", rid);
609e6cf1a08SMitchell Horne rid++;
610e6cf1a08SMitchell Horne }
611e6cf1a08SMitchell Horne
612e6cf1a08SMitchell Horne return (0);
613e6cf1a08SMitchell Horne }
614e6cf1a08SMitchell Horne
615e6cf1a08SMitchell Horne static device_method_t ram_methods[] = {
616e6cf1a08SMitchell Horne /* Device interface */
617e6cf1a08SMitchell Horne DEVMETHOD(device_identify, ram_identify),
618e6cf1a08SMitchell Horne DEVMETHOD(device_probe, ram_probe),
619e6cf1a08SMitchell Horne DEVMETHOD(device_attach, ram_attach),
620e6cf1a08SMitchell Horne
621e6cf1a08SMitchell Horne DEVMETHOD_END
622e6cf1a08SMitchell Horne };
623e6cf1a08SMitchell Horne
624e6cf1a08SMitchell Horne DEFINE_CLASS_0(ram, ram_driver, ram_methods, /* no softc */ 1);
625e6cf1a08SMitchell Horne DRIVER_MODULE(ram, nexus, ram_driver, 0, 0);
626e6cf1a08SMitchell Horne #endif /* _KERNEL */
627