fit: Add device tree compression

This patch adds support for compressing individual device trees in the
FIT image. In order to make this efficient, we'll have to pull the
compatible property out of the FDT and store it directly in the config
node of the FIT image, so that we don't have to scan (and therefore
decompress) every single FDT on boot. Device tree compression is only
supported for FIT images that have this external compatible property.
For older images with no compression, we still support fallback to
scanning the FDT for the property.

This patch was adapted from depthcharge's http://crosreview.com/1553458

Change-Id: Ifcb6997782c480c8ef6692df17b66ad96264e623
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/32872
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Julius Werner 2019-05-16 16:04:19 -07:00 committed by Patrick Georgi
parent 2855a0c14d
commit fec4206299
2 changed files with 55 additions and 25 deletions

View File

@ -136,6 +136,8 @@ static void config_node(struct device_tree_node *node)
config->fdt = find_image(prop->prop.data); config->fdt = find_image(prop->prop.data);
else if (!strcmp("ramdisk", prop->prop.name)) else if (!strcmp("ramdisk", prop->prop.name))
config->ramdisk = find_image(prop->prop.data); config->ramdisk = find_image(prop->prop.data);
else if (!strcmp("compatible", prop->prop.name))
config->compat = prop->prop;
} }
list_insert_after(&config->list_node, &config_nodes); list_insert_after(&config->list_node, &config_nodes);
@ -391,23 +393,34 @@ void fit_update_memory(struct device_tree *tree)
*/ */
static int fit_update_compat(struct fit_config_node *config) static int fit_update_compat(struct fit_config_node *config)
{ {
// If there was no "compatible" property in config node, this is a
// legacy FIT image. Must extract compat prop from FDT itself.
if (!config->compat.name) {
void *fdt_blob = config->fdt->data;
const struct fdt_header *fdt_header = fdt_blob;
uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset);
if (config->fdt->compression != CBFS_COMPRESS_NONE) { if (config->fdt->compression != CBFS_COMPRESS_NONE) {
printk(BIOS_ERR, printk(BIOS_ERR,
"FDT compression not yet supported, skipping %s.\n", "ERROR: config %s has a compressed FDT without "
"external compatible property, skipping.\n",
config->name); config->name);
return -1; return -1;
} }
void *fdt_blob = config->fdt->data; if (fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) {
struct compat_string_entry *compat_node; printk(BIOS_ERR,
const struct fdt_header *fdt_header = "ERROR: Can't find compat string in FDT %s "
(const struct fdt_header *)fdt_blob; "for config %s, skipping.\n",
uint32_t fdt_offset = be32_to_cpu(fdt_header->structure_offset); config->fdt->name, config->name);
size_t i = 0; return -1;
}
}
config->compat_pos = -1; config->compat_pos = -1;
config->compat_rank = -1; config->compat_rank = -1;
if (!fdt_find_compat(fdt_blob, fdt_offset, &config->compat)) { size_t i = 0;
struct compat_string_entry *compat_node;
list_for_each(compat_node, compat_strings, list_node) { list_for_each(compat_node, compat_strings, list_node) {
int pos = fit_check_compat(&config->compat, int pos = fit_check_compat(&config->compat,
compat_node->compat_string); compat_node->compat_string);
@ -420,7 +433,6 @@ static int fit_update_compat(struct fit_config_node *config)
} }
i++; i++;
} }
}
return 0; return 0;
} }

View File

@ -96,6 +96,24 @@ static bool extract(struct region *region, struct fit_image_node *node)
return false; return false;
} }
static struct device_tree *unpack_fdt(struct fit_image_node *image_node)
{
void *data = image_node->data;
if (image_node->compression != CBFS_COMPRESS_NONE) {
/* TODO: This is an ugly heuristic for how much the size will
expand on decompression, fix once FIT images support storing
the real uncompressed size. */
struct region r = { .offset = 0, .size = image_node->size * 5 };
data = malloc(r.size);
r.offset = (uintptr_t)data;
if (!data || extract(&r, image_node))
return NULL;
}
return fdt_unflatten(data);
}
/** /**
* Add coreboot tables, CBMEM information and optional board specific strapping * Add coreboot tables, CBMEM information and optional board specific strapping
* IDs to the device tree loaded via FIT. * IDs to the device tree loaded via FIT.
@ -181,7 +199,7 @@ void fit_payload(struct prog *payload)
return; return;
} }
dt = fdt_unflatten(config->fdt->data); dt = unpack_fdt(config->fdt);
if (!dt) { if (!dt) {
printk(BIOS_ERR, "ERROR: Failed to unflatten the FDT.\n"); printk(BIOS_ERR, "ERROR: Failed to unflatten the FDT.\n");
rdev_munmap(prog_rdev(payload), data); rdev_munmap(prog_rdev(payload), data);