blob: 748cd611a4a480e294498d40409ec502c10f2623 [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>
Julius Werner98eeb962019-12-11 15:47:42 -08005#include <cbfs.h>
Elyes HAOUAS351e3e52019-04-05 18:11:19 +02006#include <console/console.h>
Joel Kitching393c71c2019-06-16 16:09:42 +08007#include <ctype.h>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +02008#include <endian.h>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +02009#include <stdint.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020010#include <bootmem.h>
11#include <stdlib.h>
12#include <string.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020013#include <program_loading.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020014#include <memrange.h>
15#include <fit.h>
16#include <boardid.h>
Julius Werner9636a102019-05-03 17:36:43 -070017#include <commonlib/stdlib.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),
47 "%s,%s-rev%u-sku%u", CONFIG_MAINBOARD_VENDOR,
48 CONFIG_MAINBOARD_PART_NUMBER, 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",
55 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER,
56 board_id());
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020057
Patrick Rudolpha892cde2018-04-19 14:39:07 +020058 fit_add_compat_string(compat_string);
59 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020060
Julius Werner11217de2020-05-21 10:39:58 -070061 if (sku_id() != UNDEFINED_STRAPPING_ID) {
62 snprintf(compat_string, sizeof(compat_string), "%s,%s-sku%u",
63 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER,
64 sku_id());
65
66 fit_add_compat_string(compat_string);
67 }
68
Patrick Rudolpha892cde2018-04-19 14:39:07 +020069 snprintf(compat_string, sizeof(compat_string), "%s,%s",
70 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020071
Patrick Rudolpha892cde2018-04-19 14:39:07 +020072 fit_add_compat_string(compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020073}
74
Julius Wernerb379f192019-05-13 16:34:16 -070075static struct fit_image_node *find_image(const char *name)
76{
77 struct fit_image_node *image;
78 list_for_each(image, image_nodes, list_node) {
79 if (!strcmp(image->name, name))
80 return image;
81 }
82 printk(BIOS_ERR, "ERROR: Cannot find image node %s!\n", name);
83 return NULL;
84}
85
Julius Werner21b0b1a2019-05-16 16:12:04 -070086static struct fit_image_node *find_image_with_overlays(const char *name,
87 int bytes, struct list_node *prev)
88{
89 struct fit_image_node *base = find_image(name);
90 if (!base)
91 return NULL;
92
93 int len = strnlen(name, bytes) + 1;
94 bytes -= len;
95 name += len;
96 while (bytes > 0) {
97 struct fit_overlay_chain *next = xzalloc(sizeof(*next));
98 next->overlay = find_image(name);
99 if (!next->overlay)
100 return NULL;
101 list_insert_after(&next->list_node, prev);
102 prev = &next->list_node;
103 len = strnlen(name, bytes) + 1;
104 bytes -= len;
105 name += len;
106 }
107
108 return base;
109}
110
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200111static void image_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200112{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200113 struct fit_image_node *image = xzalloc(sizeof(*image));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200114
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200115 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200116 image->name = node->name;
117
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200118 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200119 list_for_each(prop, node->properties, list_node) {
120 if (!strcmp("data", prop->prop.name)) {
121 image->data = prop->prop.data;
122 image->size = prop->prop.size;
123 } else if (!strcmp("compression", prop->prop.name)) {
124 if (!strcmp("none", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200125 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200126 else if (!strcmp("lzma", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200127 image->compression = CBFS_COMPRESS_LZMA;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200128 else if (!strcmp("lz4", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200129 image->compression = CBFS_COMPRESS_LZ4;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200130 else
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200131 image->compression = -1;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200132 }
133 }
134
135 list_insert_after(&image->list_node, &image_nodes);
136}
137
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200138static void config_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200139{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200140 struct fit_config_node *config = xzalloc(sizeof(*config));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200141 config->name = node->name;
142
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200143 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200144 list_for_each(prop, node->properties, list_node) {
145 if (!strcmp("kernel", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700146 config->kernel = find_image(prop->prop.data);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200147 else if (!strcmp("fdt", prop->prop.name))
Julius Werner21b0b1a2019-05-16 16:12:04 -0700148 config->fdt = find_image_with_overlays(prop->prop.data,
149 prop->prop.size, &config->overlays);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200150 else if (!strcmp("ramdisk", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700151 config->ramdisk = find_image(prop->prop.data);
Julius Wernerfec42062019-05-16 16:04:19 -0700152 else if (!strcmp("compatible", prop->prop.name))
153 config->compat = prop->prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200154 }
155
156 list_insert_after(&config->list_node, &config_nodes);
157}
158
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200159static void fit_unpack(struct device_tree *tree, const char **default_config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200160{
Julius Wernerb379f192019-05-13 16:34:16 -0700161 struct device_tree_node *child;
162 struct device_tree_node *images = dt_find_node_by_path(tree, "/images",
163 NULL, NULL, 0);
164 if (images)
165 list_for_each(child, images->children, list_node)
166 image_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200167
Julius Wernerb379f192019-05-13 16:34:16 -0700168 struct device_tree_node *configs = dt_find_node_by_path(tree,
169 "/configurations", NULL, NULL, 0);
170 if (configs) {
171 *default_config = dt_find_string_prop(configs, "default");
172 list_for_each(child, configs->children, list_node)
173 config_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200174 }
175}
176
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200177static int fdt_find_compat(const void *blob, uint32_t start_offset,
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200178 struct fdt_property *prop)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200179{
180 int offset = start_offset;
181 int size;
182
183 size = fdt_node_name(blob, offset, NULL);
184 if (!size)
185 return -1;
186 offset += size;
187
188 while ((size = fdt_next_property(blob, offset, prop))) {
189 if (!strcmp("compatible", prop->name))
190 return 0;
191
192 offset += size;
193 }
194
195 prop->name = NULL;
196 return -1;
197}
198
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200199static int fit_check_compat(struct fdt_property *compat_prop,
200 const char *compat_name)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200201{
202 int bytes = compat_prop->size;
203 const char *compat_str = compat_prop->data;
204
205 for (int pos = 0; bytes && compat_str[0]; pos++) {
206 if (!strncmp(compat_str, compat_name, bytes))
207 return pos;
208 int len = strlen(compat_str) + 1;
209 compat_str += len;
210 bytes -= len;
211 }
212 return -1;
213}
214
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200215void fit_update_chosen(struct device_tree *tree, const char *cmd_line)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200216{
217 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200218 struct device_tree_node *node;
219 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200220
221 dt_add_string_prop(node, "bootargs", cmd_line);
222}
223
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200224void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr,
225 size_t ramdisk_size)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200226{
227 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200228 struct device_tree_node *node;
229 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200230
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200231 u64 start = (uintptr_t)ramdisk_addr;
232 u64 end = start + ramdisk_size;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200233
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200234 dt_add_u64_prop(node, "linux,initrd-start", start);
235 dt_add_u64_prop(node, "linux,initrd-end", end);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200236}
237
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200238static void update_reserve_map(uint64_t start, uint64_t end,
239 struct device_tree *tree)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200240{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200241 struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200242
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200243 entry->start = start;
244 entry->size = end - start;
245
246 list_insert_after(&entry->list_node, &tree->reserve_map);
247}
248
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200249struct entry_params {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600250 unsigned int addr_cells;
251 unsigned int size_cells;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200252 void *data;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200253};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200254
Martin Roth38ddbfb2019-10-23 21:41:00 -0600255static uint64_t max_range(unsigned int size_cells)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200256{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200257 /*
258 * Split up ranges who's sizes are too large to fit in #size-cells.
259 * The largest value we can store isn't a power of two, so we'll round
260 * down to make the math easier.
261 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200262 return 0x1ULL << (size_cells * 32 - 1);
263}
264
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200265static void update_mem_property(u64 start, u64 end, struct entry_params *params)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200266{
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200267 u8 *data = (u8 *)params->data;
268 u64 full_size = end - start;
269 while (full_size) {
270 const u64 max_size = max_range(params->size_cells);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200271 const u64 size = MIN(max_size, full_size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200272
273 dt_write_int(data, start, params->addr_cells * sizeof(u32));
274 data += params->addr_cells * sizeof(uint32_t);
275 start += size;
276
277 dt_write_int(data, size, params->size_cells * sizeof(u32));
278 data += params->size_cells * sizeof(uint32_t);
279 full_size -= size;
280 }
281 params->data = data;
282}
283
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200284struct mem_map {
285 struct memranges mem;
286 struct memranges reserved;
287};
288
289static bool walk_memory_table(const struct range_entry *r, void *arg)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200290{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200291 struct mem_map *arg_map = arg;
292
293 /*
294 * Kernel likes its available memory areas at least 1MB
295 * aligned, let's trim the regions such that unaligned padding
296 * is added to reserved memory.
297 */
298 if (range_entry_tag(r) == BM_MEM_RAM) {
299 uint64_t new_start = ALIGN_UP(range_entry_base(r), 1 * MiB);
300 uint64_t new_end = ALIGN_DOWN(range_entry_end(r), 1 * MiB);
301
302 if (new_start != range_entry_base(r))
303 memranges_insert(&arg_map->reserved,
304 range_entry_base(r),
305 new_start - range_entry_base(r),
306 BM_MEM_RESERVED);
307
308 if (new_start != new_end)
309 memranges_insert(&arg_map->mem, new_start,
310 new_end - new_start, BM_MEM_RAM);
311
312 if (new_end != range_entry_end(r))
313 memranges_insert(&arg_map->reserved, new_end,
314 range_entry_end(r) - new_end,
315 BM_MEM_RESERVED);
316 } else
317 memranges_insert(&arg_map->reserved, range_entry_base(r),
318 range_entry_size(r),
319 BM_MEM_RESERVED);
320
321 return true;
322}
323
324void fit_add_compat_string(const char *str)
325{
326 struct compat_string_entry *compat_node;
327
328 compat_node = xzalloc(sizeof(*compat_node));
329 compat_node->compat_string = strdup(str);
330
331 clean_compat_string((char *)compat_node->compat_string);
332
333 list_insert_after(&compat_node->list_node, &compat_strings);
334}
335
336void fit_update_memory(struct device_tree *tree)
337{
338 const struct range_entry *r;
339 struct device_tree_node *node;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200340 u32 addr_cells = 1, size_cells = 1;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200341 struct mem_map map;
342
343 printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n");
344
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200345 dt_read_cell_props(tree->root, &addr_cells, &size_cells);
346
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200347 /*
348 * First remove all existing device_type="memory" nodes, then add ours.
349 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200350 list_for_each(node, tree->root->children, list_node) {
351 const char *devtype = dt_find_string_prop(node, "device_type");
352 if (devtype && !strcmp(devtype, "memory"))
353 list_remove(&node->list_node);
354 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200355
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200356 node = xzalloc(sizeof(*node));
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200357
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200358 node->name = "memory";
359 list_insert_after(&node->list_node, &tree->root->children);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200360 dt_add_string_prop(node, "device_type", (char *)"memory");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200361
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200362 memranges_init_empty(&map.mem, NULL, 0);
363 memranges_init_empty(&map.reserved, NULL, 0);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200364
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200365 bootmem_walk_os_mem(walk_memory_table, &map);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200366
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200367 /* CBMEM regions are both carved out and explicitly reserved. */
368 memranges_each_entry(r, &map.reserved) {
369 update_reserve_map(range_entry_base(r), range_entry_end(r),
370 tree);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200371 }
372
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200373 /*
374 * Count the amount of 'reg' entries we need (account for size limits).
375 */
376 size_t count = 0;
377 memranges_each_entry(r, &map.mem) {
378 uint64_t size = range_entry_size(r);
379 uint64_t max_size = max_range(size_cells);
380 count += DIV_ROUND_UP(size, max_size);
381 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200382
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200383 /* Allocate the right amount of space and fill up the entries. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200384 size_t length = count * (addr_cells + size_cells) * sizeof(u32);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200385
386 void *data = xzalloc(length);
387
388 struct entry_params add_params = { addr_cells, size_cells, data };
389 memranges_each_entry(r, &map.mem) {
390 update_mem_property(range_entry_base(r), range_entry_end(r),
391 &add_params);
392 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200393 assert(add_params.data - data == length);
394
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200395 /* Assemble the final property and add it to the device tree. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200396 dt_add_bin_prop(node, "reg", data, length);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200397
398 memranges_teardown(&map.mem);
399 memranges_teardown(&map.reserved);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200400}
401
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200402/*
403 * Finds a compat string and updates the compat position and rank.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200404 * @param config The current config node to operate on
Julius Wernerb379f192019-05-13 16:34:16 -0700405 * @return 0 if compat updated, -1 if this FDT cannot be used.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200406 */
Julius Wernerb379f192019-05-13 16:34:16 -0700407static int fit_update_compat(struct fit_config_node *config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200408{
Julius Werner23df4772019-05-17 22:50:18 -0700409 /* If there was no "compatible" property in config node, this is a
410 legacy FIT image. Must extract compat prop from FDT itself. */
Julius Wernerfec42062019-05-16 16:04:19 -0700411 if (!config->compat.name) {
412 void *fdt_blob = config->fdt->data;
413 const struct fdt_header *fdt_header = fdt_blob;
414 uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset);
Julius Wernerb379f192019-05-13 16:34:16 -0700415
Julius Wernerfec42062019-05-16 16:04:19 -0700416 if (config->fdt->compression != CBFS_COMPRESS_NONE) {
417 printk(BIOS_ERR,
418 "ERROR: config %s has a compressed FDT without "
419 "external compatible property, skipping.\n",
420 config->name);
421 return -1;
422 }
423
Julius Werner23df4772019-05-17 22:50:18 -0700424 /* FDT overlays are not supported in legacy FIT images. */
Julius Werner21b0b1a2019-05-16 16:12:04 -0700425 if (config->overlays.next) {
426 printk(BIOS_ERR,
427 "ERROR: config %s has overlay but no compat!\n",
428 config->name);
429 return -1;
430 }
431
Julius Wernerfec42062019-05-16 16:04:19 -0700432 if (fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
433 printk(BIOS_ERR,
434 "ERROR: Can't find compat string in FDT %s "
435 "for config %s, skipping.\n",
436 config->fdt->name, config->name);
437 return -1;
438 }
439 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200440
Julius Wernerb379f192019-05-13 16:34:16 -0700441 config->compat_pos = -1;
442 config->compat_rank = -1;
Julius Wernerfec42062019-05-16 16:04:19 -0700443 size_t i = 0;
444 struct compat_string_entry *compat_node;
445 list_for_each(compat_node, compat_strings, list_node) {
446 int pos = fit_check_compat(&config->compat,
447 compat_node->compat_string);
448 if (pos >= 0) {
449 config->compat_pos = pos;
450 config->compat_rank = i;
451 config->compat_string =
452 compat_node->compat_string;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200453 }
Julius Wernerfec42062019-05-16 16:04:19 -0700454 i++;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200455 }
Julius Wernerb379f192019-05-13 16:34:16 -0700456
457 return 0;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200458}
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200459
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200460struct fit_config_node *fit_load(void *fit)
461{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200462 struct fit_image_node *image;
463 struct fit_config_node *config;
464 struct compat_string_entry *compat_node;
Julius Werner21b0b1a2019-05-16 16:12:04 -0700465 struct fit_overlay_chain *overlay_chain;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200466
467 printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit);
468
Julius Werner73eaec82019-05-03 17:58:07 -0700469 struct device_tree *tree = fdt_unflatten(fit);
470 if (!tree) {
471 printk(BIOS_ERR, "ERROR: Failed to unflatten FIT image!\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200472 return NULL;
473 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200474
475 const char *default_config_name = NULL;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200476 struct fit_config_node *default_config = NULL;
477 struct fit_config_node *compat_config = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200478
479 fit_unpack(tree, &default_config_name);
480
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200481 /* List the images we found. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200482 list_for_each(image, image_nodes, list_node)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200483 printk(BIOS_DEBUG, "FIT: Image %s has %d bytes.\n", image->name,
484 image->size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200485
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200486 fit_add_default_compat_strings();
487
488 printk(BIOS_DEBUG, "FIT: Compat preference "
489 "(lowest to highest priority) :");
490
491 list_for_each(compat_node, compat_strings, list_node) {
492 printk(BIOS_DEBUG, " %s", compat_node->compat_string);
493 }
494 printk(BIOS_DEBUG, "\n");
495 /* Process and list the configs. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200496 list_for_each(config, config_nodes, list_node) {
Julius Wernerb379f192019-05-13 16:34:16 -0700497 if (!config->kernel) {
498 printk(BIOS_ERR,
499 "ERROR: config %s has no kernel, skipping.\n",
500 config->name);
501 continue;
502 }
503 if (!config->fdt) {
504 printk(BIOS_ERR,
505 "ERROR: config %s has no FDT, skipping.\n",
506 config->name);
507 continue;
508 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200509
Julius Wernerb379f192019-05-13 16:34:16 -0700510 if (config->ramdisk &&
511 config->ramdisk->compression < 0) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200512 printk(BIOS_WARNING, "WARN: Ramdisk is compressed with "
513 "an unsupported algorithm, discarding config %s."
514 "\n", config->name);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200515 continue;
516 }
517
Julius Wernerb379f192019-05-13 16:34:16 -0700518 if (fit_update_compat(config))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200519 continue;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200520
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200521 printk(BIOS_DEBUG, "FIT: config %s", config->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200522 if (default_config_name &&
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200523 !strcmp(config->name, default_config_name)) {
524 printk(BIOS_DEBUG, " (default)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200525 default_config = config;
526 }
Julius Wernerb379f192019-05-13 16:34:16 -0700527 printk(BIOS_DEBUG, ", kernel %s", config->kernel->name);
528 printk(BIOS_DEBUG, ", fdt %s", config->fdt->name);
Julius Werner21b0b1a2019-05-16 16:12:04 -0700529 list_for_each(overlay_chain, config->overlays, list_node)
530 printk(BIOS_DEBUG, " %s", overlay_chain->overlay->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200531 if (config->ramdisk)
Julius Wernerb379f192019-05-13 16:34:16 -0700532 printk(BIOS_DEBUG, ", ramdisk %s",
533 config->ramdisk->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200534 if (config->compat.name) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200535 printk(BIOS_DEBUG, ", compat");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200536 int bytes = config->compat.size;
537 const char *compat_str = config->compat.data;
538 for (int pos = 0; bytes && compat_str[0]; pos++) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200539 printk(BIOS_DEBUG, " %s", compat_str);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200540 if (pos == config->compat_pos)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200541 printk(BIOS_DEBUG, " (match)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200542 int len = strlen(compat_str) + 1;
543 compat_str += len;
544 bytes -= len;
545 }
546
547 if (config->compat_rank >= 0 && (!compat_config ||
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200548 config->compat_rank > compat_config->compat_rank))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200549 compat_config = config;
550 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200551 printk(BIOS_DEBUG, "\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200552 }
553
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200554 struct fit_config_node *to_boot = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200555 if (compat_config) {
556 to_boot = compat_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200557 printk(BIOS_INFO, "FIT: Choosing best match %s for compat "
558 "%s.\n", to_boot->name, to_boot->compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200559 } else if (default_config) {
560 to_boot = default_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200561 printk(BIOS_INFO, "FIT: No match, choosing default %s.\n",
562 to_boot->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200563 } else {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200564 printk(BIOS_ERR, "FIT: No compatible or default configs. "
565 "Giving up.\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200566 return NULL;
567 }
568
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200569 return to_boot;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200570}