lib: Add FIT payload support
* Add support for parsing and booting FIT payloads. * Build fit loader code from depthcharge. * Fix coding style. * Add Kconfig option to add compiletime support for FIT. * Add support for initrd. * Add default compat strings * Apply optional devicetree fixups using dt_apply_fixups Starting at this point the CBFS payload/ can be either SELF or FIT. Tested on Cavium SoC: Parses and loads a Linux kernel 4.16.3. Tested on Cavium SoC: Parses and loads a Linux kernel 4.15.0. Tested on Cavium SoC: Parses and loads a Linux kernel 4.1.52. Change-Id: I0f27b92a5e074966f893399eb401eb97d784850d Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-on: https://review.coreboot.org/25019 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
This commit is contained in:
		
				
					committed by
					
						
						Philipp Deppenwiese
					
				
			
			
				
	
			
			
			
						parent
						
							8c986ab263
						
					
				
				
					commit
					a892cde653
				
			
							
								
								
									
										489
									
								
								src/lib/fit.c
									
									
									
									
									
								
							
							
						
						
									
										489
									
								
								src/lib/fit.c
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2013 Google Inc.
 | 
			
		||||
 * Copyright 2018-present Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * See file CREDITS for list of people who contributed to this
 | 
			
		||||
 * project.
 | 
			
		||||
 * Taken from depthcharge: src/boot/fit.c
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
@@ -17,93 +17,99 @@
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <endian.h>
 | 
			
		||||
#include <libpayload.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <bootmem.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <cbfs.h>
 | 
			
		||||
#include <program_loading.h>
 | 
			
		||||
#include <timestamp.h>
 | 
			
		||||
#include <memrange.h>
 | 
			
		||||
#include <fit.h>
 | 
			
		||||
#include <boardid.h>
 | 
			
		||||
#include <commonlib/include/commonlib/stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "base/ranges.h"
 | 
			
		||||
#include "boot/fit.h"
 | 
			
		||||
static struct list_node image_nodes;
 | 
			
		||||
static struct list_node config_nodes;
 | 
			
		||||
static struct list_node compat_strings;
 | 
			
		||||
 | 
			
		||||
struct compat_string_entry {
 | 
			
		||||
	const char *compat_string;
 | 
			
		||||
	struct list_node list_node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static ListNode image_nodes;
 | 
			
		||||
static ListNode config_nodes;
 | 
			
		||||
 | 
			
		||||
static const char *fit_kernel_compat[10] = { NULL };
 | 
			
		||||
static int num_fit_kernel_compat = 0;
 | 
			
		||||
 | 
			
		||||
void fit_add_compat(const char *compat)
 | 
			
		||||
/* Convert string to lowercase and replace '_' with '-'. */
 | 
			
		||||
static char *clean_compat_string(char *str)
 | 
			
