blob: 08fd0455b39d7925806668b9c6ad1ef74e7d5882 [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>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +02008#include <stdint.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +02009#include <bootmem.h>
10#include <stdlib.h>
11#include <string.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020012#include <program_loading.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +020013#include <memrange.h>
14#include <fit.h>
15#include <boardid.h>
Julius Werner9636a102019-05-03 17:36:43 -070016#include <commonlib/stdlib.h>
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020017
Patrick Rudolpha892cde2018-04-19 14:39:07 +020018static struct list_node image_nodes;
19static struct list_node config_nodes;
20static struct list_node compat_strings;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020021
Patrick Rudolpha892cde2018-04-19 14:39:07 +020022struct compat_string_entry {
23 const char *compat_string;
24 struct list_node list_node;
25};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020026
Jonathan Neuschäferbbaae952018-12-12 01:12:13 +010027/* Convert string to lowercase and replace '_' and spaces with '-'. */
Patrick Rudolpha892cde2018-04-19 14:39:07 +020028static char *clean_compat_string(char *str)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020029{
Patrick Rudolpha892cde2018-04-19 14:39:07 +020030 for (size_t i = 0; i < strlen(str); i++) {
31 str[i] = tolower(str[i]);
Jonathan Neuschäferbbaae952018-12-12 01:12:13 +010032 if (str[i] == '_' || str[i] == ' ')
Patrick Rudolpha892cde2018-04-19 14:39:07 +020033 str[i] = '-';
34 }
35
36 return str;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020037}
38
Patrick Rudolpha892cde2018-04-19 14:39:07 +020039static void fit_add_default_compat_strings(void)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020040{
Patrick Rudolpha892cde2018-04-19 14:39:07 +020041 char compat_string[80] = {};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020042
Patrick Rudolpha892cde2018-04-19 14:39:07 +020043 if ((board_id() != UNDEFINED_STRAPPING_ID) &&
44 (sku_id() != UNDEFINED_STRAPPING_ID)) {
45 snprintf(compat_string, sizeof(compat_string),
46 "%s,%s-rev%u-sku%u", CONFIG_MAINBOARD_VENDOR,
47 CONFIG_MAINBOARD_PART_NUMBER, board_id(), sku_id());
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020048
Patrick Rudolpha892cde2018-04-19 14:39:07 +020049 fit_add_compat_string(compat_string);
50 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020051
Patrick Rudolpha892cde2018-04-19 14:39:07 +020052 if (board_id() != UNDEFINED_STRAPPING_ID) {
53 snprintf(compat_string, sizeof(compat_string), "%s,%s-rev%u",
54 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER,
55 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",
62 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER,
63 sku_id());
64
65 fit_add_compat_string(compat_string);
66 }
67
Patrick Rudolpha892cde2018-04-19 14:39:07 +020068 snprintf(compat_string, sizeof(compat_string), "%s,%s",
69 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020070
Patrick Rudolpha892cde2018-04-19 14:39:07 +020071 fit_add_compat_string(compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +020072}
73
Julius Wernerb379f192019-05-13 16:34:16 -070074static struct fit_image_node *find_image(const char *name)
75{
76 struct fit_image_node *image;
77 list_for_each(image, image_nodes, list_node) {
78 if (!strcmp(image->name, name))
79 return image;
80 }
Julius Wernere9665952022-01-21 17:06:20 -080081 printk(BIOS_ERR, "Cannot find image node %s!\n", name);
Julius Wernerb379f192019-05-13 16:34:16 -070082 return NULL;
83}
84
Julius Werner21b0b1a2019-05-16 16:12:04 -070085static struct fit_image_node *find_image_with_overlays(const char *name,
86 int bytes, struct list_node *prev)
87{
88 struct fit_image_node *base = find_image(name);
89 if (!base)
90 return NULL;
91
92 int len = strnlen(name, bytes) + 1;
93 bytes -= len;
94 name += len;
95 while (bytes > 0) {
96 struct fit_overlay_chain *next = xzalloc(sizeof(*next));
97 next->overlay = find_image(name);
98 if (!next->overlay)
99 return NULL;
100 list_insert_after(&next->list_node, prev);
101 prev = &next->list_node;
102 len = strnlen(name, bytes) + 1;
103 bytes -= len;
104 name += len;
105 }
106
107 return base;
108}
109
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200110static void image_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200111{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200112 struct fit_image_node *image = xzalloc(sizeof(*image));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200113
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200114 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200115 image->name = node->name;
116
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200117 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200118 list_for_each(prop, node->properties, list_node) {
119 if (!strcmp("data", prop->prop.name)) {
120 image->data = prop->prop.data;
121 image->size = prop->prop.size;
122 } else if (!strcmp("compression", prop->prop.name)) {
123 if (!strcmp("none", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200124 image->compression = CBFS_COMPRESS_NONE;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200125 else if (!strcmp("lzma", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200126 image->compression = CBFS_COMPRESS_LZMA;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200127 else if (!strcmp("lz4", prop->prop.data))
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200128 image->compression = CBFS_COMPRESS_LZ4;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200129 else
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200130 image->compression = -1;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200131 }
132 }
133
134 list_insert_after(&image->list_node, &image_nodes);
135}
136
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200137static void config_node(struct device_tree_node *node)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200138{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200139 struct fit_config_node *config = xzalloc(sizeof(*config));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200140 config->name = node->name;
141
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200142 struct device_tree_property *prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200143 list_for_each(prop, node->properties, list_node) {
144 if (!strcmp("kernel", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700145 config->kernel = find_image(prop->prop.data);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200146 else if (!strcmp("fdt", prop->prop.name))
Julius Werner21b0b1a2019-05-16 16:12:04 -0700147 config->fdt = find_image_with_overlays(prop->prop.data,
148 prop->prop.size, &config->overlays);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200149 else if (!strcmp("ramdisk", prop->prop.name))
Julius Wernerb379f192019-05-13 16:34:16 -0700150 config->ramdisk = find_image(prop->prop.data);
Julius Wernerfec42062019-05-16 16:04:19 -0700151 else if (!strcmp("compatible", prop->prop.name))
152 config->compat = prop->prop;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200153 }
154
155 list_insert_after(&config->list_node, &config_nodes);
156}
157
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200158static void fit_unpack(struct device_tree *tree, const char **default_config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200159{
Julius Wernerb379f192019-05-13 16:34:16 -0700160 struct device_tree_node *child;
161 struct device_tree_node *images = dt_find_node_by_path(tree, "/images",
162 NULL, NULL, 0);
163 if (images)
164 list_for_each(child, images->children, list_node)
165 image_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200166
Julius Wernerb379f192019-05-13 16:34:16 -0700167 struct device_tree_node *configs = dt_find_node_by_path(tree,
168 "/configurations", NULL, NULL, 0);
169 if (configs) {
170 *default_config = dt_find_string_prop(configs, "default");
171 list_for_each(child, configs->children, list_node)
172 config_node(child);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200173 }
174}
175
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200176static int fdt_find_compat(const void *blob, uint32_t start_offset,
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200177 struct fdt_property *prop)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200178{
179 int offset = start_offset;
180 int size;
181
182 size = fdt_node_name(blob, offset, NULL);
183 if (!size)
184 return -1;
185 offset += size;
186
187 while ((size = fdt_next_property(blob, offset, prop))) {
188 if (!strcmp("compatible", prop->name))
189 return 0;
190
191 offset += size;
192 }
193
194 prop->name = NULL;
195 return -1;
196}
197
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200198static int fit_check_compat(struct fdt_property *compat_prop,
199 const char *compat_name)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200200{
201 int bytes = compat_prop->size;
202 const char *compat_str = compat_prop->data;
203
204 for (int pos = 0; bytes && compat_str[0]; pos++) {
205 if (!strncmp(compat_str, compat_name, bytes))
206 return pos;
207 int len = strlen(compat_str) + 1;
208 compat_str += len;
209 bytes -= len;
210 }
211 return -1;
212}
213
Patrick Rudolph0a7d6902018-08-22 09:55:15 +0200214void fit_update_chosen(struct device_tree *tree, const char *cmd_line)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200215{
216 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200217 struct device_tree_node *node;
218 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200219
220 dt_add_string_prop(node, "bootargs", cmd_line);
221}
222
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200223void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr,
224 size_t ramdisk_size)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200225{
226 const char *path[] = { "chosen", NULL };
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200227 struct device_tree_node *node;
228 node = dt_find_node(tree->root, path, NULL, NULL, 1);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200229
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200230 u64 start = (uintptr_t)ramdisk_addr;
231 u64 end = start + ramdisk_size;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200232
Patrick Rudolph3fca4ed2018-08-10 10:12:35 +0200233 dt_add_u64_prop(node, "linux,initrd-start", start);
234 dt_add_u64_prop(node, "linux,initrd-end", end);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200235}
236
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200237static void update_reserve_map(uint64_t start, uint64_t end,
238 struct device_tree *tree)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200239{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200240 struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry));
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200241
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200242 entry->start = start;
243 entry->size = end - start;
244
245 list_insert_after(&entry->list_node, &tree->reserve_map);
246}
247
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200248struct entry_params {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600249 unsigned int addr_cells;
250 unsigned int size_cells;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200251 void *data;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200252};
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200253
Martin Roth38ddbfb2019-10-23 21:41:00 -0600254static uint64_t max_range(unsigned int size_cells)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200255{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200256 /*
257 * Split up ranges who's sizes are too large to fit in #size-cells.
258 * The largest value we can store isn't a power of two, so we'll round
259 * down to make the math easier.
260 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200261 return 0x1ULL << (size_cells * 32 - 1);
262}
263
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200264static void update_mem_property(u64 start, u64 end, struct entry_params *params)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200265{
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200266 u8 *data = (u8 *)params->data;
267 u64 full_size = end - start;
268 while (full_size) {
269 const u64 max_size = max_range(params->size_cells);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200270 const u64 size = MIN(max_size, full_size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200271
272 dt_write_int(data, start, params->addr_cells * sizeof(u32));
273 data += params->addr_cells * sizeof(uint32_t);
274 start += size;
275
276 dt_write_int(data, size, params->size_cells * sizeof(u32));
277 data += params->size_cells * sizeof(uint32_t);
278 full_size -= size;
279 }
280 params->data = data;
281}
282
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200283struct mem_map {
284 struct memranges mem;
285 struct memranges reserved;
286};
287
288static bool walk_memory_table(const struct range_entry *r, void *arg)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200289{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200290 struct mem_map *arg_map = arg;
291
292 /*
293 * Kernel likes its available memory areas at least 1MB
294 * aligned, let's trim the regions such that unaligned padding
295 * is added to reserved memory.
296 */
297 if (range_entry_tag(r) == BM_MEM_RAM) {
298 uint64_t new_start = ALIGN_UP(range_entry_base(r), 1 * MiB);
299 uint64_t new_end = ALIGN_DOWN(range_entry_end(r), 1 * MiB);
300
301 if (new_start != range_entry_base(r))
302 memranges_insert(&arg_map->reserved,
303 range_entry_base(r),
304 new_start - range_entry_base(r),
305 BM_MEM_RESERVED);
306
307 if (new_start != new_end)
308 memranges_insert(&arg_map->mem, new_start,
309 new_end - new_start, BM_MEM_RAM);
310
311 if (new_end != range_entry_end(r))
312 memranges_insert(&arg_map->reserved, new_end,
313 range_entry_end(r) - new_end,
314 BM_MEM_RESERVED);
315 } else
316 memranges_insert(&arg_map->reserved, range_entry_base(r),
317 range_entry_size(r),
318 BM_MEM_RESERVED);
319
320 return true;
321}
322
323void fit_add_compat_string(const char *str)
324{
325 struct compat_string_entry *compat_node;
326
327 compat_node = xzalloc(sizeof(*compat_node));
328 compat_node->compat_string = strdup(str);
329
330 clean_compat_string((char *)compat_node->compat_string);
331
332 list_insert_after(&compat_node->list_node, &compat_strings);
333}
334
335void fit_update_memory(struct device_tree *tree)
336{
337 const struct range_entry *r;
338 struct device_tree_node *node;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200339 u32 addr_cells = 1, size_cells = 1;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200340 struct mem_map map;
341
342 printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n");
343
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200344 dt_read_cell_props(tree->root, &addr_cells, &size_cells);
345
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200346 /*
347 * First remove all existing device_type="memory" nodes, then add ours.
348 */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200349 list_for_each(node, tree->root->children, list_node) {
350 const char *devtype = dt_find_string_prop(node, "device_type");
351 if (devtype && !strcmp(devtype, "memory"))
352 list_remove(&node->list_node);
353 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200354
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200355 node = xzalloc(sizeof(*node));
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200356
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200357 node->name = "memory";
358 list_insert_after(&node->list_node, &tree->root->children);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200359 dt_add_string_prop(node, "device_type", (char *)"memory");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200360
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200361 memranges_init_empty(&map.mem, NULL, 0);
362 memranges_init_empty(&map.reserved, NULL, 0);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200363
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200364 bootmem_walk_os_mem(walk_memory_table, &map);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200365
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200366 /* CBMEM regions are both carved out and explicitly reserved. */
367 memranges_each_entry(r, &map.reserved) {
368 update_reserve_map(range_entry_base(r), range_entry_end(r),
369 tree);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200370 }
371
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200372 /*
373 * Count the amount of 'reg' entries we need (account for size limits).
374 */
375 size_t count = 0;
376 memranges_each_entry(r, &map.mem) {
377 uint64_t size = range_entry_size(r);
378 uint64_t max_size = max_range(size_cells);
379 count += DIV_ROUND_UP(size, max_size);
380 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200381
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200382 /* Allocate the right amount of space and fill up the entries. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200383 size_t length = count * (addr_cells + size_cells) * sizeof(u32);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200384
385 void *data = xzalloc(length);
386
387 struct entry_params add_params = { addr_cells, size_cells, data };
388 memranges_each_entry(r, &map.mem) {
389 update_mem_property(range_entry_base(r), range_entry_end(r),
390 &add_params);
391 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200392 assert(add_params.data - data == length);
393
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200394 /* Assemble the final property and add it to the device tree. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200395 dt_add_bin_prop(node, "reg", data, length);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200396
397 memranges_teardown(&map.mem);
398 memranges_teardown(&map.reserved);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200399}
400
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200401/*
402 * Finds a compat string and updates the compat position and rank.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200403 * @param config The current config node to operate on
Julius Wernerb379f192019-05-13 16:34:16 -0700404 * @return 0 if compat updated, -1 if this FDT cannot be used.
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200405 */
Julius Wernerb379f192019-05-13 16:34:16 -0700406static int fit_update_compat(struct fit_config_node *config)
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200407{
Julius Werner23df4772019-05-17 22:50:18 -0700408 /* If there was no "compatible" property in config node, this is a
409 legacy FIT image. Must extract compat prop from FDT itself. */
Julius Wernerfec42062019-05-16 16:04:19 -0700410 if (!config->compat.name) {
411 void *fdt_blob = config->fdt->data;
412 const struct fdt_header *fdt_header = fdt_blob;
413 uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset);
Julius Wernerb379f192019-05-13 16:34:16 -0700414
Julius Wernerfec42062019-05-16 16:04:19 -0700415 if (config->fdt->compression != CBFS_COMPRESS_NONE) {
Julius Wernere9665952022-01-21 17:06:20 -0800416 printk(BIOS_ERR, "config %s has a compressed FDT without "
Julius Wernerfec42062019-05-16 16:04:19 -0700417 "external compatible property, skipping.\n",
418 config->name);
419 return -1;
420 }
421
Julius Werner23df4772019-05-17 22:50:18 -0700422 /* FDT overlays are not supported in legacy FIT images. */
Julius Werner21b0b1a2019-05-16 16:12:04 -0700423 if (config->overlays.next) {
Julius Wernere9665952022-01-21 17:06:20 -0800424 printk(BIOS_ERR, "config %s has overlay but no compat!\n",
Julius Werner21b0b1a2019-05-16 16:12:04 -0700425 config->name);
426 return -1;
427 }
428
Julius Wernerfec42062019-05-16 16:04:19 -0700429 if (fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
Julius Wernere9665952022-01-21 17:06:20 -0800430 printk(BIOS_ERR, "Can't find compat string in FDT %s "
Julius Wernerfec42062019-05-16 16:04:19 -0700431 "for config %s, skipping.\n",
432 config->fdt->name, config->name);
433 return -1;
434 }
435 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200436
Julius Wernerb379f192019-05-13 16:34:16 -0700437 config->compat_pos = -1;
438 config->compat_rank = -1;
Julius Wernerfec42062019-05-16 16:04:19 -0700439 size_t i = 0;
440 struct compat_string_entry *compat_node;
441 list_for_each(compat_node, compat_strings, list_node) {
442 int pos = fit_check_compat(&config->compat,
443 compat_node->compat_string);
444 if (pos >= 0) {
445 config->compat_pos = pos;
446 config->compat_rank = i;
447 config->compat_string =
448 compat_node->compat_string;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200449 }
Julius Wernerfec42062019-05-16 16:04:19 -0700450 i++;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200451 }
Julius Wernerb379f192019-05-13 16:34:16 -0700452
453 return 0;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200454}
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200455
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200456struct fit_config_node *fit_load(void *fit)
457{
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200458 struct fit_image_node *image;
459 struct fit_config_node *config;
460 struct compat_string_entry *compat_node;
Julius Werner21b0b1a2019-05-16 16:12:04 -0700461 struct fit_overlay_chain *overlay_chain;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200462
463 printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit);
464
Julius Werner73eaec82019-05-03 17:58:07 -0700465 struct device_tree *tree = fdt_unflatten(fit);
466 if (!tree) {
Julius Wernere9665952022-01-21 17:06:20 -0800467 printk(BIOS_ERR, "Failed to unflatten FIT image!\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200468 return NULL;
469 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200470
471 const char *default_config_name = NULL;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200472 struct fit_config_node *default_config = NULL;
473 struct fit_config_node *compat_config = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200474
475 fit_unpack(tree, &default_config_name);
476
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200477 /* List the images we found. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200478 list_for_each(image, image_nodes, list_node)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200479 printk(BIOS_DEBUG, "FIT: Image %s has %d bytes.\n", image->name,
480 image->size);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200481
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200482 fit_add_default_compat_strings();
483
484 printk(BIOS_DEBUG, "FIT: Compat preference "
485 "(lowest to highest priority) :");
486
487 list_for_each(compat_node, compat_strings, list_node) {
488 printk(BIOS_DEBUG, " %s", compat_node->compat_string);
489 }
490 printk(BIOS_DEBUG, "\n");
491 /* Process and list the configs. */
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200492 list_for_each(config, config_nodes, list_node) {
Julius Wernerb379f192019-05-13 16:34:16 -0700493 if (!config->kernel) {
Julius Wernere9665952022-01-21 17:06:20 -0800494 printk(BIOS_ERR, "config %s has no kernel, skipping.\n",
Julius Wernerb379f192019-05-13 16:34:16 -0700495 config->name);
496 continue;
497 }
498 if (!config->fdt) {
Julius Wernere9665952022-01-21 17:06:20 -0800499 printk(BIOS_ERR, "config %s has no FDT, skipping.\n",
Julius Wernerb379f192019-05-13 16:34:16 -0700500 config->name);
501 continue;
502 }
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200503
Julius Wernerb379f192019-05-13 16:34:16 -0700504 if (config->ramdisk &&
505 config->ramdisk->compression < 0) {
Julius Wernere9665952022-01-21 17:06:20 -0800506 printk(BIOS_WARNING, "Ramdisk is compressed with "
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200507 "an unsupported algorithm, discarding config %s."
508 "\n", config->name);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200509 continue;
510 }
511
Julius Wernerb379f192019-05-13 16:34:16 -0700512 if (fit_update_compat(config))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200513 continue;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200514
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200515 printk(BIOS_DEBUG, "FIT: config %s", config->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200516 if (default_config_name &&
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200517 !strcmp(config->name, default_config_name)) {
518 printk(BIOS_DEBUG, " (default)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200519 default_config = config;
520 }
Julius Wernerb379f192019-05-13 16:34:16 -0700521 printk(BIOS_DEBUG, ", kernel %s", config->kernel->name);
522 printk(BIOS_DEBUG, ", fdt %s", config->fdt->name);
Julius Werner21b0b1a2019-05-16 16:12:04 -0700523 list_for_each(overlay_chain, config->overlays, list_node)
524 printk(BIOS_DEBUG, " %s", overlay_chain->overlay->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200525 if (config->ramdisk)
Julius Wernerb379f192019-05-13 16:34:16 -0700526 printk(BIOS_DEBUG, ", ramdisk %s",
527 config->ramdisk->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200528 if (config->compat.name) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200529 printk(BIOS_DEBUG, ", compat");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200530 int bytes = config->compat.size;
531 const char *compat_str = config->compat.data;
532 for (int pos = 0; bytes && compat_str[0]; pos++) {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200533 printk(BIOS_DEBUG, " %s", compat_str);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200534 if (pos == config->compat_pos)
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200535 printk(BIOS_DEBUG, " (match)");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200536 int len = strlen(compat_str) + 1;
537 compat_str += len;
538 bytes -= len;
539 }
540
541 if (config->compat_rank >= 0 && (!compat_config ||
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200542 config->compat_rank > compat_config->compat_rank))
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200543 compat_config = config;
544 }
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200545 printk(BIOS_DEBUG, "\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200546 }
547
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200548 struct fit_config_node *to_boot = NULL;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200549 if (compat_config) {
550 to_boot = compat_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200551 printk(BIOS_INFO, "FIT: Choosing best match %s for compat "
552 "%s.\n", to_boot->name, to_boot->compat_string);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200553 } else if (default_config) {
554 to_boot = default_config;
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200555 printk(BIOS_INFO, "FIT: No match, choosing default %s.\n",
556 to_boot->name);
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200557 } else {
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200558 printk(BIOS_ERR, "FIT: No compatible or default configs. "
559 "Giving up.\n");
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200560 return NULL;
561 }
562
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200563 return to_boot;
Patrick Rudolpha45e9f82018-04-19 14:39:07 +0200564}