"Das U-Boot" Source Tree
at master 622 lines 15 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * UPL handoff generation 4 * 5 * Copyright 2024 Google LLC 6 * Written by Simon Glass <sjg@chromium.org> 7 */ 8 9#define LOG_CATEGORY UCLASS_BOOTSTD 10 11#include <log.h> 12#include <upl.h> 13#include <dm/ofnode.h> 14#include "upl_common.h" 15 16/** 17 * write_addr() - Write an address 18 * 19 * Writes an address in the correct format, either 32- or 64-bit 20 * 21 * @upl: UPL state 22 * @node: Node to write to 23 * @prop: Property name to write 24 * @addr: Address to write 25 * Return: 0 if OK, -ve on error 26 */ 27static int write_addr(const struct upl *upl, ofnode node, const char *prop, 28 ulong addr) 29{ 30 int ret; 31 32 if (upl->addr_cells == 1) 33 ret = ofnode_write_u32(node, prop, addr); 34 else 35 ret = ofnode_write_u64(node, prop, addr); 36 37 return ret; 38} 39 40/** 41 * write_size() - Write a size 42 * 43 * Writes a size in the correct format, either 32- or 64-bit 44 * 45 * @upl: UPL state 46 * @node: Node to write to 47 * @prop: Property name to write 48 * @size: Size to write 49 * Return: 0 if OK, -ve on error 50 */ 51static int write_size(const struct upl *upl, ofnode node, const char *prop, 52 ulong size) 53{ 54 int ret; 55 56 if (upl->size_cells == 1) 57 ret = ofnode_write_u32(node, prop, size); 58 else 59 ret = ofnode_write_u64(node, prop, size); 60 61 return ret; 62} 63 64/** 65 * ofnode_write_bitmask() - Write a bit mask as a string list 66 * 67 * @node: Node to write to 68 * @prop: Property name to write 69 * @names: Array of names for each bit 70 * @count: Number of array entries 71 * @value: Bit-mask value to write 72 * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOSPC if the 73 * string is too long for the (internal) buffer 74 */ 75static int ofnode_write_bitmask(ofnode node, const char *prop, 76 const char *const names[], uint count, 77 uint value) 78{ 79 char buf[128]; 80 char *ptr, *end = buf + sizeof(buf); 81 uint bit; 82 int ret; 83 84 ptr = buf; 85 for (bit = 0; bit < count; bit++) { 86 if (value & BIT(bit)) { 87 const char *str = names[bit]; 88 uint len; 89 90 if (!str) { 91 log_debug("Unnamed bit number %d\n", bit); 92 return log_msg_ret("bit", -EINVAL); 93 } 94 len = strlen(str) + 1; 95 if (ptr + len > end) { 96 log_debug("String array too long\n"); 97 return log_msg_ret("bit", -ENOSPC); 98 } 99 100 memcpy(ptr, str, len); 101 ptr += len; 102 } 103 } 104 105 ret = ofnode_write_prop(node, prop, buf, ptr - buf, true); 106 if (ret) 107 return log_msg_ret("wri", ret); 108 109 return 0; 110} 111 112/** 113 * ofnode_write_value() - Write an int as a string value using a lookup 114 * 115 * @node: Node to write to 116 * @prop: Property name to write 117 * @names: Array of names for each int value 118 * @count: Number of array entries 119 * @value: Int value to write 120 * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOSPC if the 121 * string is too long for the (internal) buffer 122 */ 123static int ofnode_write_value(ofnode node, const char *prop, 124 const char *const names[], uint count, 125 uint value) 126{ 127 const char *str; 128 int ret; 129 130 if (value >= count) { 131 log_debug("Value of range %d\n", value); 132 return log_msg_ret("val", -ERANGE); 133 } 134 str = names[value]; 135 if (!str) { 136 log_debug("Unnamed value %d\n", value); 137 return log_msg_ret("val", -EINVAL); 138 } 139 ret = ofnode_write_string(node, prop, str); 140 if (ret) 141 return log_msg_ret("wri", ret); 142 143 return 0; 144} 145 146/** 147 * add_root_props() - Add root properties to the tree 148 * 149 * @node: Node to add to 150 * Return 0 if OK, -ve on error 151 */ 152static int add_root_props(const struct upl *upl, ofnode node) 153{ 154 int ret; 155 156 ret = ofnode_write_u32(node, UPLP_ADDRESS_CELLS, upl->addr_cells); 157 if (!ret) 158 ret = ofnode_write_u32(node, UPLP_SIZE_CELLS, upl->size_cells); 159 if (ret) 160 return log_msg_ret("cel", ret); 161 162 return 0; 163} 164 165/** 166 * add_upl_params() - Add UPL parameters node 167 * 168 * @upl: UPL state 169 * @options: /options node to add to 170 * Return 0 if OK, -ve on error 171 */ 172static int add_upl_params(const struct upl *upl, ofnode options) 173{ 174 ofnode node; 175 int ret; 176 177 ret = ofnode_add_subnode(options, UPLN_UPL_PARAMS, &node); 178 if (ret) 179 return log_msg_ret("img", ret); 180 181 ret = write_addr(upl, node, UPLP_SMBIOS, upl->smbios); 182 if (!ret) 183 ret = write_addr(upl, node, UPLP_ACPI, upl->acpi); 184 if (!ret && upl->bootmode) 185 ret = ofnode_write_bitmask(node, UPLP_BOOTMODE, bootmode_names, 186 UPLBM_COUNT, upl->bootmode); 187 if (!ret) 188 ret = ofnode_write_u32(node, UPLP_ADDR_WIDTH, upl->addr_width); 189 if (!ret) 190 ret = ofnode_write_u32(node, UPLP_ACPI_NVS_SIZE, 191 upl->acpi_nvs_size); 192 if (ret) 193 return log_msg_ret("cnf", ret); 194 195 return 0; 196} 197 198/** 199 * add_upl_image() - Add /options/upl-image nodes and properties to the tree 200 * 201 * @upl: UPL state 202 * @node: /options node to add to 203 * Return 0 if OK, -ve on error 204 */ 205static int add_upl_image(const struct upl *upl, ofnode options) 206{ 207 ofnode node; 208 int ret, i; 209 210 ret = ofnode_add_subnode(options, UPLN_UPL_IMAGE, &node); 211 if (ret) 212 return log_msg_ret("img", ret); 213 214 if (upl->fit) 215 ret = ofnode_write_u32(node, UPLP_FIT, upl->fit); 216 if (!ret && upl->conf_offset) 217 ret = ofnode_write_u32(node, UPLP_CONF_OFFSET, 218 upl->conf_offset); 219 if (ret) 220 return log_msg_ret("cnf", ret); 221 222 for (i = 0; i < upl->image.count; i++) { 223 const struct upl_image *img = alist_get(&upl->image, i, 224 struct upl_image); 225 ofnode subnode; 226 char name[10]; 227 228 snprintf(name, sizeof(name), UPLN_IMAGE "-%d", i + 1); 229 ret = ofnode_add_subnode(node, name, &subnode); 230 if (ret) 231 return log_msg_ret("sub", ret); 232 233 ret = write_addr(upl, subnode, UPLP_LOAD, img->load); 234 if (!ret) 235 ret = write_size(upl, subnode, UPLP_SIZE, img->size); 236 if (!ret && img->offset) 237 ret = ofnode_write_u32(subnode, UPLP_OFFSET, 238 img->offset); 239 ret = ofnode_write_string(subnode, UPLP_DESCRIPTION, 240 img->description); 241 if (ret) 242 return log_msg_ret("sim", ret); 243 } 244 245 return 0; 246} 247 248/** 249 * buffer_addr_size() - Generate a set of addr/size pairs 250 * 251 * Each base/size value from each region is written to the buffer in a suitable 252 * format to be written to the devicetree 253 * 254 * @upl: UPL state 255 * @buf: Buffer to write to 256 * @size: Buffer size 257 * @num_regions: Number of regions to process 258 * @region: List of regions to process (struct memregion) 259 * Returns: Number of bytes written, or -ENOSPC if the buffer is too small 260 */ 261static int buffer_addr_size(const struct upl *upl, char *buf, int size, 262 uint num_regions, const struct alist *region) 263{ 264 char *ptr, *end = buf + size; 265 int i; 266 267 ptr = buf; 268 for (i = 0; i < num_regions; i++) { 269 const struct memregion *reg = alist_get(region, i, 270 struct memregion); 271 272 if (upl->addr_cells == 1) 273 *(u32 *)ptr = cpu_to_fdt32(reg->base); 274 else 275 *(u64 *)ptr = cpu_to_fdt64(reg->base); 276 ptr += upl->addr_cells * sizeof(u32); 277 278 if (upl->size_cells == 1) 279 *(u32 *)ptr = cpu_to_fdt32(reg->size); 280 else 281 *(u64 *)ptr = cpu_to_fdt64(reg->size); 282 ptr += upl->size_cells * sizeof(u32); 283 if (ptr > end) 284 return -ENOSPC; 285 } 286 287 return ptr - buf; 288} 289 290/** 291 * add_upl_memory() - Add /memory nodes to the tree 292 * 293 * @upl: UPL state 294 * @root: Parent node to contain the new /memory nodes 295 * Return 0 if OK, -ve on error 296 */ 297static int add_upl_memory(const struct upl *upl, ofnode root) 298{ 299 int i; 300 301 for (i = 0; i < upl->mem.count; i++) { 302 const struct upl_mem *mem = alist_get(&upl->mem, i, 303 struct upl_mem); 304 char buf[mem->region.count * sizeof(64) * 2]; 305 const struct memregion *first; 306 char name[26]; 307 int ret, len; 308 ofnode node; 309 310 if (!mem->region.count) { 311 log_debug("Memory %d has no regions\n", i); 312 return log_msg_ret("reg", -EINVAL); 313 } 314 first = alist_get(&mem->region, 0, struct memregion); 315 sprintf(name, UPLN_MEMORY "@0x%lx", first->base); 316 ret = ofnode_add_subnode(root, name, &node); 317 if (ret) 318 return log_msg_ret("mem", ret); 319 320 len = buffer_addr_size(upl, buf, sizeof(buf), mem->region.count, 321 &mem->region); 322 if (len < 0) 323 return log_msg_ret("buf", len); 324 325 ret = ofnode_write_prop(node, UPLP_REG, buf, len, true); 326 if (!ret && mem->hotpluggable) 327 ret = ofnode_write_bool(node, UPLP_HOTPLUGGABLE, 328 mem->hotpluggable); 329 if (ret) 330 return log_msg_ret("lst", ret); 331 } 332 333 return 0; 334} 335 336/** 337 * add_upl_memmap() - Add memory-map nodes to the tree 338 * 339 * @upl: UPL state 340 * @root: Parent node to contain the new /memory-map node and its subnodes 341 * Return 0 if OK, -ve on error 342 */ 343static int add_upl_memmap(const struct upl *upl, ofnode root) 344{ 345 ofnode mem_node; 346 int i, ret; 347 348 if (!upl->memmap.count) 349 return 0; 350 ret = ofnode_add_subnode(root, UPLN_MEMORY_MAP, &mem_node); 351 if (ret) 352 return log_msg_ret("img", ret); 353 354 for (i = 0; i < upl->memmap.count; i++) { 355 const struct upl_memmap *memmap = alist_get(&upl->memmap, i, 356 struct upl_memmap); 357 char buf[memmap->region.count * sizeof(64) * 2]; 358 const struct memregion *first; 359 char name[26]; 360 int ret, len; 361 ofnode node; 362 363 if (!memmap->region.count) { 364 log_debug("Memory %d has no regions\n", i); 365 return log_msg_ret("reg", -EINVAL); 366 } 367 first = alist_get(&memmap->region, 0, struct memregion); 368 sprintf(name, "%s@0x%lx", memmap->name, first->base); 369 ret = ofnode_add_subnode(mem_node, name, &node); 370 if (ret) 371 return log_msg_ret("memmap", ret); 372 373 len = buffer_addr_size(upl, buf, sizeof(buf), 374 memmap->region.count, &memmap->region); 375 if (len < 0) 376 return log_msg_ret("buf", len); 377 ret = ofnode_write_prop(node, UPLP_REG, buf, len, true); 378 if (!ret && memmap->usage) 379 ret = ofnode_write_bitmask(node, UPLP_USAGE, 380 usage_names, 381 UPLUS_COUNT, memmap->usage); 382 if (ret) 383 return log_msg_ret("lst", ret); 384 } 385 386 return 0; 387} 388 389/** 390 * add_upl_memres() - Add /memory-reserved nodes to the tree 391 * 392 * @upl: UPL state 393 * @root: Parent node to contain the new node 394 * Return 0 if OK, -ve on error 395 */ 396static int add_upl_memres(const struct upl *upl, ofnode root, 397 bool skip_existing) 398{ 399 ofnode mem_node; 400 int i, ret; 401 402 if (!upl->memmap.count) 403 return 0; 404 ret = ofnode_add_subnode(root, UPLN_MEMORY_RESERVED, &mem_node); 405 if (ret) { 406 if (skip_existing && ret == -EEXIST) 407 return 0; 408 return log_msg_ret("img", ret); 409 } 410 411 for (i = 0; i < upl->memres.count; i++) { 412 const struct upl_memres *memres = alist_get(&upl->memres, i, 413 struct upl_memres); 414 char buf[memres->region.count * sizeof(64) * 2]; 415 const struct memregion *first; 416 char name[26]; 417 int ret, len; 418 ofnode node; 419 420 if (!memres->region.count) { 421 log_debug("Memory %d has no regions\n", i); 422 return log_msg_ret("reg", -EINVAL); 423 } 424 first = alist_get(&memres->region, 0, struct memregion); 425 sprintf(name, "%s@0x%lx", memres->name, first->base); 426 ret = ofnode_add_subnode(mem_node, name, &node); 427 if (ret) 428 return log_msg_ret("memres", ret); 429 430 len = buffer_addr_size(upl, buf, sizeof(buf), 431 memres->region.count, &memres->region); 432 ret = ofnode_write_prop(node, UPLP_REG, buf, len, true); 433 if (!ret && memres->no_map) 434 ret = ofnode_write_bool(node, UPLP_NO_MAP, 435 memres->no_map); 436 if (ret) 437 return log_msg_ret("lst", ret); 438 } 439 440 return 0; 441} 442 443/** 444 * add_upl_serial() - Add serial node 445 * 446 * @upl: UPL state 447 * @root: Parent node to contain the new node 448 * Return 0 if OK, -ve on error 449 */ 450static int add_upl_serial(const struct upl *upl, ofnode root, 451 bool skip_existing) 452{ 453 const struct upl_serial *ser = &upl->serial; 454 const struct memregion *first; 455 char name[26]; 456 ofnode node; 457 int ret; 458 459 if (!ser->compatible || skip_existing) 460 return 0; 461 if (!ser->reg.count) 462 return log_msg_ret("ser", -EINVAL); 463 first = alist_get(&ser->reg, 0, struct memregion); 464 sprintf(name, UPLN_SERIAL "@0x%lx", first->base); 465 ret = ofnode_add_subnode(root, name, &node); 466 if (ret) 467 return log_msg_ret("img", ret); 468 ret = ofnode_write_string(node, UPLP_COMPATIBLE, ser->compatible); 469 if (!ret) 470 ret = ofnode_write_u32(node, UPLP_CLOCK_FREQUENCY, 471 ser->clock_frequency); 472 if (!ret) 473 ret = ofnode_write_u32(node, UPLP_CURRENT_SPEED, 474 ser->current_speed); 475 if (!ret) { 476 char buf[16]; 477 int len; 478 479 len = buffer_addr_size(upl, buf, sizeof(buf), 1, &ser->reg); 480 if (len < 0) 481 return log_msg_ret("buf", len); 482 483 ret = ofnode_write_prop(node, UPLP_REG, buf, len, true); 484 } 485 if (!ret && ser->reg_io_shift != UPLD_REG_IO_SHIFT) 486 ret = ofnode_write_u32(node, UPLP_REG_IO_SHIFT, 487 ser->reg_io_shift); 488 if (!ret && ser->reg_offset != UPLD_REG_OFFSET) 489 ret = ofnode_write_u32(node, UPLP_REG_OFFSET, ser->reg_offset); 490 if (!ret && ser->reg_io_width != UPLD_REG_IO_WIDTH) 491 ret = ofnode_write_u32(node, UPLP_REG_IO_WIDTH, 492 ser->reg_io_width); 493 if (!ret && ser->virtual_reg) 494 ret = write_addr(upl, node, UPLP_VIRTUAL_REG, ser->virtual_reg); 495 if (!ret) { 496 ret = ofnode_write_value(node, UPLP_ACCESS_TYPE, access_types, 497 ARRAY_SIZE(access_types), 498 ser->access_type); 499 } 500 if (ret) 501 return log_msg_ret("ser", ret); 502 503 return 0; 504} 505 506/** 507 * add_upl_graphics() - Add graphics node 508 * 509 * @upl: UPL state 510 * @root: Parent node to contain the new node 511 * Return 0 if OK, -ve on error 512 */ 513static int add_upl_graphics(const struct upl *upl, ofnode root) 514{ 515 const struct upl_graphics *gra = &upl->graphics; 516 const struct memregion *first; 517 char name[36]; 518 ofnode node; 519 int ret; 520 521 if (!gra->reg.count) 522 return log_msg_ret("gra", -ENOENT); 523 first = alist_get(&gra->reg, 0, struct memregion); 524 sprintf(name, UPLN_GRAPHICS "@0x%lx", first->base); 525 ret = ofnode_add_subnode(root, name, &node); 526 if (ret) 527 return log_msg_ret("gra", ret); 528 529 ret = ofnode_write_string(node, UPLP_COMPATIBLE, UPLC_GRAPHICS); 530 if (!ret) { 531 char buf[16]; 532 int len; 533 534 len = buffer_addr_size(upl, buf, sizeof(buf), 1, &gra->reg); 535 if (len < 0) 536 return log_msg_ret("buf", len); 537 538 ret = ofnode_write_prop(node, UPLP_REG, buf, len, true); 539 } 540 if (!ret) 541 ret = ofnode_write_u32(node, UPLP_WIDTH, gra->width); 542 if (!ret) 543 ret = ofnode_write_u32(node, UPLP_HEIGHT, gra->height); 544 if (!ret) 545 ret = ofnode_write_u32(node, UPLP_STRIDE, gra->stride); 546 if (!ret) { 547 ret = ofnode_write_value(node, UPLP_GRAPHICS_FORMAT, 548 graphics_formats, 549 ARRAY_SIZE(graphics_formats), 550 gra->format); 551 } 552 if (ret) 553 return log_msg_ret("pro", ret); 554 555 return 0; 556} 557 558int upl_write_handoff(const struct upl *upl, ofnode root, bool skip_existing) 559{ 560 ofnode options; 561 int ret; 562 563 ret = add_root_props(upl, root); 564 if (ret) 565 return log_msg_ret("ad1", ret); 566 ret = ofnode_add_subnode(root, UPLN_OPTIONS, &options); 567 if (ret && ret != -EEXIST) 568 return log_msg_ret("opt", -EINVAL); 569 570 ret = add_upl_params(upl, options); 571 if (ret) 572 return log_msg_ret("ad1", ret); 573 574 ret = add_upl_image(upl, options); 575 if (ret) 576 return log_msg_ret("ad2", ret); 577 578 ret = add_upl_memory(upl, root); 579 if (ret) 580 return log_msg_ret("ad3", ret); 581 582 ret = add_upl_memmap(upl, root); 583 if (ret) 584 return log_msg_ret("ad4", ret); 585 586 ret = add_upl_memres(upl, root, skip_existing); 587 if (ret) 588 return log_msg_ret("ad5", ret); 589 590 ret = add_upl_serial(upl, root, skip_existing); 591 if (ret) 592 return log_msg_ret("ad6", ret); 593 594 ret = add_upl_graphics(upl, root); 595 if (ret && ret != -ENOENT) 596 return log_msg_ret("ad6", ret); 597 598 return 0; 599} 600 601int upl_create_handoff_tree(const struct upl *upl, oftree *treep) 602{ 603 ofnode root; 604 oftree tree; 605 int ret; 606 607 ret = oftree_new(&tree); 608 if (ret) 609 return log_msg_ret("new", ret); 610 611 root = oftree_root(tree); 612 if (!ofnode_valid(root)) 613 return log_msg_ret("roo", -EINVAL); 614 615 ret = upl_write_handoff(upl, root, false); 616 if (ret) 617 return log_msg_ret("wr", ret); 618 619 *treep = tree; 620 621 return 0; 622}