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