0001
0002
0003 #include <linux/kernel.h>
0004 #include <linux/libfdt.h>
0005 #include <linux/sizes.h>
0006
0007 static const void *get_prop(const void *fdt, const char *node_path,
0008 const char *property, int minlen)
0009 {
0010 const void *prop;
0011 int offset, len;
0012
0013 offset = fdt_path_offset(fdt, node_path);
0014 if (offset < 0)
0015 return NULL;
0016
0017 prop = fdt_getprop(fdt, offset, property, &len);
0018 if (!prop || len < minlen)
0019 return NULL;
0020
0021 return prop;
0022 }
0023
0024 static uint32_t get_cells(const void *fdt, const char *name)
0025 {
0026 const fdt32_t *prop = get_prop(fdt, "/", name, sizeof(fdt32_t));
0027
0028 if (!prop) {
0029
0030 return 1;
0031 }
0032
0033 return fdt32_ld(prop);
0034 }
0035
0036 static uint64_t get_val(const fdt32_t *cells, uint32_t ncells)
0037 {
0038 uint64_t r;
0039
0040 r = fdt32_ld(cells);
0041 if (ncells > 1)
0042 r = (r << 32) | fdt32_ld(cells + 1);
0043
0044 return r;
0045 }
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt)
0064 {
0065 uint32_t addr_cells, size_cells, usable_base, base;
0066 uint32_t fdt_mem_start = 0xffffffff;
0067 const fdt32_t *usable, *reg, *endp;
0068 uint64_t size, usable_end, end;
0069 const char *type;
0070 int offset, len;
0071
0072 if (!fdt)
0073 return mem_start;
0074
0075 if (fdt_magic(fdt) != FDT_MAGIC)
0076 return mem_start;
0077
0078
0079 addr_cells = get_cells(fdt, "#address-cells");
0080 size_cells = get_cells(fdt, "#size-cells");
0081 if (addr_cells > 2 || size_cells > 2)
0082 return mem_start;
0083
0084
0085
0086
0087
0088
0089 usable = get_prop(fdt, "/chosen", "linux,usable-memory-range",
0090 (addr_cells + size_cells) * sizeof(fdt32_t));
0091 if (usable) {
0092 size = get_val(usable + addr_cells, size_cells);
0093 if (!size)
0094 return mem_start;
0095
0096 if (addr_cells > 1 && fdt32_ld(usable)) {
0097
0098 return mem_start;
0099 }
0100
0101 usable_base = fdt32_ld(usable + addr_cells - 1);
0102 usable_end = usable_base + size;
0103 }
0104
0105
0106 for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0;
0107 offset = fdt_next_node(fdt, offset, NULL)) {
0108 type = fdt_getprop(fdt, offset, "device_type", NULL);
0109 if (!type || strcmp(type, "memory"))
0110 continue;
0111
0112 reg = fdt_getprop(fdt, offset, "linux,usable-memory", &len);
0113 if (!reg)
0114 reg = fdt_getprop(fdt, offset, "reg", &len);
0115 if (!reg)
0116 continue;
0117
0118 for (endp = reg + (len / sizeof(fdt32_t));
0119 endp - reg >= addr_cells + size_cells;
0120 reg += addr_cells + size_cells) {
0121 size = get_val(reg + addr_cells, size_cells);
0122 if (!size)
0123 continue;
0124
0125 if (addr_cells > 1 && fdt32_ld(reg)) {
0126
0127 continue;
0128 }
0129
0130 base = fdt32_ld(reg + addr_cells - 1);
0131 end = base + size;
0132 if (usable) {
0133
0134
0135
0136
0137 if (base < usable_base)
0138 base = usable_base;
0139
0140 if (end > usable_end)
0141 end = usable_end;
0142
0143 if (end <= base)
0144 continue;
0145 } else if (mem_start >= base && mem_start < end) {
0146
0147 return mem_start;
0148 }
0149
0150 if (base < fdt_mem_start)
0151 fdt_mem_start = base;
0152 }
0153 }
0154
0155 if (fdt_mem_start == 0xffffffff) {
0156
0157 return mem_start;
0158 }
0159
0160
0161
0162
0163
0164
0165
0166 return round_up(fdt_mem_start, SZ_2M);
0167 }