cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fdt_check_mem_start.c (4408B)


      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 * 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 use
     62 */
     63uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt)
     64{
     65	uint32_t addr_cells, size_cells, usable_base, base;
     66	uint32_t fdt_mem_start = 0xffffffff;
     67	const fdt32_t *usable, *reg, *endp;
     68	uint64_t size, usable_end, end;
     69	const char *type;
     70	int offset, len;
     71
     72	if (!fdt)
     73		return mem_start;
     74
     75	if (fdt_magic(fdt) != FDT_MAGIC)
     76		return mem_start;
     77
     78	/* There may be multiple cells on LPAE platforms */
     79	addr_cells = get_cells(fdt, "#address-cells");
     80	size_cells = get_cells(fdt, "#size-cells");
     81	if (addr_cells > 2 || size_cells > 2)
     82		return mem_start;
     83
     84	/*
     85	 * Usable memory in case of a crash dump kernel
     86	 * This property describes a limitation: memory within this range is
     87	 * only valid when also described through another mechanism
     88	 */
     89	usable = get_prop(fdt, "/chosen", "linux,usable-memory-range",
     90			  (addr_cells + size_cells) * sizeof(fdt32_t));
     91	if (usable) {
     92		size = get_val(usable + addr_cells, size_cells);
     93		if (!size)
     94			return mem_start;
     95
     96		if (addr_cells > 1 && fdt32_ld(usable)) {
     97			/* Outside 32-bit address space */
     98			return mem_start;
     99		}
    100
    101		usable_base = fdt32_ld(usable + addr_cells - 1);
    102		usable_end = usable_base + size;
    103	}
    104
    105	/* Walk all memory nodes and regions */
    106	for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0;
    107	     offset = fdt_next_node(fdt, offset, NULL)) {
    108		type = fdt_getprop(fdt, offset, "device_type", NULL);
    109		if (!type || strcmp(type, "memory"))
    110			continue;
    111
    112		reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len);
    113		if (!reg)
    114			reg = fdt_getprop(fdt, offset, "reg", &len);
    115		if (!reg)
    116			continue;
    117
    118		for (endp = reg + (len / sizeof(fdt32_t));
    119		     endp - reg >= addr_cells + size_cells;
    120		     reg += addr_cells + size_cells) {
    121			size = get_val(reg + addr_cells, size_cells);
    122			if (!size)
    123				continue;
    124
    125			if (addr_cells > 1 && fdt32_ld(reg)) {
    126				/* Outside 32-bit address space, skipping */
    127				continue;
    128			}
    129
    130			base = fdt32_ld(reg + addr_cells - 1);
    131			end = base + size;
    132			if (usable) {
    133				/*
    134				 * Clip to usable range, which takes precedence
    135				 * over mem_start
    136				 */
    137				if (base < usable_base)
    138					base = usable_base;
    139
    140				if (end > usable_end)
    141					end = usable_end;
    142
    143				if (end <= base)
    144					continue;
    145			} else if (mem_start >= base && mem_start < end) {
    146				/* Calculated address is valid, use it */
    147				return mem_start;
    148			}
    149
    150			if (base < fdt_mem_start)
    151				fdt_mem_start = base;
    152		}
    153	}
    154
    155	if (fdt_mem_start == 0xffffffff) {
    156		/* No usable memory found, falling back to default */
    157		return mem_start;
    158	}
    159
    160	/*
    161	 * The calculated address is not usable, or was overridden by the
    162	 * "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	 */
    166	return round_up(fdt_mem_start, SZ_2M);
    167}