xref: /freebsd-src/sys/kern/subr_physmem.c (revision f45213c74cbac7f41ab3c94440cb37a2fd92a449)
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 = &regions[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 = &regions[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(&regions[idx + 1], &regions[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