Linux Audio

Check our new training course

Loading...
v6.13.7
  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#include "misc.h"
  7
  8static const void *get_prop(const void *fdt, const char *node_path,
  9			    const char *property, int minlen)
 10{
 11	const void *prop;
 12	int offset, len;
 13
 14	offset = fdt_path_offset(fdt, node_path);
 15	if (offset < 0)
 16		return NULL;
 17
 18	prop = fdt_getprop(fdt, offset, property, &len);
 19	if (!prop || len < minlen)
 20		return NULL;
 21
 22	return prop;
 23}
 24
 25static uint32_t get_cells(const void *fdt, const char *name)
 26{
 27	const fdt32_t *prop = get_prop(fdt, "/", name, sizeof(fdt32_t));
 28
 29	if (!prop) {
 30		/* default */
 31		return 1;
 32	}
 33
 34	return fdt32_ld(prop);
 35}
 36
 37static uint64_t get_val(const fdt32_t *cells, uint32_t ncells)
 38{
 39	uint64_t r;
 40
 41	r = fdt32_ld(cells);
 42	if (ncells > 1)
 43		r = (r << 32) | fdt32_ld(cells + 1);
 44
 45	return r;
 46}
 47
 48/*
 49 * Check the start of physical memory
 50 *
 51 * Traditionally, the start address of physical memory is obtained by masking
 52 * the program counter.  However, this does require that this address is a
 53 * multiple of 128 MiB, precluding booting Linux on platforms where this
 54 * requirement is not fulfilled.
 55 * Hence validate the calculated address against the memory information in the
 56 * DTB, and, if out-of-range, replace it by the real start address.
 57 * To preserve backwards compatibility (systems reserving a block of memory
 58 * at the start of physical memory, kdump, ...), the traditional method is
 59 * used if it yields a valid address, unless the "linux,usable-memory-range"
 60 * property is present.
 61 *
 62 * Return value: start address of physical memory to use
 63 */
 64uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt)
 65{
 66	uint32_t addr_cells, size_cells, usable_base, base;
 67	uint32_t fdt_mem_start = 0xffffffff;
 68	const fdt32_t *usable, *reg, *endp;
 69	uint64_t size, usable_end, end;
 70	const char *type;
 71	int offset, len;
 72
 73	if (!fdt)
 74		return mem_start;
 75
 76	if (fdt_magic(fdt) != FDT_MAGIC)
 77		return mem_start;
 78
 79	/* There may be multiple cells on LPAE platforms */
 80	addr_cells = get_cells(fdt, "#address-cells");
 81	size_cells = get_cells(fdt, "#size-cells");
 82	if (addr_cells > 2 || size_cells > 2)
 83		return mem_start;
 84
 85	/*
 86	 * Usable memory in case of a crash dump kernel
 87	 * This property describes a limitation: memory within this range is
 88	 * only valid when also described through another mechanism
 89	 */
 90	usable = get_prop(fdt, "/chosen", "linux,usable-memory-range",
 91			  (addr_cells + size_cells) * sizeof(fdt32_t));
 92	if (usable) {
 93		size = get_val(usable + addr_cells, size_cells);
 94		if (!size)
 95			return mem_start;
 96
 97		if (addr_cells > 1 && fdt32_ld(usable)) {
 98			/* Outside 32-bit address space */
 99			return mem_start;
100		}
101
102		usable_base = fdt32_ld(usable + addr_cells - 1);
103		usable_end = usable_base + size;
104	}
105
106	/* Walk all memory nodes and regions */
107	for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0;
108	     offset = fdt_next_node(fdt, offset, NULL)) {
109		type = fdt_getprop(fdt, offset, "device_type", NULL);
110		if (!type || strcmp(type, "memory"))
111			continue;
112
113		reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len);
114		if (!reg)
115			reg = fdt_getprop(fdt, offset, "reg", &len);
116		if (!reg)
117			continue;
118
119		for (endp = reg + (len / sizeof(fdt32_t));
120		     endp - reg >= addr_cells + size_cells;
121		     reg += addr_cells + size_cells) {
122			size = get_val(reg + addr_cells, size_cells);
123			if (!size)
124				continue;
125
126			if (addr_cells > 1 && fdt32_ld(reg)) {
127				/* Outside 32-bit address space, skipping */
128				continue;
129			}
130
131			base = fdt32_ld(reg + addr_cells - 1);
132			end = base + size;
133			if (usable) {
134				/*
135				 * Clip to usable range, which takes precedence
136				 * over mem_start
137				 */
138				if (base < usable_base)
139					base = usable_base;
140
141				if (end > usable_end)
142					end = usable_end;
143
144				if (end <= base)
145					continue;
146			} else if (mem_start >= base && mem_start < end) {
147				/* Calculated address is valid, use it */
148				return mem_start;
149			}
150
151			if (base < fdt_mem_start)
152				fdt_mem_start = base;
153		}
154	}
155
156	if (fdt_mem_start == 0xffffffff) {
157		/* No usable memory found, falling back to default */
158		return mem_start;
159	}
160
161	/*
162	 * The calculated address is not usable, or was overridden by the
163	 * "linux,usable-memory-range" property.
164	 * Use the lowest usable physical memory address from the DTB instead,
165	 * and make sure this is a multiple of 2 MiB for phys/virt patching.
166	 */
167	return round_up(fdt_mem_start, SZ_2M);
168}
v5.14.15
  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}