/ src / lib / fit.c
fit.c
  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 '-'. */
 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  
 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  
 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  
 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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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   */
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  
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  }