fit.c
1 /* Taken from depthcharge: src/boot/fit.c */ 2 /* SPDX-License-Identifier: GPL-2.0-or-later */ 3 4 #include <assert.h> 5 #include <console/console.h> 6 #include <ctype.h> 7 #include <endian.h> 8 #include <identity.h> 9 #include <bootmem.h> 10 #include <string.h> 11 #include <program_loading.h> 12 #include <memrange.h> 13 #include <fit.h> 14 #include <boardid.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <types.h> 18 19 static struct list_node image_nodes; 20 static struct list_node config_nodes; 21 static struct list_node compat_strings; 22 23 struct compat_string_entry { 24 const char *compat_string; 25 struct list_node list_node; 26 }; 27 28 /* Convert string to lowercase and replace '_' and spaces with '-'. */ 29 static char *clean_compat_string(char *str) 30 { 31 for (size_t i = 0; i < strlen(str); i++) { 32 str[i] = tolower(str[i]); 33 if (str[i] == '_' || str[i] == ' ') 34 str[i] = '-'; 35 } 36 37 return str; 38 } 39 40 static void fit_add_default_compat_strings(void) 41 { 42 char compat_string[80] = {}; 43 44 if ((board_id() != UNDEFINED_STRAPPING_ID) && 45 (sku_id() != UNDEFINED_STRAPPING_ID)) { 46 snprintf(compat_string, sizeof(compat_string), 47 "%s,%s-rev%u-sku%u", mainboard_vendor, mainboard_part_number, 48 board_id(), sku_id()); 49 50 fit_add_compat_string(compat_string); 51 } 52 53 if (board_id() != UNDEFINED_STRAPPING_ID) { 54 snprintf(compat_string, sizeof(compat_string), "%s,%s-rev%u", 55 mainboard_vendor, mainboard_part_number, board_id()); 56 57 fit_add_compat_string(compat_string); 58 } 59 60 if (sku_id() != UNDEFINED_STRAPPING_ID) { 61 snprintf(compat_string, sizeof(compat_string), "%s,%s-sku%u", 62 mainboard_vendor, mainboard_part_number, sku_id()); 63 64 fit_add_compat_string(compat_string); 65 } 66 67 snprintf(compat_string, sizeof(compat_string), "%s,%s", 68 mainboard_vendor, mainboard_part_number); 69 70 fit_add_compat_string(compat_string); 71 } 72 73 static struct fit_image_node *find_image(const char *name) 74 { 75 struct fit_image_node *image; 76 list_for_each(image, image_nodes, list_node) { 77 if (!strcmp(image->name, name)) 78 return image; 79 } 80 printk(BIOS_ERR, "Cannot find image node %s!\n", name); 81 return NULL; 82 } 83 84 static struct fit_image_node *find_image_with_overlays(const char *name, 85 int bytes, struct list_node *prev) 86 { 87 struct fit_image_node *base = find_image(name); 88 if (!base) 89 return NULL; 90 91 int len = strnlen(name, bytes) + 1; 92 bytes -= len; 93 name += len; 94 while (bytes > 0) { 95 struct fit_overlay_chain *next = xzalloc(sizeof(*next)); 96 next->overlay = find_image(name); 97 if (!next->overlay) 98 return NULL; 99 list_insert_after(&next->list_node, prev); 100 prev = &next->list_node; 101 len = strnlen(name, bytes) + 1; 102 bytes -= len; 103 name += len; 104 } 105 106 return base; 107 } 108 109 static void image_node(struct device_tree_node *node) 110 { 111 struct fit_image_node *image = xzalloc(sizeof(*image)); 112 113 image->compression = CBFS_COMPRESS_NONE; 114 image->name = node->name; 115 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; 128 else 129 image->compression = -1; 130 } 131 } 132 133 list_insert_after(&image->list_node, &image_nodes); 134 } 135 136 static void config_node(struct device_tree_node *node) 137 { 138 struct fit_config_node *config = xzalloc(sizeof(*config)); 139 config->name = node->name; 140 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; 152 } 153 154 list_insert_after(&config->list_node, &config_nodes); 155 } 156 157 static void fit_unpack(struct device_tree *tree, const char **default_config) 158 { 159 struct device_tree_node *child; 160 struct device_tree_node *images = dt_find_node_by_path(tree, "/images", 161 NULL, NULL, 0); 162 if (images) 163 list_for_each(child, images->children, list_node) 164 image_node(child); 165 166 struct device_tree_node *configs = dt_find_node_by_path(tree, 167 "/configurations", NULL, NULL, 0); 168 if (configs) { 169 *default_config = dt_find_string_prop(configs, "default"); 170 list_for_each(child, configs->children, list_node) 171 config_node(child); 172 } 173 } 174 175 static int fdt_find_compat(const void *blob, uint32_t start_offset, 176 struct fdt_property *prop) 177 { 178 int offset = start_offset; 179 int size; 180 181 size = fdt_next_node_name(blob, offset, NULL); 182 if (!size) 183 return -1; 184 offset += size; 185 186 while ((size = fdt_next_property(blob, offset, prop))) { 187 if (!strcmp("compatible", prop->name)) 188 return 0; 189 190 offset += size; 191 } 192 193 prop->name = NULL; 194 return -1; 195 } 196 197 static int fit_check_compat(struct fdt_property *compat_prop, 198 const char *compat_name) 199 { 200 int bytes = compat_prop->size; 201 const char *compat_str = compat_prop->data; 202 203 for (int pos = 0; bytes && compat_str[0]; pos++) { 204 if (!strncmp(compat_str, compat_name, bytes)) 205 return pos; 206 int len = strlen(compat_str) + 1; 207 compat_str += len; 208 bytes -= len; 209 } 210 return -1; 211 } 212 213 void fit_update_chosen(struct device_tree *tree, const char *cmd_line) 214 { 215 const char *path[] = { "chosen", NULL }; 216 struct device_tree_node *node; 217 node = dt_find_node(tree->root, path, NULL, NULL, 1); 218 219 dt_add_string_prop(node, "bootargs", cmd_line); 220 } 221 222 void fit_add_ramdisk(struct device_tree *tree, void *ramdisk_addr, 223 size_t ramdisk_size) 224 { 225 const char *path[] = { "chosen", NULL }; 226 struct device_tree_node *node; 227 node = dt_find_node(tree->root, path, NULL, NULL, 1); 228 229 u64 start = (uintptr_t)ramdisk_addr; 230 u64 end = start + ramdisk_size; 231 232 dt_add_u64_prop(node, "linux,initrd-start", start); 233 dt_add_u64_prop(node, "linux,initrd-end", end); 234 } 235 236 static void update_reserve_map(uint64_t start, uint64_t end, 237 struct device_tree *tree) 238 { 239 struct device_tree_reserve_map_entry *entry = xzalloc(sizeof(*entry)); 240 241 entry->start = start; 242 entry->size = end - start; 243 244 list_insert_after(&entry->list_node, &tree->reserve_map); 245 } 246 247 struct entry_params { 248 unsigned int addr_cells; 249 unsigned int size_cells; 250 void *data; 251 }; 252 253 static uint64_t max_range(unsigned int size_cells) 254 { 255 /* 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. 259 */ 260 return 0x1ULL << (size_cells * 32 - 1); 261 } 262 263 static void update_mem_property(u64 start, u64 end, struct entry_params *params) 264 { 265 u8 *data = (u8 *)params->data; 266 u64 full_size = end - start; 267 while (full_size) { 268 const u64 max_size = max_range(params->size_cells); 269 const u64 size = MIN(max_size, full_size); 270 271 dt_write_int(data, start, params->addr_cells * sizeof(u32)); 272 data += params->addr_cells * sizeof(uint32_t); 273 start += size; 274 275 dt_write_int(data, size, params->size_cells * sizeof(u32)); 276 data += params->size_cells * sizeof(uint32_t); 277 full_size -= size; 278 } 279 params->data = data; 280 } 281 282 struct mem_map { 283 struct memranges mem; 284 struct memranges reserved; 285 }; 286 287 static bool walk_memory_table(const struct range_entry *r, void *arg) 288 { 289 struct mem_map *arg_map = arg; 290 struct memranges *ranges; 291 enum bootmem_type tag; 292 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); 296 return true; 297 } 298 299 void fit_add_compat_string(const char *str) 300 { 301 struct compat_string_entry *compat_node; 302 303 compat_node = xzalloc(sizeof(*compat_node)); 304 compat_node->compat_string = strdup(str); 305 306 clean_compat_string((char *)compat_node->compat_string); 307 308 list_insert_after(&compat_node->list_node, &compat_strings); 309 } 310 311 void fit_update_memory(struct device_tree *tree) 312 { 313 const struct range_entry *r; 314 struct device_tree_node *node; 315 u32 addr_cells = 1, size_cells = 1; 316 struct mem_map map; 317 318 printk(BIOS_INFO, "FIT: Updating devicetree memory entries\n"); 319 320 dt_read_cell_props(tree->root, &addr_cells, &size_cells); 321 322 /* 323 * First remove all existing device_type="memory" nodes, then add ours. 324 */ 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); 329 } 330 331 node = xzalloc(sizeof(*node)); 332 333 node->name = "memory"; 334 list_insert_after(&node->list_node, &tree->root->children); 335 dt_add_string_prop(node, "device_type", (char *)"memory"); 336 337 memranges_init_empty(&map.mem, NULL, 0); 338 memranges_init_empty(&map.reserved, NULL, 0); 339 340 bootmem_walk_os_mem(walk_memory_table, &map); 341 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), 345 tree); 346 } 347 348 /* 349 * Count the amount of 'reg' entries we need (account for size limits). 350 */ 351 size_t count = 0; 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); 356 } 357 358 /* Allocate the right amount of space and fill up the entries. */ 359 size_t length = count * (addr_cells + size_cells) * sizeof(u32); 360 361 void *data = xzalloc(length); 362 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), 366 &add_params); 367 } 368 assert(add_params.data - data == length); 369 370 /* Assemble the final property and add it to the device tree. */ 371 dt_add_bin_prop(node, "reg", data, length); 372 373 memranges_teardown(&map.mem); 374 memranges_teardown(&map.reserved); 375 } 376 377 /* 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. 381 */ 382 static int fit_update_compat(struct fit_config_node *config) 383 { 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); 390 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", 394 config->name); 395 return -1; 396 } 397 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", 401 config->name); 402 return -1; 403 } 404 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); 409 return -1; 410 } 411 } 412 413 config->compat_pos = -1; 414 config->compat_rank = -1; 415 size_t i = 0; 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); 420 if (pos >= 0) { 421 config->compat_pos = pos; 422 config->compat_rank = i; 423 config->compat_string = 424 compat_node->compat_string; 425 } 426 i++; 427 } 428 429 return 0; 430 } 431 432 struct fit_config_node *fit_load(void *fit) 433 { 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; 438 439 printk(BIOS_DEBUG, "FIT: Loading FIT from %p\n", fit); 440 441 struct device_tree *tree = fdt_unflatten(fit); 442 if (!tree) { 443 printk(BIOS_ERR, "Failed to unflatten FIT image!\n"); 444 return NULL; 445 } 446 447 const char *default_config_name = NULL; 448 struct fit_config_node *default_config = NULL; 449 struct fit_config_node *compat_config = NULL; 450 451 fit_unpack(tree, &default_config_name); 452 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, 456 image->size); 457 458 fit_add_default_compat_strings(); 459 460 printk(BIOS_DEBUG, "FIT: Compat preference " 461 "(lowest to highest priority) :"); 462 463 list_for_each(compat_node, compat_strings, list_node) { 464 printk(BIOS_DEBUG, " %s", compat_node->compat_string); 465 } 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", 471 config->name); 472 continue; 473 } 474 if (!config->fdt) { 475 printk(BIOS_ERR, "config %s has no FDT, skipping.\n", 476 config->name); 477 continue; 478 } 479 480 if (config->ramdisk && 481 config->ramdisk->compression < 0) { 482 printk(BIOS_WARNING, "Ramdisk is compressed with " 483 "an unsupported algorithm, discarding config %s." 484 "\n", config->name); 485 continue; 486 } 487 488 if (fit_update_compat(config)) 489 continue; 490 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; 496 } 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); 501 if (config->ramdisk) 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; 513 compat_str += len; 514 bytes -= len; 515 } 516 517 if (config->compat_rank >= 0 && (!compat_config || 518 config->compat_rank > compat_config->compat_rank)) 519 compat_config = config; 520 } 521 printk(BIOS_DEBUG, "\n"); 522 } 523 524 struct fit_config_node *to_boot = NULL; 525 if (compat_config) { 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", 532 to_boot->name); 533 } else { 534 printk(BIOS_ERR, "FIT: No compatible or default configs. " 535 "Giving up.\n"); 536 return NULL; 537 } 538 539 return to_boot; 540 }