xref: /aosp_15_r20/external/coreboot/src/lib/fit.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* Taken from depthcharge: src/boot/fit.c */
2 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 
4 #include <assert.h>
5 #include <console/console.h>
6 #include <ctype.h>
7 #include <endian.h>
8 #include <identity.h>
9 #include <bootmem.h>
10 #include <string.h>
11 #include <program_loading.h>
12 #include <memrange.h>
13 #include <fit.h>
14 #include <boardid.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <types.h>
18 
19 static struct list_node image_nodes;
20 static struct list_node config_nodes;
21 static struct list_node compat_strings;
22 
23 struct compat_string_entry {
24 	const char *compat_string;
25 	struct list_node list_node;
26 };
27 
28 /* Convert string to lowercase and replace '_' and spaces with '-'. */
clean_compat_string(char * str)29 static char *clean_compat_string(char *str)
30 {
31 	for (size_t i = 0; i < strlen(str); i++) {
32 		str[i] = tolower(str[i]);
33 		if (str[i] == '_' || str[i] == ' ')
34 			str[i] = '-';
35 	}
36 
37 	return str;
38 }
39 
fit_add_default_compat_strings(void)40 static void fit_add_default_compat_strings(void)
41 {
42 	char compat_string[80] = {};
43 
44 	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", mainboard_vendor, mainboard_part_number,
48 			 board_id(), sku_id());
49 
50 		fit_add_compat_string(compat_string);
51 	}
52 
53 	if (board_id() != UNDEFINED_STRAPPING_ID) {
54 		snprintf(compat_string, sizeof(compat_string), "%s,%s-rev%u",
55 			 mainboard_vendor, mainboard_part_number, board_id());
56 
57 		fit_add_compat_string(compat_string);
58 	}
59 
60 	if (sku_id() != UNDEFINED_STRAPPING_ID) {
61 		snprintf(compat_string, sizeof(compat_string), "%s,%s-sku%u",
62 			 mainboard_vendor, mainboard_part_number, sku_id());
63 
64 		fit_add_compat_string(compat_string);
65 	}
66 
67 	snprintf(compat_string, sizeof(compat_string), "%s,%s",
68 		 mainboard_vendor, mainboard_part_number);
69 
70 	fit_add_compat_string(compat_string);
71 }
72 
find_image(const char * name)73 static 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 	}
80 	printk(BIOS_ERR, "Cannot find image node %s!\n", name);
81 	return NULL;
82 }
83 
find_image_with_overlays(const char * name,int bytes,struct list_node * prev)84 static 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 
image_node(struct device_tree_node * node)109 static void image_node(struct device_tree_node *node)
110 {
111 	struct fit_image_node *image = xzalloc(sizeof(*image));
112 
113 	image->compression = CBFS_COMPRESS_NONE;
114 	image->name = node->name;
115 
116 	struct device_tree_property *prop;
117 	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))
123 				image->compression = CBFS_COMPRESS_NONE;
124 			else if (!strcmp("lzma", prop->prop.data))
125 				image->compression = CBFS_COMPRESS_LZMA;
126 			else if (!strcmp("lz4", prop->prop.data))
127 				image->compression = CBFS_COMPRESS_LZ4;
128 			else
129 				image->compression = -1;
130 		}
131 	}
132 
133 	list_insert_after(&image->list_node, &image_nodes);
134 }
135 
config_node(struct device_tree_node * node)136 static void config_node(struct device_tree_node *node)
137 {
138 	struct fit_config_node *config = xzalloc(sizeof(*config));
139 	config->name = node->name;
140 
141 	struct device_tree_property *prop;
142 	list_for_each(prop, node->properties, list_node) {
143 		if (!strcmp("kernel", prop->prop.name))
144 			config->kernel = find_image(prop->prop.data);
145 		else if (!strcmp("fdt", prop->prop.name))
146 			config->fdt = find_image_with_overlays(prop->prop.data,
147 				prop->prop.size, &config->overlays);
148 		else if (!strcmp("ramdisk", prop->prop.name))
149 			config->ramdisk = find_image(prop->prop.data);
150 		else if (!strcmp("compatible", prop->prop.name))
151 			config->compat = prop->prop;
152 	}
153 
154 	list_insert_after(&config->list_node, &config_nodes);
155 }
156 
fit_unpack(struct device_tree * tree,const char ** default_config)157 static void fit_unpack(struct device_tree *tree, const char **default_config)
158 {
159 	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);
165 
166 	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);
172 	}
173 }
174 
fdt_find_compat(const void * blob,uint32_t start_offset,struct fdt_property * prop)175 static int fdt_find_compat(const void *blob, uint32_t start_offset,
176 			   struct fdt_property *prop)
177 {
178 	int offset = start_offset;
179 	int size;
180 
181 	size = fdt_next_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 
fit_check_compat(struct fdt_property * compat_prop,const char * compat_name)197 static int fit_check_compat(struct fdt_property *compat_prop,
198 			    const char *compat_name)
199 {
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 
fit_update_chosen(struct device_tree * tree,const char * cmd_line)213 void fit_update_chosen(struct device_tree *tree, const char *cmd_line)
214 {
215 	const char *path[] = { "chosen", NULL };
216 	struct device_tree_node *node;
217 	node = dt_find_node(tree->root, path, NULL, NULL, 1);
218 
219 	dt_add_string_prop(node, "bootargs", cmd_line);
220 }
221 
fit_add_ramdisk(struct device_tree * tree,void * ramdisk_addr,size_t ramdisk_size)222 void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr,
223 		     size_t ramdisk_size)
224 {
225 	const char *path[] = { "chosen", NULL };
226 	struct device_tree_node *node;
227 	node = dt_find_node(tree->root, path, NULL, NULL, 1);
228 
229 	u64 start = (uintptr_t)ramdisk_addr;
230 	u64 end = start + ramdisk_size;
231 
232 	dt_add_u64_prop(node, "linux,initrd-start", start);
233 	dt_add_u64_prop(node, "linux,initrd-end", end);
234 }
235 
update_reserve_map(uint64_t start,uint64_t end,struct device_tree * tree)236 static void update_reserve_map(uint64_t start, uint64_t end,
237 			       struct device_tree *tree)
238 {
239 	struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry));
240 
241 	entry->start = start;
242 	entry->size = end - start;
243 
244 	list_insert_after(&entry->list_node, &tree->reserve_map);
245 }
246 
247 struct entry_params {
248 	unsigned int addr_cells;
249 	unsigned int size_cells;
250 	void *data;
251 };
252 
max_range(unsigned int size_cells)253 static uint64_t max_range(unsigned int size_cells)
254 {
255 	/*
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 	 */
260 	return 0x1ULL << (size_cells * 32 - 1);
261 }
262 
update_mem_property(u64 start,u64 end,struct entry_params * params)263 static void update_mem_property(u64 start, u64 end, struct entry_params *params)
264 {
265 	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);
269 		const u64 size = MIN(max_size, full_size);
270 
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 
282 struct mem_map {
283 	struct memranges mem;
284 	struct memranges reserved;
285 };
286 
walk_memory_table(const struct range_entry * r,void * arg)287 static bool walk_memory_table(const struct range_entry *r, void *arg)
288 {
289 	struct mem_map *arg_map = arg;
290 	struct memranges *ranges;
291 	enum bootmem_type tag;
292 
293 	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);
296 	return true;
297 }
298 
fit_add_compat_string(const char * str)299 void 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 
fit_update_memory(struct device_tree * tree)311 void fit_update_memory(struct device_tree *tree)
312 {
313 	const struct range_entry *r;
314 	struct device_tree_node *node;
315 	u32 addr_cells = 1, size_cells = 1;
316 	struct mem_map map;
317 
318 	printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n");
319 
320 	dt_read_cell_props(tree->root, &addr_cells, &size_cells);
321 
322 	/*
323 	 * First remove all existing device_type="memory" nodes, then add ours.
324 	 */
325 	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 	}
330 
331 	node = xzalloc(sizeof(*node));
332 
333 	node->name = "memory";
334 	list_insert_after(&node->list_node, &tree->root->children);
335 	dt_add_string_prop(node, "device_type", (char *)"memory");
336 
337 	memranges_init_empty(&map.mem, NULL, 0);
338 	memranges_init_empty(&map.reserved, NULL, 0);
339 
340 	bootmem_walk_os_mem(walk_memory_table, &map);
341 
342 	/* 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);
346 	}
347 
348 	/*
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 	}
357 
358 	/* Allocate the right amount of space and fill up the entries. */
359 	size_t length = count * (addr_cells + size_cells) * sizeof(u32);
360 
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 	}
368 	assert(add_params.data - data == length);
369 
370 	/* Assemble the final property and add it to the device tree. */
371 	dt_add_bin_prop(node, "reg", data, length);
372 
373 	memranges_teardown(&map.mem);
374 	memranges_teardown(&map.reserved);
375 }
376 
377 /*
378  * Finds a compat string and updates the compat position and rank.
379  * @param config The current config node to operate on
380  * @return 0 if compat updated, -1 if this FDT cannot be used.
381  */
fit_update_compat(struct fit_config_node * config)382 static int fit_update_compat(struct fit_config_node *config)
383 {
384 	/* If there was no "compatible" property in config node, this is a
385 	   legacy FIT image. Must extract compat prop from FDT itself. */
386 	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);
390 
391 		if (config->fdt->compression != CBFS_COMPRESS_NONE) {
392 			printk(BIOS_ERR, "config %s has a compressed FDT without "
393 			       "external compatible property, skipping.\n",
394 			       config->name);
395 			return -1;
396 		}
397 
398 		/* FDT overlays are not supported in legacy FIT images. */
399 		if (config->overlays.next) {
400 			printk(BIOS_ERR, "config %s has overlay but no compat!\n",
401 			       config->name);
402 			return -1;
403 		}
404 
405 		if (fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
406 			printk(BIOS_ERR, "Can't find compat string in FDT %s "
407 			       "for config %s, skipping.\n",
408 			       config->fdt->name, config->name);
409 			return -1;
410 		}
411 	}
412 
413 	config->compat_pos = -1;
414 	config->compat_rank = -1;
415 	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;
425 		}
426 		i++;
427 	}
428 
429 	return 0;
430 }
431 
fit_load(void * fit)432 struct fit_config_node *fit_load(void *fit)
433 {
434 	struct fit_image_node *image;
435 	struct fit_config_node *config;
436 	struct compat_string_entry *compat_node;
437 	struct fit_overlay_chain *overlay_chain;
438 
439 	printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit);
440 
441 	struct device_tree *tree = fdt_unflatten(fit);
442 	if (!tree) {
443 		printk(BIOS_ERR, "Failed to unflatten FIT image!\n");
444 		return NULL;
445 	}
446 
447 	const char *default_config_name = NULL;
448 	struct fit_config_node *default_config = NULL;
449 	struct fit_config_node *compat_config = NULL;
450 
451 	fit_unpack(tree, &default_config_name);
452 
453 	/* List the images we found. */
454 	list_for_each(image, image_nodes, list_node)
455 		printk(BIOS_DEBUG, "FIT: Image %s has %d bytes.\n", image->name,
456 		       image->size);
457 
458 	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. */
468 	list_for_each(config, config_nodes, list_node) {
469 		if (!config->kernel) {
470 			printk(BIOS_ERR, "config %s has no kernel, skipping.\n",
471 			       config->name);
472 			continue;
473 		}
474 		if (!config->fdt) {
475 			printk(BIOS_ERR, "config %s has no FDT, skipping.\n",
476 			       config->name);
477 			continue;
478 		}
479 
480 		if (config->ramdisk &&
481 		    config->ramdisk->compression < 0) {
482 			printk(BIOS_WARNING, "Ramdisk is compressed with "
483 			       "an unsupported algorithm, discarding config %s."
484 			       "\n", config->name);
485 			continue;
486 		}
487 
488 		if (fit_update_compat(config))
489 			continue;
490 
491 		printk(BIOS_DEBUG, "FIT: config %s", config->name);
492 		if (default_config_name &&
493 		    !strcmp(config->name, default_config_name)) {
494 			printk(BIOS_DEBUG, " (default)");
495 			default_config = config;
496 		}
497 		printk(BIOS_DEBUG, ", kernel %s", config->kernel->name);
498 		printk(BIOS_DEBUG, ", fdt %s", config->fdt->name);
499 		list_for_each(overlay_chain, config->overlays, list_node)
500 			printk(BIOS_DEBUG, " %s", overlay_chain->overlay->name);
501 		if (config->ramdisk)
502 			printk(BIOS_DEBUG, ", ramdisk %s",
503 			       config->ramdisk->name);
504 		if (config->compat.name) {
505 			printk(BIOS_DEBUG, ", compat");
506 			int bytes = config->compat.size;
507 			const char *compat_str = config->compat.data;
508 			for (int pos = 0; bytes && compat_str[0]; pos++) {
509 				printk(BIOS_DEBUG, " %s", compat_str);
510 				if (pos == config->compat_pos)
511 					printk(BIOS_DEBUG, " (match)");
512 				int len = strlen(compat_str) + 1;
513 				compat_str += len;
514 				bytes -= len;
515 			}
516 
517 			if (config->compat_rank >= 0 && (!compat_config ||
518 			    config->compat_rank > compat_config->compat_rank))
519 				compat_config = config;
520 		}
521 		printk(BIOS_DEBUG, "\n");
522 	}
523 
524 	struct fit_config_node *to_boot = NULL;
525 	if (compat_config) {
526 		to_boot = compat_config;
527 		printk(BIOS_INFO, "FIT: Choosing best match %s for compat "
528 		       "%s.\n", to_boot->name, to_boot->compat_string);
529 	} else if (default_config) {
530 		to_boot = default_config;
531 		printk(BIOS_INFO, "FIT: No match, choosing default %s.\n",
532 		       to_boot->name);
533 	} else {
534 		printk(BIOS_ERR, "FIT: No compatible or default configs. "
535 		       "Giving up.\n");
536 		return NULL;
537 	}
538 
539 	return to_boot;
540 }
541