blob: 7f0fe6773af5f86a7f3a76fe0187a9184af74bb0 [file] [log] [blame]
Patrick Georgiafd4c872020-05-05 23:43:18 +02001/* Taken from depthcharge: src/boot/fit.c */
Patrick Georgiac959032020-05-05 22:49:26 +02002/* SPDX-License-Identifier: GPL-2.0-or-later */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +02003
4#include <assert.h>
Elyes HAOUAS351e3e52019-04-05 18:11:19 +02005#include <console/console.h>
Joel Kitching393c71c2019-06-16 16:09:42 +08006#include <ctype.h>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +02007#include <endian.h>
Kyösti Mälkkib78e4622022-12-15 22:12:29 +02008#include <identity.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +02009#include <bootmem.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020010#include <string.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020011#include <program_loading.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020012#include <memrange.h>
13#include <fit.h>
14#include <boardid.h>
Elyes Haouasbdd03c22024-05-27 11:20:07 +020015#include <stdio.h>
Maximilian Bruneb3e336c2023-09-16 19:49:39 +020016#include <stdlib.h>
Elyes HAOUAS93a195c2021-12-31 18:46:13 +010017#include <types.h>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020018
Patrick Rudolpha892cde2018-04-19 14:39:07 +020019static struct list_node image_nodes;
20static struct list_node config_nodes;
21static struct list_node compat_strings;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020022
Patrick Rudolpha892cde2018-04-19 14:39:07 +020023struct compat_string_entry {
24 const char *compat_string;
25 struct list_node list_node;
26};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020027
Jonathan Neuschäferbbaae952018-12-12 01:12:13 +010028/* Convert string to lowercase and replace '_' and spaces with '-'. */
Patrick Rudolpha892cde2018-04-19 14:39:07 +020029static char *clean_compat_string(char *str)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020030{
Patrick Rudolpha892cde2018-04-19 14:39:07 +020031 for (size_t i = 0; i < strlen(str); i++) {
32 str[i] = tolower(str[i]);
Jonathan Neuschäferbbaae952018-12-12 01:12:13 +010033 if (str[i] == '_' || str[i] == ' ')
Patrick Rudolpha892cde2018-04-19 14:39:07 +020034 str[i] = '-';
35 }
36
37 return str;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020038}
39
Patrick Rudolpha892cde2018-04-19 14:39:07 +020040static void fit_add_default_compat_strings(void)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020041{
Patrick Rudolpha892cde2018-04-19 14:39:07 +020042 char compat_string[80] = {};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020043
Patrick Rudolpha892cde2018-04-19 14:39:07 +020044 if ((board_id() != UNDEFINED_STRAPPING_ID) &&
45 (sku_id() != UNDEFINED_STRAPPING_ID)) {
46 snprintf(compat_string, sizeof(compat_string),
Kyösti Mälkkib78e4622022-12-15 22:12:29 +020047 "%s,%s-rev%u-sku%u", mainboard_vendor, mainboard_part_number,
48 board_id(), sku_id());
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020049
Patrick Rudolpha892cde2018-04-19 14:39:07 +020050 fit_add_compat_string(compat_string);
51 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020052
Patrick Rudolpha892cde2018-04-19 14:39:07 +020053 if (board_id() != UNDEFINED_STRAPPING_ID) {
54 snprintf(compat_string, sizeof(compat_string), "%s,%s-rev%u",
Kyösti Mälkkib78e4622022-12-15 22:12:29 +020055 mainboard_vendor, mainboard_part_number, board_id());
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020056
Patrick Rudolpha892cde2018-04-19 14:39:07 +020057 fit_add_compat_string(compat_string);
58 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020059
Julius Werner11217de2020-05-21 10:39:58 -070060 if (sku_id() != UNDEFINED_STRAPPING_ID) {
61 snprintf(compat_string, sizeof(compat_string), "%s,%s-sku%u",
Kyösti Mälkkib78e4622022-12-15 22:12:29 +020062 mainboard_vendor, mainboard_part_number, sku_id());
Julius Werner11217de2020-05-21 10:39:58 -070063
64 fit_add_compat_string(compat_string);
65 }
66
Patrick Rudolpha892cde2018-04-19 14:39:07 +020067 snprintf(compat_string, sizeof(compat_string), "%s,%s",
Kyösti Mälkkib78e4622022-12-15 22:12:29 +020068 mainboard_vendor, mainboard_part_number);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020069
Patrick Rudolpha892cde2018-04-19 14:39:07 +020070 fit_add_compat_string(compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020071}
72
Julius Wernerb379f192019-05-13 16:34:16 -070073static struct fit_image_node *find_image(const char *name)
74{
75 struct fit_image_node *image;
76 list_for_each(image, image_nodes, list_node) {
77 if (!strcmp(image->name, name))
78 return image;
79 }
Julius Wernere9665952022-01-21 17:06:20 -080080 printk(BIOS_ERR, "Cannot find image node %s!\n", name);
Julius Wernerb379f192019-05-13 16:34:16 -070081 return NULL;
82}
83
Julius Werner21b0b1a2019-05-16 16:12:04 -070084static struct fit_image_node *find_image_with_overlays(const char *name,
85 int bytes, struct list_node *prev)
86{
87 struct fit_image_node *base = find_image(name);
88 if (!base)
89 return NULL;
90
91 int len = strnlen(name, bytes) + 1;
92 bytes -= len;
93 name += len;
94 while (bytes > 0) {
95 struct fit_overlay_chain *next = xzalloc(sizeof(*next));
96 next->overlay = find_image(name);
97 if (!next->overlay)
98 return NULL;
99 list_insert_after(&next->list_node, prev);
100 prev = &next->list_node;
101 len = strnlen(name, bytes) + 1;
102 bytes -= len;
103 name += len;
104 }
105
106 return base;
107}
108
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200109static void image_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200110{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200111 struct fit_image_node *image = xzalloc(sizeof(*image));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200112
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200113 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200114 image->name = node->name;
115
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200116 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200117 list_for_each(prop, node->properties, list_node) {
118 if (!strcmp("data", prop->prop.name)) {
119 image->data = prop->prop.data;
120 image->size = prop->prop.size;
121 } else if (!strcmp("compression", prop->prop.name)) {
122 if (!strcmp("none", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200123 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200124 else if (!strcmp("lzma", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200125 image->compression = CBFS_COMPRESS_LZMA;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200126 else if (!strcmp("lz4", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200127 image->compression = CBFS_COMPRESS_LZ4;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200128 else
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200129 image->compression = -1;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200130 }
131 }
132
133 list_insert_after(&image->list_node, &image_nodes);
134}
135
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200136static void config_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200137{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200138 struct fit_config_node *config = xzalloc(sizeof(*config));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200139 config->name = node->name;
140
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200141 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200142 list_for_each(prop, node->properties, list_node) {
143 if (!strcmp("kernel", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700144 config->kernel = find_image(prop->prop.data);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200145 else if (!strcmp("fdt", prop->prop.name))
Julius Werner21b0b1a2019-05-16 16:12:04 -0700146 config->fdt = find_image_with_overlays(prop->prop.data,
147 prop->prop.size, &config->overlays);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200148 else if (!strcmp("ramdisk", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700149 config->ramdisk = find_image(prop->prop.data);
Julius Wernerfec42062019-05-16 16:04:19 -0700150 else if (!strcmp("compatible", prop->prop.name))
151 config->compat = prop->prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200152 }
153
154 list_insert_after(&config->list_node, &config_nodes);
155}
156
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200157static void fit_unpack(struct device_tree *tree, const char **default_config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200158{
Julius Wernerb379f192019-05-13 16:34:16 -0700159 struct device_tree_node *child;
160 struct device_tree_node *images = dt_find_node_by_path(tree, "/images",
161 NULL, NULL, 0);
162 if (images)
163 list_for_each(child, images->children, list_node)
164 image_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200165
Julius Wernerb379f192019-05-13 16:34:16 -0700166 struct device_tree_node *configs = dt_find_node_by_path(tree,
167 "/configurations", NULL, NULL, 0);
168 if (configs) {
169 *default_config = dt_find_string_prop(configs, "default");
170 list_for_each(child, configs->children, list_node)
171 config_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200172 }
173}
174
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200175static int fdt_find_compat(const void *blob, uint32_t start_offset,
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200176 struct fdt_property *prop)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200177{
178 int offset = start_offset;
179 int size;
180
181 size = fdt_node_name(blob, offset, NULL);
182 if (!size)
183 return -1;
184 offset += size;
185
186 while ((size = fdt_next_property(blob, offset, prop))) {
187 if (!strcmp("compatible", prop->name))
188 return 0;
189
190 offset += size;
191 }
192
193 prop->name = NULL;
194 return -1;
195}
196
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200197static int fit_check_compat(struct fdt_property *compat_prop,
198 const char *compat_name)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200199{
200 int bytes = compat_prop->size;
201 const char *compat_str = compat_prop->data;
202
203 for (int pos = 0; bytes && compat_str[0]; pos++) {
204 if (!strncmp(compat_str, compat_name, bytes))
205 return pos;
206 int len = strlen(compat_str) + 1;
207 compat_str += len;
208 bytes -= len;
209 }
210 return -1;
211}
212
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200213void fit_update_chosen(struct device_tree *tree, const char *cmd_line)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200214{
215 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200216 struct device_tree_node *node;
217 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200218
219 dt_add_string_prop(node, "bootargs", cmd_line);
220}
221
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200222void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr,
223 size_t ramdisk_size)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200224{
225 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200226 struct device_tree_node *node;
227 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200228
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200229 u64 start = (uintptr_t)ramdisk_addr;
230 u64 end = start + ramdisk_size;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200231
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200232 dt_add_u64_prop(node, "linux,initrd-start", start);
233 dt_add_u64_prop(node, "linux,initrd-end", end);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200234}
235
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200236static void update_reserve_map(uint64_t start, uint64_t end,
237 struct device_tree *tree)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200238{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200239 struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200240
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200241 entry->start = start;
242 entry->size = end - start;
243
244 list_insert_after(&entry->list_node, &tree->reserve_map);
245}
246
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200247struct entry_params {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600248 unsigned int addr_cells;
249 unsigned int size_cells;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200250 void *data;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200251};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200252
Martin Roth38ddbfb2019-10-23 21:41:00 -0600253static uint64_t max_range(unsigned int size_cells)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200254{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200255 /*
256 * Split up ranges who's sizes are too large to fit in #size-cells.
257 * The largest value we can store isn't a power of two, so we'll round
258 * down to make the math easier.
259 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200260 return 0x1ULL << (size_cells * 32 - 1);
261}
262
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200263static void update_mem_property(u64 start, u64 end, struct entry_params *params)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200264{
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200265 u8 *data = (u8 *)params->data;
266 u64 full_size = end - start;
267 while (full_size) {
268 const u64 max_size = max_range(params->size_cells);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200269 const u64 size = MIN(max_size, full_size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200270
271 dt_write_int(data, start, params->addr_cells * sizeof(u32));
272 data += params->addr_cells * sizeof(uint32_t);
273 start += size;
274
275 dt_write_int(data, size, params->size_cells * sizeof(u32));
276 data += params->size_cells * sizeof(uint32_t);
277 full_size -= size;
278 }
279 params->data = data;
280}
281
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200282struct mem_map {
283 struct memranges mem;
284 struct memranges reserved;
285};
286
287static bool walk_memory_table(const struct range_entry *r, void *arg)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200288{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200289 struct mem_map *arg_map = arg;
Arthur Heymans1793eb42022-07-01 15:45:56 +0200290 struct memranges *ranges;
291 enum bootmem_type tag;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200292
Arthur Heymans1793eb42022-07-01 15:45:56 +0200293 ranges = range_entry_tag(r) == BM_MEM_RAM ? &arg_map->mem : &arg_map->reserved;
294 tag = range_entry_tag(r) == BM_MEM_RAM ? BM_MEM_RAM : BM_MEM_RESERVED;
295 memranges_insert(ranges, range_entry_base(r), range_entry_size(r), tag);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200296 return true;
297}
298
299void fit_add_compat_string(const char *str)
300{
301 struct compat_string_entry *compat_node;
302
303 compat_node = xzalloc(sizeof(*compat_node));
304 compat_node->compat_string = strdup(str);
305
306 clean_compat_string((char *)compat_node->compat_string);
307
308 list_insert_after(&compat_node->list_node, &compat_strings);
309}
310
311void fit_update_memory(struct device_tree *tree)
312{
313 const struct range_entry *r;
314 struct device_tree_node *node;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200315 u32 addr_cells = 1, size_cells = 1;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200316 struct mem_map map;
317
318 printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n");
319
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200320 dt_read_cell_props(tree->root, &addr_cells, &size_cells);
321
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200322 /*
323 * First remove all existing device_type="memory" nodes, then add ours.
324 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200325 list_for_each(node, tree->root->children, list_node) {
326 const char *devtype = dt_find_string_prop(node, "device_type");
327 if (devtype && !strcmp(devtype, "memory"))
328 list_remove(&node->list_node);
329 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200330
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200331 node = xzalloc(sizeof(*node));
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200332
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200333 node->name = "memory";
334 list_insert_after(&node->list_node, &tree->root->children);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200335 dt_add_string_prop(node, "device_type", (char *)"memory");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200336
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200337 memranges_init_empty(&map.mem, NULL, 0);
338 memranges_init_empty(&map.reserved, NULL, 0);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200339
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200340 bootmem_walk_os_mem(walk_memory_table, &map);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200341
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200342 /* CBMEM regions are both carved out and explicitly reserved. */
343 memranges_each_entry(r, &map.reserved) {
344 update_reserve_map(range_entry_base(r), range_entry_end(r),
345 tree);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200346 }
347
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200348 /*
349 * Count the amount of 'reg' entries we need (account for size limits).
350 */
351 size_t count = 0;
352 memranges_each_entry(r, &map.mem) {
353 uint64_t size = range_entry_size(r);
354 uint64_t max_size = max_range(size_cells);
355 count += DIV_ROUND_UP(size, max_size);
356 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200357
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200358 /* Allocate the right amount of space and fill up the entries. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200359 size_t length = count * (addr_cells + size_cells) * sizeof(u32);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200360
361 void *data = xzalloc(length);
362
363 struct entry_params add_params = { addr_cells, size_cells, data };
364 memranges_each_entry(r, &map.mem) {
365 update_mem_property(range_entry_base(r), range_entry_end(r),
366 &add_params);
367 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200368 assert(add_params.data - data == length);
369
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200370 /* Assemble the final property and add it to the device tree. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200371 dt_add_bin_prop(node, "reg", data, length);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200372
373 memranges_teardown(&map.mem);
374 memranges_teardown(&map.reserved);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200375}
376
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200377/*
378 * Finds a compat string and updates the compat position and rank.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200379 * @param config The current config node to operate on
Julius Wernerb379f192019-05-13 16:34:16 -0700380 * @return 0 if compat updated, -1 if this FDT cannot be used.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200381 */
Julius Wernerb379f192019-05-13 16:34:16 -0700382static int fit_update_compat(struct fit_config_node *config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200383{
Julius Werner23df4772019-05-17 22:50:18 -0700384 /* If there was no "compatible" property in config node, this is a
385 legacy FIT image. Must extract compat prop from FDT itself. */
Julius Wernerfec42062019-05-16 16:04:19 -0700386 if (!config->compat.name) {
387 void *fdt_blob = config->fdt->data;
388 const struct fdt_header *fdt_header = fdt_blob;
389 uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset);
Julius Wernerb379f192019-05-13 16:34:16 -0700390
Julius Wernerfec42062019-05-16 16:04:19 -0700391 if (config->fdt->compression != CBFS_COMPRESS_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800392 printk(BIOS_ERR, "config %s has a compressed FDT without "
Julius Wernerfec42062019-05-16 16:04:19 -0700393 "external compatible property, skipping.\n",
394 config->name);
395 return -1;
396 }
397
Julius Werner23df4772019-05-17 22:50:18 -0700398 /* FDT overlays are not supported in legacy FIT images. */
Julius Werner21b0b1a2019-05-16 16:12:04 -0700399 if (config->overlays.next) {
Julius Wernere9665952022-01-21 17:06:20 -0800400 printk(BIOS_ERR, "config %s has overlay but no compat!\n",
Julius Werner21b0b1a2019-05-16 16:12:04 -0700401 config->name);
402 return -1;
403 }
404
Julius Wernerfec42062019-05-16 16:04:19 -0700405 if (fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
Julius Wernere9665952022-01-21 17:06:20 -0800406 printk(BIOS_ERR, "Can't find compat string in FDT %s "
Julius Wernerfec42062019-05-16 16:04:19 -0700407 "for config %s, skipping.\n",
408 config->fdt->name, config->name);
409 return -1;
410 }
411 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200412
Julius Wernerb379f192019-05-13 16:34:16 -0700413 config->compat_pos = -1;
414 config->compat_rank = -1;
Julius Wernerfec42062019-05-16 16:04:19 -0700415 size_t i = 0;
416 struct compat_string_entry *compat_node;
417 list_for_each(compat_node, compat_strings, list_node) {
418 int pos = fit_check_compat(&config->compat,
419 compat_node->compat_string);
420 if (pos >= 0) {
421 config->compat_pos = pos;
422 config->compat_rank = i;
423 config->compat_string =
424 compat_node->compat_string;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200425 }
Julius Wernerfec42062019-05-16 16:04:19 -0700426 i++;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200427 }
Julius Wernerb379f192019-05-13 16:34:16 -0700428
429 return 0;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200430}
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200431
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200432struct fit_config_node *fit_load(void *fit)
433{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200434 struct fit_image_node *image;
435 struct fit_config_node *config;
436 struct compat_string_entry *compat_node;
Julius Werner21b0b1a2019-05-16 16:12:04 -0700437 struct fit_overlay_chain *overlay_chain;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200438
439 printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit);
440
Julius Werner73eaec82019-05-03 17:58:07 -0700441 struct device_tree *tree = fdt_unflatten(fit);
442 if (!tree) {
Julius Wernere9665952022-01-21 17:06:20 -0800443 printk(BIOS_ERR, "Failed to unflatten FIT image!\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200444 return NULL;
445 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200446
447 const char *default_config_name = NULL;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200448 struct fit_config_node *default_config = NULL;
449 struct fit_config_node *compat_config = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200450
451 fit_unpack(tree, &default_config_name);
452
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200453 /* List the images we found. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200454 list_for_each(image, image_nodes, list_node)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200455 printk(BIOS_DEBUG, "FIT: Image %s has %d bytes.\n", image->name,
456 image->size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200457
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200458 fit_add_default_compat_strings();
459
460 printk(BIOS_DEBUG, "FIT: Compat preference "
461 "(lowest to highest priority) :");
462
463 list_for_each(compat_node, compat_strings, list_node) {
464 printk(BIOS_DEBUG, " %s", compat_node->compat_string);
465 }
466 printk(BIOS_DEBUG, "\n");
467 /* Process and list the configs. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200468 list_for_each(config, config_nodes, list_node) {
Julius Wernerb379f192019-05-13 16:34:16 -0700469 if (!config->kernel) {
Julius Wernere9665952022-01-21 17:06:20 -0800470 printk(BIOS_ERR, "config %s has no kernel, skipping.\n",
Julius Wernerb379f192019-05-13 16:34:16 -0700471 config->name);
472 continue;
473 }
474 if (!config->fdt) {
Julius Wernere9665952022-01-21 17:06:20 -0800475 printk(BIOS_ERR, "config %s has no FDT, skipping.\n",
Julius Wernerb379f192019-05-13 16:34:16 -0700476 config->name);
477 continue;
478 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200479
Julius Wernerb379f192019-05-13 16:34:16 -0700480 if (config->ramdisk &&
481 config->ramdisk->compression < 0) {
Julius Wernere9665952022-01-21 17:06:20 -0800482 printk(BIOS_WARNING, "Ramdisk is compressed with "
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200483 "an unsupported algorithm, discarding config %s."
484 "\n", config->name);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200485 continue;
486 }
487
Julius Wernerb379f192019-05-13 16:34:16 -0700488 if (fit_update_compat(config))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200489 continue;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200490
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200491 printk(BIOS_DEBUG, "FIT: config %s", config->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200492 if (default_config_name &&
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200493 !strcmp(config->name, default_config_name)) {
494 printk(BIOS_DEBUG, " (default)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200495 default_config = config;
496 }
Julius Wernerb379f192019-05-13 16:34:16 -0700497 printk(BIOS_DEBUG, ", kernel %s", config->kernel->name);
498 printk(BIOS_DEBUG, ", fdt %s", config->fdt->name);
Julius Werner21b0b1a2019-05-16 16:12:04 -0700499 list_for_each(overlay_chain, config->overlays, list_node)
500 printk(BIOS_DEBUG, " %s", overlay_chain->overlay->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200501 if (config->ramdisk)
Julius Wernerb379f192019-05-13 16:34:16 -0700502 printk(BIOS_DEBUG, ", ramdisk %s",
503 config->ramdisk->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200504 if (config->compat.name) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200505 printk(BIOS_DEBUG, ", compat");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200506 int bytes = config->compat.size;
507 const char *compat_str = config->compat.data;
508 for (int pos = 0; bytes && compat_str[0]; pos++) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200509 printk(BIOS_DEBUG, " %s", compat_str);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200510 if (pos == config->compat_pos)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200511 printk(BIOS_DEBUG, " (match)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200512 int len = strlen(compat_str) + 1;
513 compat_str += len;
514 bytes -= len;
515 }
516
517 if (config->compat_rank >= 0 && (!compat_config ||
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200518 config->compat_rank > compat_config->compat_rank))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200519 compat_config = config;
520 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200521 printk(BIOS_DEBUG, "\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200522 }
523
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200524 struct fit_config_node *to_boot = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200525 if (compat_config) {
526 to_boot = compat_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200527 printk(BIOS_INFO, "FIT: Choosing best match %s for compat "
528 "%s.\n", to_boot->name, to_boot->compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200529 } else if (default_config) {
530 to_boot = default_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200531 printk(BIOS_INFO, "FIT: No match, choosing default %s.\n",
532 to_boot->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200533 } else {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200534 printk(BIOS_ERR, "FIT: No compatible or default configs. "
535 "Giving up.\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200536 return NULL;
537 }
538
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200539 return to_boot;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200540}