linux/arch/arm/boot/compressed/fdt_check_mem_start.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include <linux/kernel.h>
   4#include <linux/libfdt.h>
   5#include <linux/sizes.h>
   6
   7static const void *get_prop(const void *fdt, const char *node_path,
   8                            const char *property, int minlen)
   9{
  10        const void *prop;
  11        int offset, len;
  12
  13        offset = fdt_path_offset(fdt, node_path);
  14        if (offset < 0)
  15                return NULL;
  16
  17        prop = fdt_getprop(fdt, offset, property, &len);
  18        if (!prop || len < minlen)
  19                return NULL;
  20
  21        return prop;
  22}
  23
  24static uint32_t get_cells(const void *fdt, const char *name)
  25{
  26        const fdt32_t *prop = get_prop(fdt, "/", name, sizeof(fdt32_t));
  27
  28        if (!prop) {
  29                /* default */
  30                return 1;
  31        }
  32
  33        return fdt32_ld(prop);
  34}
  35
  36static uint64_t get_val(const fdt32_t *cells, uint32_t ncells)
  37{
  38        uint64_t r;
  39
  40        r = fdt32_ld(cells);
  41        if (ncells > 1)
  42                r = (r << 32) | fdt32_ld(cells + 1);
  43
  44        return r;
  45}
  46
  47/*
  48 * Check the start of physical memory
  49 *
  50 * Traditionally, the start address of physical memory is obtained by masking
  51 * the program counter.  However, this does require that this address is a
  52 * multiple of 128 MiB, precluding booting Linux on platforms where this
  53 * requirement is not fulfilled.
  54 * Hence validate the calculated address against the memory information in the
  55 * DTB, and, if out-of-range, replace it by the real start address.
  56 * To preserve backwards compatibility (systems reserving a block of memory
  57 * at the start of physical memory, kdump, ...), the traditional method is
  58 * always used if it yields a valid address.
  59 *
  60 * Return value: start address of physical memory to use
  61 */
  62uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt)
  63{
  64        uint32_t addr_cells, size_cells, base;
  65        uint32_t fdt_mem_start = 0xffffffff;
  66        const fdt32_t *reg, *endp;
  67        uint64_t size, end;
  68        const char *type;
  69        int offset, len;
  70
  71        if (!fdt)
  72                return mem_start;
  73
  74        if (fdt_magic(fdt) != FDT_MAGIC)
  75                return mem_start;
  76
  77        /* There may be multiple cells on LPAE platforms */
  78        addr_cells = get_cells(fdt, "#address-cells");
  79        size_cells = get_cells(fdt, "#size-cells");
  80        if (addr_cells > 2 || size_cells > 2)
  81                return mem_start;
  82
  83        /* Walk all memory nodes and regions */
  84        for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0;
  85             offset = fdt_next_node(fdt, offset, NULL)) {
  86                type = fdt_getprop(fdt, offset, "device_type", NULL);
  87                if (!type || strcmp(type, "memory"))
  88                        continue;
  89
  90                reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len);
  91                if (!reg)
  92                        reg = fdt_getprop(fdt, offset, "reg", &len);
  93                if (!reg)
  94                        continue;
  95
  96                for (endp = reg + (len / sizeof(fdt32_t));
  97                     endp - reg >= addr_cells + size_cells;
  98                     reg += addr_cells + size_cells) {
  99                        size = get_val(reg + addr_cells, size_cells);
 100                        if (!size)
 101                                continue;
 102
 103                        if (addr_cells > 1 && fdt32_ld(reg)) {
 104                                /* Outside 32-bit address space, skipping */
 105                                continue;
 106                        }
 107
 108                        base = fdt32_ld(reg + addr_cells - 1);
 109                        end = base + size;
 110                        if (mem_start >= base && mem_start < end) {
 111                                /* Calculated address is valid, use it */
 112                                return mem_start;
 113                        }
 114
 115                        if (base < fdt_mem_start)
 116                                fdt_mem_start = base;
 117                }
 118        }
 119
 120        if (fdt_mem_start == 0xffffffff) {
 121                /* No usable memory found, falling back to default */
 122                return mem_start;
 123        }
 124
 125        /*
 126         * The calculated address is not usable.
 127         * Use the lowest usable physical memory address from the DTB instead,
 128         * and make sure this is a multiple of 2 MiB for phys/virt patching.
 129         */
 130        return round_up(fdt_mem_start, SZ_2M);
 131}
 132