fit_payload.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 #include <commonlib/bsd/compression.h> 4 #include <console/console.h> 5 #include <bootmem.h> 6 #include <cbmem.h> 7 #include <device/resource.h> 8 #include <stdlib.h> 9 #include <commonlib/region.h> 10 #include <fit.h> 11 #include <program_loading.h> 12 #include <timestamp.h> 13 #include <string.h> 14 #include <lib.h> 15 #include <boardid.h> 16 17 /* Pack the device_tree and place it at given position. */ 18 static void pack_fdt(struct region *fdt, struct device_tree *dt) 19 { 20 printk(BIOS_INFO, "FIT: Flattening FDT to %p\n", 21 (void *)fdt->offset); 22 23 dt_flatten(dt, (void *)fdt->offset); 24 prog_segment_loaded(fdt->offset, fdt->size, 0); 25 } 26 27 /** 28 * Extract a node to given regions. 29 * Returns true on error, false on success. 30 */ 31 static bool extract(struct region *region, struct fit_image_node *node) 32 { 33 void *dst = (void *)region->offset; 34 const char *comp_name; 35 size_t true_size = 0; 36 37 if (node->size == 0) { 38 printk(BIOS_ERR, "The %s size is 0\n", node->name); 39 return true; 40 } 41 42 switch (node->compression) { 43 case CBFS_COMPRESS_NONE: 44 comp_name = "Relocating uncompressed"; 45 break; 46 case CBFS_COMPRESS_LZMA: 47 comp_name = "Decompressing LZMA"; 48 break; 49 case CBFS_COMPRESS_LZ4: 50 comp_name = "Decompressing LZ4"; 51 break; 52 default: 53 printk(BIOS_ERR, "Unsupported compression\n"); 54 return true; 55 } 56 57 printk(BIOS_INFO, "FIT: %s %s to %p\n", comp_name, node->name, dst); 58 59 switch (node->compression) { 60 case CBFS_COMPRESS_NONE: 61 memcpy(dst, node->data, node->size); 62 true_size = node->size; 63 break; 64 case CBFS_COMPRESS_LZMA: 65 timestamp_add_now(TS_ULZMA_START); 66 true_size = ulzman(node->data, node->size, dst, region->size); 67 timestamp_add_now(TS_ULZMA_END); 68 break; 69 case CBFS_COMPRESS_LZ4: 70 timestamp_add_now(TS_ULZ4F_START); 71 true_size = ulz4fn(node->data, node->size, dst, region->size); 72 timestamp_add_now(TS_ULZ4F_END); 73 break; 74 default: 75 return true; 76 } 77 78 if (!true_size) { 79 printk(BIOS_ERR, "%s decompression failed!\n", 80 comp_name); 81 return true; 82 } 83 84 return false; 85 } 86 87 static struct device_tree *unpack_fdt(struct fit_image_node *image_node) 88 { 89 void *data = image_node->data; 90 91 if (image_node->compression != CBFS_COMPRESS_NONE) { 92 /* TODO: This is an ugly heuristic for how much the size will 93 expand on decompression, fix once FIT images support storing 94 the real uncompressed size. */ 95 struct region r = { .offset = 0, .size = image_node->size * 5 }; 96 data = malloc(r.size); 97 r.offset = (uintptr_t)data; 98 if (!data || extract(&r, image_node)) 99 return NULL; 100 } 101 102 return fdt_unflatten(data); 103 } 104 105 /** 106 * Add coreboot tables, CBMEM information and optional board specific strapping 107 * IDs to the device tree loaded via FIT. 108 */ 109 static void add_cb_fdt_data(struct device_tree *tree) 110 { 111 u32 addr_cells = 1, size_cells = 1; 112 u64 reg_addrs[2], reg_sizes[2]; 113 void *baseptr; 114 size_t size; 115 116 static const char *firmware_path[] = {"firmware", NULL}; 117 struct device_tree_node *firmware_node = dt_find_node(tree->root, 118 firmware_path, &addr_cells, &size_cells, 1); 119 120 /* Need to add 'ranges' to the intermediate node to make 'reg' work. */ 121 dt_add_bin_prop(firmware_node, "ranges", NULL, 0); 122 123 static const char *coreboot_path[] = {"coreboot", NULL}; 124 struct device_tree_node *coreboot_node = dt_find_node(firmware_node, 125 coreboot_path, &addr_cells, &size_cells, 1); 126 127 dt_add_string_prop(coreboot_node, "compatible", "coreboot"); 128 129 /* Fetch CB tables from cbmem */ 130 void *cbtable = cbmem_find(CBMEM_ID_CBTABLE); 131 if (!cbtable) { 132 printk(BIOS_WARNING, "FIT: No coreboot table found!\n"); 133 return; 134 } 135 136 /* First 'reg' address range is the coreboot table. */ 137 const struct lb_header *header = cbtable; 138 reg_addrs[0] = (uintptr_t)header; 139 reg_sizes[0] = header->header_bytes + header->table_bytes; 140 141 /* Second is the CBMEM area (which usually includes the coreboot 142 table). */ 143 if (cbmem_get_region(&baseptr, &size)) { 144 printk(BIOS_WARNING, "FIT: CBMEM pointer/size not found!\n"); 145 return; 146 } 147 148 reg_addrs[1] = (uintptr_t)baseptr; 149 reg_sizes[1] = size; 150 151 dt_add_reg_prop(coreboot_node, reg_addrs, reg_sizes, 2, addr_cells, 152 size_cells); 153 154 /* Expose board ID, SKU ID, and RAM code to payload.*/ 155 if (board_id() != UNDEFINED_STRAPPING_ID) 156 dt_add_u32_prop(coreboot_node, "board-id", board_id()); 157 158 if (sku_id() != UNDEFINED_STRAPPING_ID) 159 dt_add_u32_prop(coreboot_node, "sku-id", sku_id()); 160 161 if (ram_code() != UNDEFINED_STRAPPING_ID) 162 dt_add_u32_prop(coreboot_node, "ram-code", ram_code()); 163 } 164 165 /* 166 * Parse the uImage FIT, choose a configuration and extract images. 167 */ 168 void fit_payload(struct prog *payload, void *data) 169 { 170 struct device_tree *dt = NULL; 171 struct region kernel = {0}, fdt = {0}, initrd = {0}; 172 173 printk(BIOS_INFO, "FIT: Examine payload %s\n", payload->name); 174 175 struct fit_config_node *config = fit_load(data); 176 177 if (!config) { 178 printk(BIOS_ERR, "Could not load FIT\n"); 179 return; 180 } 181 182 dt = unpack_fdt(config->fdt); 183 if (!dt) { 184 printk(BIOS_ERR, "Failed to unflatten the FDT.\n"); 185 return; 186 } 187 188 struct fit_overlay_chain *chain; 189 list_for_each(chain, config->overlays, list_node) { 190 struct device_tree *overlay = unpack_fdt(chain->overlay); 191 if (!overlay || dt_apply_overlay(dt, overlay)) { 192 printk(BIOS_ERR, "Failed to apply overlay %s!\n", 193 chain->overlay->name); 194 } 195 } 196 197 dt_apply_fixups(dt); 198 199 /* Insert coreboot specific information */ 200 add_cb_fdt_data(dt); 201 202 /* Update device_tree */ 203 #if defined(CONFIG_LINUX_COMMAND_LINE) 204 fit_update_chosen(dt, (char *)CONFIG_LINUX_COMMAND_LINE); 205 #endif 206 fit_update_memory(dt); 207 208 /* Collect infos for fit_payload_arch */ 209 kernel.size = config->kernel->size; 210 fdt.size = dt_flat_size(dt); 211 initrd.size = config->ramdisk ? config->ramdisk->size : 0; 212 213 /* Invoke arch specific payload placement and fixups */ 214 if (!fit_payload_arch(payload, config, &kernel, &fdt, &initrd)) { 215 printk(BIOS_ERR, "Failed to find free memory region\n"); 216 bootmem_dump_ranges(); 217 return; 218 } 219 220 /* Update ramdisk location in FDT */ 221 if (config->ramdisk) 222 fit_add_ramdisk(dt, (void *)initrd.offset, initrd.size); 223 224 /* Repack FDT for handoff to kernel */ 225 pack_fdt(&fdt, dt); 226 227 if (config->ramdisk && 228 extract(&initrd, config->ramdisk)) { 229 printk(BIOS_ERR, "Failed to extract initrd\n"); 230 prog_set_entry(payload, NULL, NULL); 231 return; 232 } 233 234 timestamp_add_now(TS_KERNEL_DECOMPRESSION); 235 236 if (extract(&kernel, config->kernel)) { 237 printk(BIOS_ERR, "Failed to extract kernel\n"); 238 prog_set_entry(payload, NULL, NULL); 239 return; 240 } 241 242 timestamp_add_now(TS_KERNEL_START); 243 }