Path: blob/master/arch/arm/boot/compressed/fdt_check_mem_start.c
29269 views
// SPDX-License-Identifier: GPL-2.0-only12#include <linux/kernel.h>3#include <linux/libfdt.h>4#include <linux/sizes.h>5#include "misc.h"67static const void *get_prop(const void *fdt, const char *node_path,8const char *property, int minlen)9{10const void *prop;11int offset, len;1213offset = fdt_path_offset(fdt, node_path);14if (offset < 0)15return NULL;1617prop = fdt_getprop(fdt, offset, property, &len);18if (!prop || len < minlen)19return NULL;2021return prop;22}2324static uint32_t get_cells(const void *fdt, const char *name)25{26const fdt32_t *prop = get_prop(fdt, "/", name, sizeof(fdt32_t));2728if (!prop) {29/* default */30return 1;31}3233return fdt32_ld(prop);34}3536static uint64_t get_val(const fdt32_t *cells, uint32_t ncells)37{38uint64_t r;3940r = fdt32_ld(cells);41if (ncells > 1)42r = (r << 32) | fdt32_ld(cells + 1);4344return r;45}4647/*48* Check the start of physical memory49*50* Traditionally, the start address of physical memory is obtained by masking51* the program counter. However, this does require that this address is a52* multiple of 128 MiB, precluding booting Linux on platforms where this53* requirement is not fulfilled.54* Hence validate the calculated address against the memory information in the55* DTB, and, if out-of-range, replace it by the real start address.56* To preserve backwards compatibility (systems reserving a block of memory57* at the start of physical memory, kdump, ...), the traditional method is58* used if it yields a valid address, unless the "linux,usable-memory-range"59* property is present.60*61* Return value: start address of physical memory to use62*/63uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt)64{65uint32_t addr_cells, size_cells, usable_base, base;66uint32_t fdt_mem_start = 0xffffffff;67const fdt32_t *usable, *reg, *endp;68uint64_t size, usable_end, end;69const char *type;70int offset, len;7172if (!fdt)73return mem_start;7475if (fdt_magic(fdt) != FDT_MAGIC)76return mem_start;7778/* There may be multiple cells on LPAE platforms */79addr_cells = get_cells(fdt, "#address-cells");80size_cells = get_cells(fdt, "#size-cells");81if (addr_cells > 2 || size_cells > 2)82return mem_start;8384/*85* Usable memory in case of a crash dump kernel86* This property describes a limitation: memory within this range is87* only valid when also described through another mechanism88*/89usable = get_prop(fdt, "/chosen", "linux,usable-memory-range",90(addr_cells + size_cells) * sizeof(fdt32_t));91if (usable) {92size = get_val(usable + addr_cells, size_cells);93if (!size)94return mem_start;9596if (addr_cells > 1 && fdt32_ld(usable)) {97/* Outside 32-bit address space */98return mem_start;99}100101usable_base = fdt32_ld(usable + addr_cells - 1);102usable_end = usable_base + size;103}104105/* Walk all memory nodes and regions */106for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0;107offset = fdt_next_node(fdt, offset, NULL)) {108type = fdt_getprop(fdt, offset, "device_type", NULL);109if (!type || strcmp(type, "memory"))110continue;111112reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len);113if (!reg)114reg = fdt_getprop(fdt, offset, "reg", &len);115if (!reg)116continue;117118for (endp = reg + (len / sizeof(fdt32_t));119endp - reg >= addr_cells + size_cells;120reg += addr_cells + size_cells) {121size = get_val(reg + addr_cells, size_cells);122if (!size)123continue;124125if (addr_cells > 1 && fdt32_ld(reg)) {126/* Outside 32-bit address space, skipping */127continue;128}129130base = fdt32_ld(reg + addr_cells - 1);131end = base + size;132if (usable) {133/*134* Clip to usable range, which takes precedence135* over mem_start136*/137if (base < usable_base)138base = usable_base;139140if (end > usable_end)141end = usable_end;142143if (end <= base)144continue;145} else if (mem_start >= base && mem_start < end) {146/* Calculated address is valid, use it */147return mem_start;148}149150if (base < fdt_mem_start)151fdt_mem_start = base;152}153}154155if (fdt_mem_start == 0xffffffff) {156/* No usable memory found, falling back to default */157return mem_start;158}159160/*161* The calculated address is not usable, or was overridden by the162* "linux,usable-memory-range" property.163* Use the lowest usable physical memory address from the DTB instead,164* and make sure this is a multiple of 2 MiB for phys/virt patching.165*/166return round_up(fdt_mem_start, SZ_2M);167}168169170