1 /* Taken from depthcharge: src/boot/fit.c */
2 /* SPDX-License-Identifier: GPL-2.0-or-later */
5 #include <console/console.h>
10 #include <program_loading.h>
14 #include <commonlib/stdlib.h>
17 static struct list_node image_nodes
;
18 static struct list_node config_nodes
;
19 static struct list_node compat_strings
;
21 struct compat_string_entry
{
22 const char *compat_string
;
23 struct list_node list_node
;
26 /* Convert string to lowercase and replace '_' and spaces with '-'. */
27 static char *clean_compat_string(char *str
)
29 for (size_t i
= 0; i
< strlen(str
); i
++) {
30 str
[i
] = tolower(str
[i
]);
31 if (str
[i
] == '_' || str
[i
] == ' ')
38 static void fit_add_default_compat_strings(void)
40 char compat_string
[80] = {};
42 if ((board_id() != UNDEFINED_STRAPPING_ID
) &&
43 (sku_id() != UNDEFINED_STRAPPING_ID
)) {
44 snprintf(compat_string
, sizeof(compat_string
),
45 "%s,%s-rev%u-sku%u", CONFIG_MAINBOARD_VENDOR
,
46 CONFIG_MAINBOARD_PART_NUMBER
, board_id(), sku_id());
48 fit_add_compat_string(compat_string
);
51 if (board_id() != UNDEFINED_STRAPPING_ID
) {
52 snprintf(compat_string
, sizeof(compat_string
), "%s,%s-rev%u",
53 CONFIG_MAINBOARD_VENDOR
, CONFIG_MAINBOARD_PART_NUMBER
,
56 fit_add_compat_string(compat_string
);
59 if (sku_id() != UNDEFINED_STRAPPING_ID
) {
60 snprintf(compat_string
, sizeof(compat_string
), "%s,%s-sku%u",
61 CONFIG_MAINBOARD_VENDOR
, CONFIG_MAINBOARD_PART_NUMBER
,
64 fit_add_compat_string(compat_string
);
67 snprintf(compat_string
, sizeof(compat_string
), "%s,%s",
68 CONFIG_MAINBOARD_VENDOR
, CONFIG_MAINBOARD_PART_NUMBER
);
70 fit_add_compat_string(compat_string
);
73 static struct fit_image_node
*find_image(const char *name
)
75 struct fit_image_node
*image
;
76 list_for_each(image
, image_nodes
, list_node
) {
77 if (!strcmp(image
->name
, name
))
80 printk(BIOS_ERR
, "Cannot find image node %s!\n", name
);
84 static struct fit_image_node
*find_image_with_overlays(const char *name
,
85 int bytes
, struct list_node
*prev
)
87 struct fit_image_node
*base
= find_image(name
);
91 int len
= strnlen(name
, bytes
) + 1;
95 struct fit_overlay_chain
*next
= xzalloc(sizeof(*next
));
96 next
->overlay
= find_image(name
);
99 list_insert_after(&next
->list_node
, prev
);
100 prev
= &next
->list_node
;
101 len
= strnlen(name
, bytes
) + 1;
109 static void image_node(struct device_tree_node
*node
)
111 struct fit_image_node
*image
= xzalloc(sizeof(*image
));
113 image
->compression
= CBFS_COMPRESS_NONE
;
114 image
->name
= node
->name
;
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
;
129 image
->compression
= -1;
133 list_insert_after(&image
->list_node
, &image_nodes
);
136 static void config_node(struct device_tree_node
*node
)
138 struct fit_config_node
*config
= xzalloc(sizeof(*config
));
139 config
->name
= node
->name
;
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
;
154 list_insert_after(&config
->list_node
, &config_nodes
);
157 static void fit_unpack(struct device_tree
*tree
, const char **default_config
)
159 struct device_tree_node
*child
;
160 struct device_tree_node
*images
= dt_find_node_by_path(tree
, "/images",
163 list_for_each(child
, images
->children
, list_node
)
166 struct device_tree_node
*configs
= dt_find_node_by_path(tree
,
167 "/configurations", NULL
, NULL
, 0);
169 *default_config
= dt_find_string_prop(configs
, "default");
170 list_for_each(child
, configs
->children
, list_node
)
175 static int fdt_find_compat(const void *blob
, uint32_t start_offset
,
176 struct fdt_property
*prop
)
178 int offset
= start_offset
;
181 size
= fdt_node_name(blob
, offset
, NULL
);
186 while ((size
= fdt_next_property(blob
, offset
, prop
))) {
187 if (!strcmp("compatible", prop
->name
))
197 static int fit_check_compat(struct fdt_property
*compat_prop
,
198 const char *compat_name
)
200 int bytes
= compat_prop
->size
;
201 const char *compat_str
= compat_prop
->data
;
203 for (int pos
= 0; bytes
&& compat_str
[0]; pos
++) {
204 if (!strncmp(compat_str
, compat_name
, bytes
))
206 int len
= strlen(compat_str
) + 1;
213 void fit_update_chosen(struct device_tree
*tree
, const char *cmd_line
)
215 const char *path
[] = { "chosen", NULL
};
216 struct device_tree_node
*node
;
217 node
= dt_find_node(tree
->root
, path
, NULL
, NULL
, 1);
219 dt_add_string_prop(node
, "bootargs", cmd_line
);
222 void fit_add_ramdisk(struct device_tree
*tree
, void *ramdisk_addr
,
225 const char *path
[] = { "chosen", NULL
};
226 struct device_tree_node
*node
;
227 node
= dt_find_node(tree
->root
, path
, NULL
, NULL
, 1);
229 u64 start
= (uintptr_t)ramdisk_addr
;
230 u64 end
= start
+ ramdisk_size
;
232 dt_add_u64_prop(node
, "linux,initrd-start", start
);
233 dt_add_u64_prop(node
, "linux,initrd-end", end
);
236 static void update_reserve_map(uint64_t start
, uint64_t end
,
237 struct device_tree
*tree
)
239 struct device_tree_reserve_map_entry
*entry
= xzalloc(sizeof(*entry
));
241 entry
->start
= start
;
242 entry
->size
= end
- start
;
244 list_insert_after(&entry
->list_node
, &tree
->reserve_map
);
247 struct entry_params
{
248 unsigned int addr_cells
;
249 unsigned int size_cells
;
253 static uint64_t max_range(unsigned int size_cells
)
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.
260 return 0x1ULL
<< (size_cells
* 32 - 1);
263 static void update_mem_property(u64 start
, u64 end
, struct entry_params
*params
)
265 u8
*data
= (u8
*)params
->data
;
266 u64 full_size
= end
- start
;
268 const u64 max_size
= max_range(params
->size_cells
);
269 const u64 size
= MIN(max_size
, full_size
);
271 dt_write_int(data
, start
, params
->addr_cells
* sizeof(u32
));
272 data
+= params
->addr_cells
* sizeof(uint32_t);
275 dt_write_int(data
, size
, params
->size_cells
* sizeof(u32
));
276 data
+= params
->size_cells
* sizeof(uint32_t);
283 struct memranges mem
;
284 struct memranges reserved
;
287 static bool walk_memory_table(const struct range_entry
*r
, void *arg
)
289 struct mem_map
*arg_map
= arg
;
290 struct memranges
*ranges
;
291 enum bootmem_type tag
;
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
);
299 void fit_add_compat_string(const char *str
)
301 struct compat_string_entry
*compat_node
;
303 compat_node
= xzalloc(sizeof(*compat_node
));
304 compat_node
->compat_string
= strdup(str
);
306 clean_compat_string((char *)compat_node
->compat_string
);
308 list_insert_after(&compat_node
->list_node
, &compat_strings
);
311 void fit_update_memory(struct device_tree
*tree
)
313 const struct range_entry
*r
;
314 struct device_tree_node
*node
;
315 u32 addr_cells
= 1, size_cells
= 1;
318 printk(BIOS_INFO
, "FIT: Updating devicetree memory entries\n");
320 dt_read_cell_props(tree
->root
, &addr_cells
, &size_cells
);
323 * First remove all existing device_type="memory" nodes, then add ours.
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
);
331 node
= xzalloc(sizeof(*node
));
333 node
->name
= "memory";
334 list_insert_after(&node
->list_node
, &tree
->root
->children
);
335 dt_add_string_prop(node
, "device_type", (char *)"memory");
337 memranges_init_empty(&map
.mem
, NULL
, 0);
338 memranges_init_empty(&map
.reserved
, NULL
, 0);
340 bootmem_walk_os_mem(walk_memory_table
, &map
);
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
),
349 * Count the amount of 'reg' entries we need (account for size limits).
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
);
358 /* Allocate the right amount of space and fill up the entries. */
359 size_t length
= count
* (addr_cells
+ size_cells
) * sizeof(u32
);
361 void *data
= xzalloc(length
);
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
),
368 assert(add_params
.data
- data
== length
);
370 /* Assemble the final property and add it to the device tree. */
371 dt_add_bin_prop(node
, "reg", data
, length
);
373 memranges_teardown(&map
.mem
);
374 memranges_teardown(&map
.reserved
);
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.
382 static int fit_update_compat(struct fit_config_node
*config
)
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
);
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",
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",
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
);
413 config
->compat_pos
= -1;
414 config
->compat_rank
= -1;
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
);
421 config
->compat_pos
= pos
;
422 config
->compat_rank
= i
;
423 config
->compat_string
=
424 compat_node
->compat_string
;
432 struct fit_config_node
*fit_load(void *fit
)
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
;
439 printk(BIOS_DEBUG
, "FIT: Loading FIT from %p\n", fit
);
441 struct device_tree
*tree
= fdt_unflatten(fit
);
443 printk(BIOS_ERR
, "Failed to unflatten FIT image!\n");
447 const char *default_config_name
= NULL
;
448 struct fit_config_node
*default_config
= NULL
;
449 struct fit_config_node
*compat_config
= NULL
;
451 fit_unpack(tree
, &default_config_name
);
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
,
458 fit_add_default_compat_strings();
460 printk(BIOS_DEBUG
, "FIT: Compat preference "
461 "(lowest to highest priority) :");
463 list_for_each(compat_node
, compat_strings
, list_node
) {
464 printk(BIOS_DEBUG
, " %s", compat_node
->compat_string
);
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",
475 printk(BIOS_ERR
, "config %s has no FDT, skipping.\n",
480 if (config
->ramdisk
&&
481 config
->ramdisk
->compression
< 0) {
482 printk(BIOS_WARNING
, "Ramdisk is compressed with "
483 "an unsupported algorithm, discarding config %s."
488 if (fit_update_compat(config
))
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
;
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
);
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;
517 if (config
->compat_rank
>= 0 && (!compat_config
||
518 config
->compat_rank
> compat_config
->compat_rank
))
519 compat_config
= config
;
521 printk(BIOS_DEBUG
, "\n");
524 struct fit_config_node
*to_boot
= NULL
;
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",
534 printk(BIOS_ERR
, "FIT: No compatible or default configs. "