1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/bsd/compression.h>
4 #include <console/console.h>
7 #include <device/resource.h>
9 #include <commonlib/region.h>
11 #include <program_loading.h>
12 #include <timestamp.h>
17 /* Pack the device_tree and place it at given position. */
18 static void pack_fdt(struct region
*fdt
, struct device_tree
*dt
)
20 printk(BIOS_INFO
, "FIT: Flattening FDT to %p\n",
23 dt_flatten(dt
, (void *)fdt
->offset
);
24 prog_segment_loaded(fdt
->offset
, fdt
->size
, 0);
28 * Extract a node to given regions.
29 * Returns true on error, false on success.
31 static bool extract(struct region
*region
, struct fit_image_node
*node
)
33 void *dst
= (void *)region
->offset
;
34 const char *comp_name
;
37 if (node
->size
== 0) {
38 printk(BIOS_ERR
, "The %s size is 0\n", node
->name
);
42 switch (node
->compression
) {
43 case CBFS_COMPRESS_NONE
:
44 comp_name
= "Relocating uncompressed";
46 case CBFS_COMPRESS_LZMA
:
47 comp_name
= "Decompressing LZMA";
49 case CBFS_COMPRESS_LZ4
:
50 comp_name
= "Decompressing LZ4";
53 printk(BIOS_ERR
, "Unsupported compression\n");
57 printk(BIOS_INFO
, "FIT: %s %s to %p\n", comp_name
, node
->name
, dst
);
59 switch (node
->compression
) {
60 case CBFS_COMPRESS_NONE
:
61 memcpy(dst
, node
->data
, node
->size
);
62 true_size
= node
->size
;
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
);
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
);
79 printk(BIOS_ERR
, "%s decompression failed!\n",
87 static struct device_tree
*unpack_fdt(struct fit_image_node
*image_node
)
89 void *data
= image_node
->data
;
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
))
102 return fdt_unflatten(data
);
106 * Add coreboot tables, CBMEM information and optional board specific strapping
107 * IDs to the device tree loaded via FIT.
109 static void add_cb_fdt_data(struct device_tree
*tree
)
111 u32 addr_cells
= 1, size_cells
= 1;
112 u64 reg_addrs
[2], reg_sizes
[2];
113 void *baseptr
= NULL
;
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);
120 /* Need to add 'ranges' to the intermediate node to make 'reg' work. */
121 dt_add_bin_prop(firmware_node
, "ranges", NULL
, 0);
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);
127 dt_add_string_prop(coreboot_node
, "compatible", "coreboot");
129 /* Fetch CB tables from cbmem */
130 void *cbtable
= cbmem_find(CBMEM_ID_CBTABLE
);
132 printk(BIOS_WARNING
, "FIT: No coreboot table found!\n");
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
;
141 /* Second is the CBMEM area (which usually includes the coreboot
143 cbmem_get_region(&baseptr
, &size
);
144 if (!baseptr
|| size
== 0) {
145 printk(BIOS_WARNING
, "FIT: CBMEM pointer/size not found!\n");
149 reg_addrs
[1] = (uintptr_t)baseptr
;
152 dt_add_reg_prop(coreboot_node
, reg_addrs
, reg_sizes
, 2, addr_cells
,
155 /* Expose board ID, SKU ID, and RAM code to payload.*/
156 if (board_id() != UNDEFINED_STRAPPING_ID
)
157 dt_add_u32_prop(coreboot_node
, "board-id", board_id());
159 if (sku_id() != UNDEFINED_STRAPPING_ID
)
160 dt_add_u32_prop(coreboot_node
, "sku-id", sku_id());
162 if (ram_code() != UNDEFINED_STRAPPING_ID
)
163 dt_add_u32_prop(coreboot_node
, "ram-code", ram_code());
167 * Parse the uImage FIT, choose a configuration and extract images.
169 void fit_payload(struct prog
*payload
, void *data
)
171 struct device_tree
*dt
= NULL
;
172 struct region kernel
= {0}, fdt
= {0}, initrd
= {0};
174 printk(BIOS_INFO
, "FIT: Examine payload %s\n", payload
->name
);
176 struct fit_config_node
*config
= fit_load(data
);
179 printk(BIOS_ERR
, "Could not load FIT\n");
183 dt
= unpack_fdt(config
->fdt
);
185 printk(BIOS_ERR
, "Failed to unflatten the FDT.\n");
189 struct fit_overlay_chain
*chain
;
190 list_for_each(chain
, config
->overlays
, list_node
) {
191 struct device_tree
*overlay
= unpack_fdt(chain
->overlay
);
192 if (!overlay
|| dt_apply_overlay(dt
, overlay
)) {
193 printk(BIOS_ERR
, "Failed to apply overlay %s!\n",
194 chain
->overlay
->name
);
200 /* Insert coreboot specific information */
203 /* Update device_tree */
204 #if defined(CONFIG_LINUX_COMMAND_LINE)
205 fit_update_chosen(dt
, (char *)CONFIG_LINUX_COMMAND_LINE
);
207 fit_update_memory(dt
);
209 /* Collect infos for fit_payload_arch */
210 kernel
.size
= config
->kernel
->size
;
211 fdt
.size
= dt_flat_size(dt
);
212 initrd
.size
= config
->ramdisk
? config
->ramdisk
->size
: 0;
214 /* Invoke arch specific payload placement and fixups */
215 if (!fit_payload_arch(payload
, config
, &kernel
, &fdt
, &initrd
)) {
216 printk(BIOS_ERR
, "Failed to find free memory region\n");
217 bootmem_dump_ranges();
221 /* Update ramdisk location in FDT */
223 fit_add_ramdisk(dt
, (void *)initrd
.offset
, initrd
.size
);
225 /* Repack FDT for handoff to kernel */
228 if (config
->ramdisk
&&
229 extract(&initrd
, config
->ramdisk
)) {
230 printk(BIOS_ERR
, "Failed to extract initrd\n");
231 prog_set_entry(payload
, NULL
, NULL
);
235 timestamp_add_now(TS_KERNEL_DECOMPRESSION
);
237 if (extract(&kernel
, config
->kernel
)) {
238 printk(BIOS_ERR
, "Failed to extract kernel\n");
239 prog_set_entry(payload
, NULL
, NULL
);
243 timestamp_add_now(TS_KERNEL_START
);