]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/arm/physmem.c
Merge ^/vendor/libc++/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / arm / arm / physmem.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_acpi.h"
33 #include "opt_ddb.h"
34
35 /*
36  * Routines for describing and initializing anything related to physical memory.
37  */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <vm/vm.h>
42 #include <vm/vm_param.h>
43 #include <vm/vm_page.h>
44 #include <vm/vm_phys.h>
45 #include <machine/md_var.h>
46 #include <arm/include/physmem.h>
47
48 /*
49  * These structures are used internally to keep track of regions of physical
50  * ram, and regions within the physical ram that need to be excluded.  An
51  * exclusion region can be excluded from crash dumps, from the vm pool of pages
52  * that can be allocated, or both, depending on the exclusion flags associated
53  * with the region.
54  */
55 #ifdef DEV_ACPI
56 #define MAX_HWCNT       32      /* ACPI needs more regions */
57 #define MAX_EXCNT       32
58 #else
59 #define MAX_HWCNT       16
60 #define MAX_EXCNT       16
61 #endif
62
63 #if defined(__arm__)
64 #define MAX_PHYS_ADDR   0xFFFFFFFFull
65 #define pm_btop(x)      arm32_btop(x)
66 #elif defined(__aarch64__)
67 #define MAX_PHYS_ADDR   0xFFFFFFFFFFFFFFFFull
68 #define pm_btop(x)      arm64_btop(x)
69 #endif
70
71 struct region {
72         vm_paddr_t      addr;
73         vm_size_t       size;
74         uint32_t        flags;
75 };
76
77 static struct region hwregions[MAX_HWCNT];
78 static struct region exregions[MAX_EXCNT];
79
80 static size_t hwcnt;
81 static size_t excnt;
82
83 /*
84  * realmem is the total number of hardware pages, excluded or not.
85  * Maxmem is one greater than the last physical page number.
86  */
87 long realmem;
88 long Maxmem;
89
90 /* The address at which the kernel was loaded.  Set early in initarm(). */
91 vm_paddr_t arm_physmem_kernaddr;
92
93 /*
94  * Print the contents of the physical and excluded region tables using the
95  * provided printf-like output function (which will be either printf or
96  * db_printf).
97  */
98 static void
99 physmem_dump_tables(int (*prfunc)(const char *, ...))
100 {
101         int flags, i;
102         uintmax_t addr, size;
103         const unsigned int mbyte = 1024 * 1024;
104
105         prfunc("Physical memory chunk(s):\n");
106         for (i = 0; i < hwcnt; ++i) {
107                 addr = hwregions[i].addr;
108                 size = hwregions[i].size;
109                 prfunc("  0x%08jx - 0x%08jx, %5ju MB (%7ju pages)\n", addr,
110                     addr + size - 1, size / mbyte, size / PAGE_SIZE);
111         }
112
113         prfunc("Excluded memory regions:\n");
114         for (i = 0; i < excnt; ++i) {
115                 addr  = exregions[i].addr;
116                 size  = exregions[i].size;
117                 flags = exregions[i].flags;
118                 prfunc("  0x%08jx - 0x%08jx, %5ju MB (%7ju pages) %s %s\n",
119                     addr, addr + size - 1, size / mbyte, size / PAGE_SIZE,
120                     (flags & EXFLAG_NOALLOC) ? "NoAlloc" : "",
121                     (flags & EXFLAG_NODUMP)  ? "NoDump" : "");
122         }
123
124 #ifdef DEBUG
125         prfunc("Avail lists:\n");
126         for (i = 0; phys_avail[i] != 0; ++i) {
127                 prfunc("  phys_avail[%d] 0x%08x\n", i, phys_avail[i]);
128         }
129         for (i = 0; dump_avail[i] != 0; ++i) {
130                 prfunc("  dump_avail[%d] 0x%08x\n", i, dump_avail[i]);
131         }
132 #endif
133 }
134
135 /*
136  * Print the contents of the static mapping table.  Used for bootverbose.
137  */
138 void
139 arm_physmem_print_tables(void)
140 {
141
142         physmem_dump_tables(printf);
143 }
144
145 /*
146  * Walk the list of hardware regions, processing it against the list of
147  * exclusions that contain the given exflags, and generating an "avail list".
148  *
149  * Updates the value at *pavail with the sum of all pages in all hw regions.
150  *
151  * Returns the number of pages of non-excluded memory added to the avail list.
152  */
153 static size_t
154 regions_to_avail(vm_paddr_t *avail, uint32_t exflags, size_t maxavail,
155     long *pavail, long *prealmem)
156 {
157         size_t acnt, exi, hwi;
158         uint64_t end, start, xend, xstart;
159         long availmem, totalmem;
160         const struct region *exp, *hwp;
161
162         totalmem = 0;
163         availmem = 0;
164         acnt = 0;
165         for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) {
166                 start = hwp->addr;
167                 end   = hwp->size + start;
168                 totalmem += pm_btop((vm_offset_t)(end - start));
169                 for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) {
170                         /*
171                          * If the excluded region does not match given flags,
172                          * continue checking with the next excluded region.
173                          */
174                         if ((exp->flags & exflags) == 0)
175                                 continue;
176                         xstart = exp->addr;
177                         xend   = exp->size + xstart;
178                         /*
179                          * If the excluded region ends before this hw region,
180                          * continue checking with the next excluded region.
181                          */
182                         if (xend <= start)
183                                 continue;
184                         /*
185                          * If the excluded region begins after this hw region
186                          * we're done because both lists are sorted.
187                          */
188                         if (xstart >= end)
189                                 break;
190                         /*
191                          * If the excluded region completely covers this hw
192                          * region, shrink this hw region to zero size.
193                          */
194                         if ((start >= xstart) && (end <= xend)) {
195                                 start = xend;
196                                 end = xend;
197                                 break;
198                         }
199                         /*
200                          * If the excluded region falls wholly within this hw
201                          * region without abutting or overlapping the beginning
202                          * or end, create an available entry from the leading
203                          * fragment, then adjust the start of this hw region to
204                          * the end of the excluded region, and continue checking
205                          * the next excluded region because another exclusion
206                          * could affect the remainder of this hw region.
207                          */
208                         if ((xstart > start) && (xend < end)) {
209                                 if (acnt > 0 &&
210                                     avail[acnt - 1] == (vm_paddr_t)start) {
211                                         avail[acnt - 1] = (vm_paddr_t)xstart;
212                                 } else {
213                                         avail[acnt++] = (vm_paddr_t)start;
214                                         avail[acnt++] = (vm_paddr_t)xstart;
215                                 }
216                                 availmem +=
217                                     pm_btop((vm_offset_t)(xstart - start));
218                                 start = xend;
219                                 continue;
220                         }
221                         /*
222                          * We know the excluded region overlaps either the start
223                          * or end of this hardware region (but not both), trim
224                          * the excluded portion off the appropriate end.
225                          */
226                         if (xstart <= start)
227                                 start = xend;
228                         else
229                                 end = xstart;
230                 }
231                 /*
232                  * If the trimming actions above left a non-zero size, create an
233                  * available entry for it.
234                  */
235                 if (end > start) {
236                         if (acnt > 0 && avail[acnt - 1] == (vm_paddr_t)start) {
237                                 avail[acnt - 1] = (vm_paddr_t)end;
238                         } else {
239                                 avail[acnt++] = (vm_paddr_t)start;
240                                 avail[acnt++] = (vm_paddr_t)end;
241                         }
242                         availmem += pm_btop((vm_offset_t)(end - start));
243                 }
244                 if (acnt >= maxavail)
245                         panic("Not enough space in the dump/phys_avail arrays");
246         }
247
248         if (pavail != NULL)
249                 *pavail = availmem;
250         if (prealmem != NULL)
251                 *prealmem = totalmem;
252         return (acnt);
253 }
254
255 /*
256  * Insertion-sort a new entry into a regions list; sorted by start address.
257  */
258 static size_t
259 insert_region(struct region *regions, size_t rcnt, vm_paddr_t addr,
260     vm_size_t size, uint32_t flags)
261 {
262         size_t i;
263         struct region *ep, *rp;
264
265         ep = regions + rcnt;
266         for (i = 0, rp = regions; i < rcnt; ++i, ++rp) {
267                 if (rp->addr == addr && rp->size == size) /* Pure dup. */
268                         return (rcnt);
269                 if (flags == rp->flags) {
270                         if (addr + size == rp->addr) {
271                                 rp->addr = addr;
272                                 rp->size += size;
273                                 return (rcnt);
274                         } else if (rp->addr + rp->size == addr) {
275                                 rp->size += size;
276                                 return (rcnt);
277                         }
278                 }
279                 if (addr < rp->addr) {
280                         bcopy(rp, rp + 1, (ep - rp) * sizeof(*rp));
281                         break;
282                 }
283         }
284         rp->addr  = addr;
285         rp->size  = size;
286         rp->flags = flags;
287         rcnt++;
288
289         return (rcnt);
290 }
291
292 /*
293  * Add a hardware memory region.
294  */
295 void
296 arm_physmem_hardware_region(uint64_t pa, uint64_t sz)
297 {
298         vm_offset_t adj;
299
300         /*
301          * Filter out the page at PA 0x00000000.  The VM can't handle it, as
302          * pmap_extract() == 0 means failure.
303          */
304         if (pa == 0) {
305                 if (sz <= PAGE_SIZE)
306                         return;
307                 pa  = PAGE_SIZE;
308                 sz -= PAGE_SIZE;
309         } else if (pa > MAX_PHYS_ADDR) {
310                 /* This range is past usable memory, ignore it */
311                 return;
312         }
313
314         /*
315          * Also filter out the page at the end of the physical address space --
316          * if addr is non-zero and addr+size is zero we wrapped to the next byte
317          * beyond what vm_paddr_t can express.  That leads to a NULL pointer
318          * deref early in startup; work around it by leaving the last page out.
319          *
320          * XXX This just in:  subtract out a whole megabyte, not just 1 page.
321          * Reducing the size by anything less than 1MB results in the NULL
322          * pointer deref in _vm_map_lock_read().  Better to give up a megabyte
323          * than leave some folks with an unusable system while we investigate.
324          */
325         if ((pa + sz) > (MAX_PHYS_ADDR - 1024 * 1024)) {
326                 sz = MAX_PHYS_ADDR - pa + 1;
327                 if (sz <= 1024 * 1024)
328                         return;
329                 sz -= 1024 * 1024;
330         }
331
332         /*
333          * Round the starting address up to a page boundary, and truncate the
334          * ending page down to a page boundary.
335          */
336         adj = round_page(pa) - pa;
337         pa  = round_page(pa);
338         sz  = trunc_page(sz - adj);
339
340         if (sz > 0 && hwcnt < nitems(hwregions))
341                 hwcnt = insert_region(hwregions, hwcnt, pa, sz, 0);
342 }
343
344 /*
345  * Add an exclusion region.
346  */
347 void
348 arm_physmem_exclude_region(vm_paddr_t pa, vm_size_t sz, uint32_t exflags)
349 {
350         vm_offset_t adj;
351
352         /*
353          * Truncate the starting address down to a page boundary, and round the
354          * ending page up to a page boundary.
355          */
356         adj = pa - trunc_page(pa);
357         pa  = trunc_page(pa);
358         sz  = round_page(sz + adj);
359
360         if (excnt >= nitems(exregions))
361                 panic("failed to exclude region %#jx-%#jx", (uintmax_t)pa,
362                     (uintmax_t)(pa + sz));
363         excnt = insert_region(exregions, excnt, pa, sz, exflags);
364 }
365
366 size_t
367 arm_physmem_avail(vm_paddr_t *avail, size_t maxavail)
368 {
369
370         return (regions_to_avail(avail, EXFLAG_NOALLOC, maxavail, NULL, NULL));
371 }
372
373 /*
374  * Process all the regions added earlier into the global avail lists.
375  *
376  * Updates the kernel global 'physmem' with the number of physical pages
377  * available for use (all pages not in any exclusion region).
378  *
379  * Updates the kernel global 'Maxmem' with the page number one greater then the
380  * last page of physical memory in the system.
381  */
382 void
383 arm_physmem_init_kernel_globals(void)
384 {
385         size_t nextidx;
386
387         regions_to_avail(dump_avail, EXFLAG_NODUMP, PHYS_AVAIL_ENTRIES, NULL,
388             NULL);
389         nextidx = regions_to_avail(phys_avail, EXFLAG_NOALLOC,
390             PHYS_AVAIL_ENTRIES, &physmem, &realmem);
391         if (nextidx == 0)
392                 panic("No memory entries in phys_avail");
393         Maxmem = atop(phys_avail[nextidx - 1]);
394 }
395
396 #ifdef DDB
397 #include <ddb/ddb.h>
398
399 DB_SHOW_COMMAND(physmem, db_show_physmem)
400 {
401
402         physmem_dump_tables(db_printf);
403 }
404
405 #endif /* DDB */
406