		||||
{
 | 
			
		||||
	assert(num_fit_kernel_compat < ARRAY_SIZE(fit_kernel_compat));
 | 
			
		||||
	fit_kernel_compat[num_fit_kernel_compat++] = compat;
 | 
			
		||||
	for (size_t i = 0; i < strlen(str); i++) {
 | 
			
		||||
		str[i] = tolower(str[i]);
 | 
			
		||||
		if (str[i] == '_')
 | 
			
		||||
			str[i] = '-';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fit_add_default_compats(void)
 | 
			
		||||
static void fit_add_default_compat_strings(void)
 | 
			
		||||
{
 | 
			
		||||
	const char pattern[] = "google,%s-rev%u-sku%u";
 | 
			
		||||
	u32 rev = lib_sysinfo.board_id;
 | 
			
		||||
	u32 sku = lib_sysinfo.sku_id;
 | 
			
		||||
	char compat_string[80] = {};
 | 
			
		||||
 | 
			
		||||
	static int done = 0;
 | 
			
		||||
	if (done)
 | 
			
		||||
		return;
 | 
			
		||||
	done = 1;
 | 
			
		||||
	if ((board_id() != UNDEFINED_STRAPPING_ID) &&
 | 
			
		||||
	    (sku_id() != UNDEFINED_STRAPPING_ID)) {
 | 
			
		||||
		snprintf(compat_string, sizeof(compat_string),
 | 
			
		||||
			 "%s,%s-rev%u-sku%u", CONFIG_MAINBOARD_VENDOR,
 | 
			
		||||
			 CONFIG_MAINBOARD_PART_NUMBER, board_id(), sku_id());
 | 
			
		||||
 | 
			
		||||
	char *compat = xmalloc(sizeof(pattern) + sizeof(CONFIG_BOARD) + 20);
 | 
			
		||||
	sprintf(compat, pattern, CONFIG_BOARD,
 | 
			
		||||
		lib_sysinfo.board_id, lib_sysinfo.sku_id);
 | 
			
		||||
		fit_add_compat_string(compat_string);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char *c;
 | 
			
		||||
	for (c = compat; *c != '\0'; c++)
 | 
			
		||||
		if (*c == '_')
 | 
			
		||||
			*c = '-';
 | 
			
		||||
	if (board_id() != UNDEFINED_STRAPPING_ID) {
 | 
			
		||||
		snprintf(compat_string, sizeof(compat_string), "%s,%s-rev%u",
 | 
			
		||||
			 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER,
 | 
			
		||||
			 board_id());
 | 
			
		||||
 | 
			
		||||
	if (sku != UNDEFINED_STRAPPING_ID && rev != UNDEFINED_STRAPPING_ID)
 | 
			
		||||
		fit_add_compat(strdup(compat));
 | 
			
		||||
		fit_add_compat_string(compat_string);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*strrchr(compat, '-') = '\0';
 | 
			
		||||
	if (rev != UNDEFINED_STRAPPING_ID)
 | 
			
		||||
		fit_add_compat(strdup(compat));
 | 
			
		||||
	snprintf(compat_string, sizeof(compat_string), "%s,%s",
 | 
			
		||||
		 CONFIG_MAINBOARD_VENDOR, CONFIG_MAINBOARD_PART_NUMBER);
 | 
			
		||||
 | 
			
		||||
	*strrchr(compat, '-') = '\0';
 | 
			
		||||
	fit_add_compat(compat);
 | 
			
		||||
	fit_add_compat_string(compat_string);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void image_node(DeviceTreeNode *node)
 | 
			
		||||
static void image_node(struct device_tree_node *node)
 | 
			
		||||
{
 | 
			
		||||
	FitImageNode *image = xzalloc(sizeof(*image));
 | 
			
		||||
	image->compression = CompressionNone;
 | 
			
		||||
	struct fit_image_node *image = xzalloc(sizeof(*image));
 | 
			
		||||
 | 
			
		||||
	image->compression = CBFS_COMPRESS_NONE;
 | 
			
		||||
	image->name = node->name;
 | 
			
		||||
 | 
			
		||||
	DeviceTreeProperty *prop;
 | 
			
		||||
	struct device_tree_property *prop;
 | 
			
		||||
	list_for_each(prop, node->properties, list_node) {
 | 
			
		||||
		if (!strcmp("data", prop->prop.name)) {
 | 
			
		||||
			image->data = prop->prop.data;
 | 
			
		||||
			image->size = prop->prop.size;
 | 
			
		||||
		} else if (!strcmp("compression", prop->prop.name)) {
 | 
			
		||||
			if (!strcmp("none", prop->prop.data))
 | 
			
		||||
				image->compression = CompressionNone;
 | 
			
		||||
				image->compression = CBFS_COMPRESS_NONE;
 | 
			
		||||
			else if (!strcmp("lzma", prop->prop.data))
 | 
			
		||||
				image->compression = CompressionLzma;
 | 
			
		||||
				image->compression = CBFS_COMPRESS_LZMA;
 | 
			
		||||
			else if (!strcmp("lz4", prop->prop.data))
 | 
			
		||||
				image->compression = CompressionLz4;
 | 
			
		||||
				image->compression = CBFS_COMPRESS_LZ4;
 | 
			
		||||
			else
 | 
			
		||||
				image->compression = CompressionInvalid;
 | 
			
		||||
				image->compression = -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list_insert_after(&image->list_node, &image_nodes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void config_node(DeviceTreeNode *node)
 | 
			
		||||
static void config_node(struct device_tree_node *node)
 | 
			
		||||
{
 | 
			
		||||
	FitConfigNode *config = xzalloc(sizeof(*config));
 | 
			
		||||
	struct fit_config_node *config = xzalloc(sizeof(*config));
 | 
			
		||||
	config->name = node->name;
 | 
			
		||||
 | 
			
		||||
	DeviceTreeProperty *prop;
 | 
			
		||||
	struct device_tree_property *prop;
 | 
			
		||||
	list_for_each(prop, node->properties, list_node) {
 | 
			
		||||
		if (!strcmp("kernel", prop->prop.name))
 | 
			
		||||
			config->kernel = prop->prop.data;
 | 
			
		||||
@@ -116,21 +122,20 @@ static void config_node(DeviceTreeNode *node)
 | 
			
		||||
	list_insert_after(&config->list_node, &config_nodes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fit_unpack(DeviceTree *tree, const char **default_config)
 | 
			
		||||
static void fit_unpack(struct device_tree *tree, const char **default_config)
 | 
			
		||||
{
 | 
			
		||||
	assert(tree && tree->root);
 | 
			
		||||
 | 
			
		||||
	DeviceTreeNode *top;
 | 
			
		||||
	struct device_tree_node *top;
 | 
			
		||||
	list_for_each(top, tree->root->children, list_node) {
 | 
			
		||||
		DeviceTreeNode *child;
 | 
			
		||||
		struct device_tree_node *child;
 | 
			
		||||
		if (!strcmp("images", top->name)) {
 | 
			
		||||
 | 
			
		||||
			list_for_each(child, top->children, list_node)
 | 
			
		||||
				image_node(child);
 | 
			
		||||
 | 
			
		||||
		} else if (!strcmp("configurations", top->name)) {
 | 
			
		||||
 | 
			
		||||
			DeviceTreeProperty *prop;
 | 
			
		||||
			struct device_tree_property *prop;
 | 
			
		||||
			list_for_each(prop, top->properties, list_node) {
 | 
			
		||||
				if (!strcmp("default", prop->prop.name) &&
 | 
			
		||||
						default_config)
 | 
			
		||||
@@ -143,9 +148,9 @@ static void fit_unpack(DeviceTree *tree, const char **default_config)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static FitImageNode *find_image(const char *name)
 | 
			
		||||
static struct fit_image_node *find_image(const char *name)
 | 
			
		||||
{
 | 
			
		||||
	FitImageNode *image;
 | 
			
		||||
	struct fit_image_node *image;
 | 
			
		||||
	list_for_each(image, image_nodes, list_node) {
 | 
			
		||||
		if (!strcmp(image->name, name))
 | 
			
		||||
			return image;
 | 
			
		||||
@@ -153,7 +158,8 @@ static FitImageNode *find_image(const char *name)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fdt_find_compat(void *blob, uint32_t start_offset, FdtProperty *prop)
 | 
			
		||||
static int fdt_find_compat(void *blob, uint32_t start_offset,
 | 
			
		||||
			   struct fdt_property *prop)
 | 
			
		||||
{
 | 
			
		||||
	int offset = start_offset;
 | 
			
		||||
	int size;
 | 
			
		||||
@@ -174,7 +180,8 @@ static int fdt_find_compat(void *blob, uint32_t start_offset, FdtProperty *prop)
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fit_check_compat(FdtProperty *compat_prop, const char *compat_name)
 | 
			
		||||
static int fit_check_compat(struct fdt_property *compat_prop,
 | 
			
		||||
			    const char *compat_name)
 | 
			
		||||
{
 | 
			
		||||
	int bytes = compat_prop->size;
 | 
			
		||||
	const char *compat_str = compat_prop->data;
 | 
			
		||||
@@ -189,18 +196,21 @@ static int fit_check_compat(FdtProperty *compat_prop, const char *compat_name)
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_chosen(DeviceTree *tree, char *cmd_line)
 | 
			
		||||
void fit_update_chosen(struct device_tree *tree, char *cmd_line)
 | 
			
		||||
{
 | 
			
		||||
	const char *path[] = { "chosen", NULL };
 | 
			
		||||
	DeviceTreeNode *node = dt_find_node(tree->root, path, NULL, NULL, 1);
 | 
			
		||||
	struct device_tree_node *node;
 | 
			
		||||
	node = dt_find_node(tree->root, path, NULL, NULL, 1);
 | 
			
		||||
 | 
			
		||||
	dt_add_string_prop(node, "bootargs", cmd_line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fit_add_ramdisk(DeviceTree *tree, void *ramdisk_addr, size_t ramdisk_size)
 | 
			
		||||
void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr,
 | 
			
		||||
		     size_t ramdisk_size)
 | 
			
		||||
{
 | 
			
		||||
	const char *path[] = { "chosen", NULL };
 | 
			
		||||
	DeviceTreeNode *node = dt_find_node(tree->root, path, NULL, NULL, 1);
 | 
			
		||||
	struct device_tree_node *node;
 | 
			
		||||
	node = dt_find_node(tree->root, path, NULL, NULL, 1);
 | 
			
		||||
 | 
			
		||||
	/* Warning: this assumes the ramdisk is currently located below 4GiB. */
 | 
			
		||||
	u32 start = (uintptr_t)ramdisk_addr;
 | 
			
		||||
@@ -210,49 +220,40 @@ void fit_add_ramdisk(DeviceTree *tree, void *ramdisk_addr, size_t ramdisk_size)
 | 
			
		||||
	dt_add_u32_prop(node, "linux,initrd-end", end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_reserve_map(uint64_t start, uint64_t end, void *data)
 | 
			
		||||
static void update_reserve_map(uint64_t start, uint64_t end,
 | 
			
		||||
			       struct device_tree *tree)
 | 
			
		||||
{
 | 
			
		||||
	DeviceTree *tree = (DeviceTree *)data;
 | 
			
		||||
	struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry));
 | 
			
		||||
 | 
			
		||||
	DeviceTreeReserveMapEntry *entry = xzalloc(sizeof(*entry));
 | 
			
		||||
	entry->start = start;
 | 
			
		||||
	entry->size = end - start;
 | 
			
		||||
 | 
			
		||||
	list_insert_after(&entry->list_node, &tree->reserve_map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct EntryParams
 | 
			
		||||
{
 | 
			
		||||
struct entry_params {
 | 
			
		||||
	unsigned addr_cells;
 | 
			
		||||
	unsigned size_cells;
 | 
			
		||||
	void *data;
 | 
			
		||||
} EntryParams;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint64_t max_range(unsigned size_cells)
 | 
			
		||||
{
 | 
			
		||||
	// Split up ranges who's sizes are too large to fit in #size-cells.
 | 
			
		||||
	// The largest value we can store isn't a power of two, so we'll round
 | 
			
		||||
	// down to make the math easier.
 | 
			
		||||
	/*
 | 
			
		||||
	 * Split up ranges who's sizes are too large to fit in #size-cells.
 | 
			
		||||
	 * The largest value we can store isn't a power of two, so we'll round
 | 
			
		||||
	 * down to make the math easier.
 | 
			
		||||
	 */
 | 
			
		||||
	return 0x1ULL << (size_cells * 32 - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void count_entries(u64 start, u64 end, void *pdata)
 | 
			
		||||
static void update_mem_property(u64 start, u64 end, struct entry_params *params)
 | 
			
		||||
{
 | 
			
		||||
	EntryParams *params = (EntryParams *)pdata;
 | 
			
		||||
	unsigned *count = (unsigned *)params->data;
 | 
			
		||||
	u64 size = end - start;
 | 
			
		||||
	u64 max_size = max_range(params->size_cells);
 | 
			
		||||
	*count += ALIGN_UP(size, max_size) / max_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_mem_property(u64 start, u64 end, void *pdata)
 | 
			
		||||
{
 | 
			
		||||
	EntryParams *params = (EntryParams *)pdata;
 | 
			
		||||
	u8 *data = (u8 *)params->data;
 | 
			
		||||
	u64 full_size = end - start;
 | 
			
		||||
	while (full_size) {
 | 
			
		||||
		const u64 max_size = max_range(params->size_cells);
 | 
			
		||||
		const u32 size = MIN(max_size, full_size);
 | 
			
		||||
		const u64 size = MIN(max_size, full_size);
 | 
			
		||||
 | 
			
		||||
		dt_write_int(data, start, params->addr_cells * sizeof(u32));
 | 
			
		||||
		data += params->addr_cells * sizeof(uint32_t);
 | 
			
		||||
@@ -265,109 +266,189 @@ static void update_mem_property(u64 start, u64 end, void *pdata)
 | 
			
		||||
	params->data = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_memory(DeviceTree *tree)
 | 
			
		||||
struct mem_map {
 | 
			
		||||
	struct memranges mem;
 | 
			
		||||
	struct memranges reserved;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool walk_memory_table(const struct range_entry *r, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	Ranges mem;
 | 
			
		||||
	Ranges reserved;
 | 
			
		||||
	DeviceTreeNode *node;
 | 
			
		||||
	struct mem_map *arg_map = arg;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Kernel likes its available memory areas at least 1MB
 | 
			
		||||
	 * aligned, let's trim the regions such that unaligned padding
 | 
			
		||||
	 * is added to reserved memory.
 | 
			
		||||
	 */
 | 
			
		||||
	if (range_entry_tag(r) == BM_MEM_RAM) {
 | 
			
		||||
		uint64_t new_start = ALIGN_UP(range_entry_base(r), 1 * MiB);
 | 
			
		||||
		uint64_t new_end = ALIGN_DOWN(range_entry_end(r), 1 * MiB);
 | 
			
		||||
 | 
			
		||||
		if (new_start != range_entry_base(r))
 | 
			
		||||
			memranges_insert(&arg_map->reserved,
 | 
			
		||||
					 range_entry_base(r),
 | 
			
		||||
					 new_start - range_entry_base(r),
 | 
			
		||||
					 BM_MEM_RESERVED);
 | 
			
		||||
 | 
			
		||||
		if (new_start != new_end)
 | 
			
		||||
			memranges_insert(&arg_map->mem, new_start,
 | 
			
		||||
					 new_end - new_start, BM_MEM_RAM);
 | 
			
		||||
 | 
			
		||||
		if (new_end != range_entry_end(r))
 | 
			
		||||
			memranges_insert(&arg_map->reserved, new_end,
 | 
			
		||||
					 range_entry_end(r) - new_end,
 | 
			
		||||
					 BM_MEM_RESERVED);
 | 
			
		||||
	} else
 | 
			
		||||
		memranges_insert(&arg_map->reserved, range_entry_base(r),
 | 
			
		||||
				 range_entry_size(r),
 | 
			
		||||
				 BM_MEM_RESERVED);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fit_add_compat_string(const char *str)
 | 
			
		||||
{
 | 
			
		||||
	struct compat_string_entry *compat_node;
 | 
			
		||||
 | 
			
		||||
	compat_node = xzalloc(sizeof(*compat_node));
 | 
			
		||||
	compat_node->compat_string = strdup(str);
 | 
			
		||||
 | 
			
		||||
	clean_compat_string((char *)compat_node->compat_string);
 | 
			
		||||
 | 
			
		||||
	list_insert_after(&compat_node->list_node, &compat_strings);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fit_update_memory(struct device_tree *tree)
 | 
			
		||||
{
 | 
			
		||||
	const struct range_entry *r;
 | 
			
		||||
	struct device_tree_node *node;
 | 
			
		||||
	u32 addr_cells = 1, size_cells = 1;
 | 
			
		||||
	struct mem_map map;
 | 
			
		||||
 | 
			
		||||
	printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n");
 | 
			
		||||
 | 
			
		||||
	dt_read_cell_props(tree->root, &addr_cells, &size_cells);
 | 
			
		||||
 | 
			
		||||
	// First remove all existing device_type="memory" nodes, then add ours.
 | 
			
		||||
	/*
 | 
			
		||||
	 * First remove all existing device_type="memory" nodes, then add ours.
 | 
			
		||||
	 */
 | 
			
		||||
	list_for_each(node, tree->root->children, list_node) {
 | 
			
		||||
		const char *devtype = dt_find_string_prop(node, "device_type");
 | 
			
		||||
		if (devtype && !strcmp(devtype, "memory"))
 | 
			
		||||
			list_remove(&node->list_node);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	node = xzalloc(sizeof(*node));
 | 
			
		||||
 | 
			
		||||
	node->name = "memory";
 | 
			
		||||
	list_insert_after(&node->list_node, &tree->root->children);
 | 
			
		||||
	dt_add_string_prop(node, "device_type", "memory");
 | 
			
		||||
	dt_add_string_prop(node, "device_type", (char *)"memory");
 | 
			
		||||
 | 
			
		||||
	// Read memory info from coreboot (ranges are merged automatically).
 | 
			
		||||
	ranges_init(&mem);
 | 
			
		||||
	ranges_init(&reserved);
 | 
			
		||||
	memranges_init_empty(&map.mem, NULL, 0);
 | 
			
		||||
	memranges_init_empty(&map.reserved, NULL, 0);
 | 
			
		||||
 | 
			
		||||
#define MEMORY_ALIGNMENT (1 << 20)
 | 
			
		||||
	for (int i = 0; i < lib_sysinfo.n_memranges; i++) {
 | 
			
		||||
		struct memrange *range = &lib_sysinfo.memrange[i];
 | 
			
		||||
		uint64_t start = range->base;
 | 
			
		||||
		uint64_t end = range->base + range->size;
 | 
			
		||||
	bootmem_walk_os_mem(walk_memory_table, &map);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Kernel likes its availabe memory areas at least 1MB
 | 
			
		||||
		 * aligned, let's trim the regions such that unaligned padding
 | 
			
		||||
		 * is added to reserved memory.
 | 
			
		||||
		 */
 | 
			
		||||
		if (range->type == CB_MEM_RAM) {
 | 
			
		||||
			uint64_t new_start = ALIGN_UP(start, MEMORY_ALIGNMENT);
 | 
			
		||||
			uint64_t new_end = ALIGN_DOWN(end, MEMORY_ALIGNMENT);
 | 
			
		||||
 | 
			
		||||
			if (new_start != start)
 | 
			
		||||
				ranges_add(&reserved, start, new_start);
 | 
			
		||||
 | 
			
		||||
			if (new_start != new_end)
 | 
			
		||||
				ranges_add(&mem, new_start, new_end);
 | 
			
		||||
 | 
			
		||||
			if (new_end != end)
 | 
			
		||||
				ranges_add(&reserved, new_end, end);
 | 
			
		||||
		} else {
 | 
			
		||||
			ranges_add(&reserved, start, end);
 | 
			
		||||
		}
 | 
			
		||||
	/* CBMEM regions are both carved out and explicitly reserved. */
 | 
			
		||||
	memranges_each_entry(r, &map.reserved) {
 | 
			
		||||
		update_reserve_map(range_entry_base(r), range_entry_end(r),
 | 
			
		||||
				   tree);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// CBMEM regions are both carved out and explicitly reserved.
 | 
			
		||||
	ranges_for_each(&reserved, &update_reserve_map, tree);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Count the amount of 'reg' entries we need (account for size limits).
 | 
			
		||||
	 */
 | 
			
		||||
	size_t count = 0;
 | 
			
		||||
	memranges_each_entry(r, &map.mem) {
 | 
			
		||||
		uint64_t size = range_entry_size(r);
 | 
			
		||||
		uint64_t max_size = max_range(size_cells);
 | 
			
		||||
		count += DIV_ROUND_UP(size, max_size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Count the amount of 'reg' entries we need (account for size limits).
 | 
			
		||||
	unsigned count = 0;
 | 
			
		||||
	EntryParams count_params = { addr_cells, size_cells, &count };
 | 
			
		||||
	ranges_for_each(&mem, &count_entries, &count_params);
 | 
			
		||||
 | 
			
		||||
	// Allocate the right amount of space and fill up the entries.
 | 
			
		||||
	/* Allocate the right amount of space and fill up the entries. */
 | 
			
		||||
	size_t length = count * (addr_cells + size_cells) * sizeof(u32);
 | 
			
		||||
	void *data = xmalloc(length);
 | 
			
		||||
	EntryParams add_params = { addr_cells, size_cells, data };
 | 
			
		||||
	ranges_for_each(&mem, &update_mem_property, &add_params);
 | 
			
		||||
 | 
			
		||||
	void *data = xzalloc(length);
 | 
			
		||||
 | 
			
		||||
	struct entry_params add_params = { addr_cells, size_cells, data };
 | 
			
		||||
	memranges_each_entry(r, &map.mem) {
 | 
			
		||||
		update_mem_property(range_entry_base(r), range_entry_end(r),
 | 
			
		||||
				    &add_params);
 | 
			
		||||
	}
 | 
			
		||||
	assert(add_params.data - data == length);
 | 
			
		||||
 | 
			
		||||
	// Assemble the final property and add it to the device tree.
 | 
			
		||||
	/* Assemble the final property and add it to the device tree. */
 | 
			
		||||
	dt_add_bin_prop(node, "reg", data, length);
 | 
			
		||||
 | 
			
		||||
	memranges_teardown(&map.mem);
 | 
			
		||||
	memranges_teardown(&map.reserved);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FitImageNode *fit_load(void *fit, char *cmd_line, DeviceTree **dt)
 | 
			
		||||
/*
 | 
			
		||||
 * Finds a compat string and updates the compat position and rank.
 | 
			
		||||
 * @param fdt_blob Pointer to FDT
 | 
			
		||||
 * @param config The current config node to operate on
 | 
			
		||||
 */
 | 
			
		||||
static void fit_update_compat(void *fdt_blob, struct fit_config_node *config)
 | 
			
		||||
{
 | 
			
		||||
	FdtHeader *header = (FdtHeader *)fit;
 | 
			
		||||
	FitImageNode *image;
 | 
			
		||||
	FitConfigNode *config;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct compat_string_entry *compat_node;
 | 
			
		||||
	struct fdt_header *fdt_header = (struct fdt_header *)fdt_blob;
 | 
			
		||||
	uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset);
 | 
			
		||||
	size_t i = 0;
 | 
			
		||||
 | 
			
		||||
	printf("Loading FIT.\n");
 | 
			
		||||
	if (!fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
 | 
			
		||||
		list_for_each(compat_node, compat_strings, list_node) {
 | 
			
		||||
			int pos = fit_check_compat(&config->compat,
 | 
			
		||||
						   compat_node->compat_string);
 | 
			
		||||
			if (pos >= 0) {
 | 
			
		||||
				config->compat_pos = pos;
 | 
			
		||||
				config->compat_rank = i;
 | 
			
		||||
				config->compat_string =
 | 
			
		||||
					compat_node->compat_string;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if (betohl(header->magic) != FdtMagic) {
 | 
			
		||||
		printf("Bad FIT header magic value 0x%08x.\n",
 | 
			
		||||
			betohl(header->magic));
 | 
			
		||||
struct fit_config_node *fit_load(void *fit)
 | 
			
		||||
{
 | 
			
		||||
	struct fdt_header *header = (struct fdt_header *)fit;
 | 
			
		||||
	struct fit_image_node *image;
 | 
			
		||||
	struct fit_config_node *config;
 | 
			
		||||
	struct compat_string_entry *compat_node;
 | 
			
		||||
 | 
			
		||||
	printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit);
 | 
			
		||||
 | 
			
		||||
	if (be32toh(header->magic) != FDT_HEADER_MAGIC) {
 | 
			
		||||
		printk(BIOS_ERR, "FIT: Bad header magic value 0x%08x.\n",
 | 
			
		||||
		       be32toh(header->magic));
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DeviceTree *tree = fdt_unflatten(fit);
 | 
			
		||||
	struct device_tree *tree = fdt_unflatten(fit);
 | 
			
		||||
 | 
			
		||||
	const char *default_config_name = NULL;
 | 
			
		||||
	FitConfigNode *default_config = NULL;
 | 
			
		||||
	FitConfigNode *compat_config = NULL;
 | 
			
		||||
	struct fit_config_node *default_config = NULL;
 | 
			
		||||
	struct fit_config_node *compat_config = NULL;
 | 
			
		||||
 | 
			
		||||
	fit_unpack(tree, &default_config_name);
 | 
			
		||||
 | 
			
		||||
	// List the images we found.
 | 
			
		||||
	/* List the images we found. */
 | 
			
		||||
	list_for_each(image, image_nodes, list_node)
 | 
			
		||||
		printf("Image %s has %d bytes.\n", image->name, image->size);
 | 
			
		||||
		printk(BIOS_DEBUG, "FIT: Image %s has %d bytes.\n", image->name,
 | 
			
		||||
		       image->size);
 | 
			
		||||
 | 
			
		||||
	fit_add_default_compats();
 | 
			
		||||
	printf("Compat preference:");
 | 
			
		||||
	for (i = 0; i < num_fit_kernel_compat; i++)
 | 
			
		||||
		printf(" %s", fit_kernel_compat[i]);
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	// Process and list the configs.
 | 
			
		||||
	fit_add_default_compat_strings();
 | 
			
		||||
 | 
			
		||||
	printk(BIOS_DEBUG, "FIT: Compat preference "
 | 
			
		||||
	       "(lowest to highest priority) :");
 | 
			
		||||
 | 
			
		||||
	list_for_each(compat_node, compat_strings, list_node) {
 | 
			
		||||
		printk(BIOS_DEBUG, " %s", compat_node->compat_string);
 | 
			
		||||
	}
 | 
			
		||||
	printk(BIOS_DEBUG, "\n");
 | 
			
		||||
	/* Process and list the configs. */
 | 
			
		||||
	list_for_each(config, config_nodes, list_node) {
 | 
			
		||||
		if (config->kernel)
 | 
			
		||||
			config->kernel_node = find_image(config->kernel);
 | 
			
		||||
@@ -376,112 +457,82 @@ FitImageNode *fit_load(void *fit, char *cmd_line, DeviceTree **dt)
 | 
			
		||||
		if (config->ramdisk)
 | 
			
		||||
			config->ramdisk_node = find_image(config->ramdisk);
 | 
			
		||||
 | 
			
		||||
		if (config->ramdisk_node &&
 | 
			
		||||
		    config->ramdisk_node->compression < 0) {
 | 
			
		||||
			printk(BIOS_WARNING, "WARN: Ramdisk is compressed with "
 | 
			
		||||
			       "an unsupported algorithm, discarding config %s."
 | 
			
		||||
			       "\n", config->name);
 | 
			
		||||
			list_remove(&config->list_node);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!config->kernel_node ||
 | 
			
		||||
				(config->fdt && !config->fdt_node)) {
 | 
			
		||||
			printf("Missing image, discarding config %s.\n",
 | 
			
		||||
				config->name);
 | 
			
		||||
		    (config->fdt && !config->fdt_node)) {
 | 
			
		||||
			printk(BIOS_DEBUG, "FIT: Missing image, discarding "
 | 
			
		||||
			       "config %s.\n", config->name);
 | 
			
		||||
			list_remove(&config->list_node);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (config->fdt_node) {
 | 
			
		||||
			if (config->fdt_node->compression != CompressionNone) {
 | 
			
		||||
				printf("FDT compression not yet supported, "
 | 
			
		||||
				       "skipping config %s.\n", config->name);
 | 
			
		||||
			if (config->fdt_node->compression !=
 | 
			
		||||
			    CBFS_COMPRESS_NONE) {
 | 
			
		||||
				printk(BIOS_DEBUG,
 | 
			
		||||
				       "FIT: FDT compression not yet supported,"
 | 
			
		||||
				       " skipping config %s.\n", config->name);
 | 
			
		||||
				list_remove(&config->list_node);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void *fdt_blob = config->fdt_node->data;
 | 
			
		||||
			FdtHeader *fdt_header = (FdtHeader *)fdt_blob;
 | 
			
		||||
			uint32_t fdt_offset =
 | 
			
		||||
				betohl(fdt_header->structure_offset);
 | 
			
		||||
			config->compat_pos = -1;
 | 
			
		||||
			config->compat_rank = -1;
 | 
			
		||||
			if (!fdt_find_compat(fdt_blob, fdt_offset,
 | 
			
		||||
					    &config->compat)) {
 | 
			
		||||
				for (i = 0; i < num_fit_kernel_compat; i++) {
 | 
			
		||||
					int pos = fit_check_compat(
 | 
			
		||||
							&config->compat,
 | 
			
		||||
							fit_kernel_compat[i]);
 | 
			
		||||
					if (pos >= 0) {
 | 
			
		||||
						config->compat_pos = pos;
 | 
			
		||||
						config->compat_rank = i;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printf("Config %s", config->name);
 | 
			
		||||
			fit_update_compat(config->fdt_node->data, config);
 | 
			
		||||
		}
 | 
			
		||||
		printk(BIOS_DEBUG, "FIT: config %s", config->name);
 | 
			
		||||
		if (default_config_name &&
 | 
			
		||||
				!strcmp(config->name, default_config_name)) {
 | 
			
		||||
			printf(" (default)");
 | 
			
		||||
		    !strcmp(config->name, default_config_name)) {
 | 
			
		||||
			printk(BIOS_DEBUG, " (default)");
 | 
			
		||||
			default_config = config;
 | 
			
		||||
		}
 | 
			
		||||
		printf(", kernel %s", config->kernel);
 | 
			
		||||
		if (config->fdt)
 | 
			
		||||
			printf(", fdt %s", config->fdt);
 | 
			
		||||
			printk(BIOS_DEBUG, ", fdt %s", config->fdt);
 | 
			
		||||
		if (config->ramdisk)
 | 
			
		||||
			printf(", ramdisk %s", config->ramdisk);
 | 
			
		||||
			printk(BIOS_DEBUG, ", ramdisk %s", config->ramdisk);
 | 
			
		||||
		if (config->compat.name) {
 | 
			
		||||
			printf(", compat");
 | 
			
		||||
			printk(BIOS_DEBUG, ", compat");
 | 
			
		||||
			int bytes = config->compat.size;
 | 
			
		||||
			const char *compat_str = config->compat.data;
 | 
			
		||||
			for (int pos = 0; bytes && compat_str[0]; pos++) {
 | 
			
		||||
				printf(" %s", compat_str);
 | 
			
		||||
				printk(BIOS_DEBUG, " %s", compat_str);
 | 
			
		||||
				if (pos == config->compat_pos)
 | 
			
		||||
					printf(" (match)");
 | 
			
		||||
					printk(BIOS_DEBUG, " (match)");
 | 
			
		||||
				int len = strlen(compat_str) + 1;
 | 
			
		||||
				compat_str += len;
 | 
			
		||||
				bytes -= len;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (config->compat_rank >= 0 && (!compat_config ||
 | 
			
		||||
			    config->compat_rank < compat_config->compat_rank))
 | 
			
		||||
			    config->compat_rank > compat_config->compat_rank))
 | 
			
		||||
				compat_config = config;
 | 
			
		||||
		}
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		printk(BIOS_DEBUG, "\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FitConfigNode *to_boot = NULL;
 | 
			
		||||
	struct fit_config_node *to_boot = NULL;
 | 
			
		||||
	if (compat_config) {
 | 
			
		||||
		to_boot = compat_config;
 | 
			
		||||
		printf("Choosing best match %s for compat %s.\n",
 | 
			
		||||
		       to_boot->name, fit_kernel_compat[to_boot->compat_rank]);
 | 
			
		||||
		printk(BIOS_INFO, "FIT: Choosing best match %s for compat "
 | 
			
		||||
		       "%s.\n", to_boot->name, to_boot->compat_string);
 | 
			
		||||
	} else if (default_config) {
 | 
			
		||||
		to_boot = default_config;
 | 
			
		||||
		printf("No match, choosing default %s.\n", to_boot->name);
 | 
			
		||||
		printk(BIOS_INFO, "FIT: No match, choosing default %s.\n",
 | 
			
		||||
		       to_boot->name);
 | 
			
		||||
	} else {
 | 
			
		||||
		printf("No compatible or default configs. Giving up.\n");
 | 
			
		||||
		// We're leaking memory here, but at this point we're beyond
 | 
			
		||||
		// saving anyway.
 | 
			
		||||
		printk(BIOS_ERR, "FIT: No compatible or default configs. "
 | 
			
		||||
		       "Giving up.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (to_boot->fdt_node) {
 | 
			
		||||
		*dt = fdt_unflatten(to_boot->fdt_node->data);
 | 
			
		||||
		if (!*dt) {
 | 
			
		||||
			printf("Failed to unflatten the kernel's fdt.\n");
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Update only if non-NULL cmd line */
 | 
			
		||||
		if (cmd_line)
 | 
			
		||||
			update_chosen(*dt, cmd_line);
 | 
			
		||||
 | 
			
		||||
		update_memory(*dt);
 | 
			
		||||
 | 
			
		||||
		if (to_boot->ramdisk_node) {
 | 
			
		||||
			if (to_boot->ramdisk_node->compression
 | 
			
		||||
					!= CompressionNone) {
 | 
			
		||||
				printf("Ramdisk compression not supported.\n");
 | 
			
		||||
				return NULL;
 | 
			
		||||
			}
 | 
			
		||||
			fit_add_ramdisk(*dt, to_boot->ramdisk_node->data,
 | 
			
		||||
					to_boot->ramdisk_node->size);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return to_boot->kernel_node;
 | 
			
		||||
	return to_boot;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user