"Das U-Boot" Source Tree
at master 588 lines 14 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * UPL handoff parsing 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 * read_addr() - Read an address 18 * 19 * Reads an address in the correct format, either 32- or 64-bit 20 * 21 * @upl: UPL state 22 * @node: Node to read from 23 * @prop: Property name to read 24 * @addr: Place to put the address 25 * Return: 0 if OK, -ve on error 26 */ 27static int read_addr(const struct upl *upl, ofnode node, const char *prop, 28 ulong *addrp) 29{ 30 int ret; 31 32 if (upl->addr_cells == 1) { 33 u32 val; 34 35 ret = ofnode_read_u32(node, prop, &val); 36 if (!ret) 37 *addrp = val; 38 } else { 39 u64 val; 40 41 ret = ofnode_read_u64(node, prop, &val); 42 if (!ret) 43 *addrp = val; 44 } 45 46 return ret; 47} 48 49/** 50 * read_size() - Read a size 51 * 52 * Reads a size in the correct format, either 32- or 64-bit 53 * 54 * @upl: UPL state 55 * @node: Node to read from 56 * @prop: Property name to read 57 * @addr: Place to put the size 58 * Return: 0 if OK, -ve on error 59 */ 60static int read_size(const struct upl *upl, ofnode node, const char *prop, 61 ulong *sizep) 62{ 63 int ret; 64 65 if (upl->size_cells == 1) { 66 u32 val; 67 68 ret = ofnode_read_u32(node, prop, &val); 69 if (!ret) 70 *sizep = val; 71 } else { 72 u64 val; 73 74 ret = ofnode_read_u64(node, prop, &val); 75 if (!ret) 76 *sizep = val; 77 } 78 79 return ret; 80} 81 82/** 83 * ofnode_read_bitmask() - Read a bit mask from a string list 84 * 85 * @node: Node to read from 86 * @prop: Property name to read 87 * @names: Array of names for each bit 88 * @count: Number of array entries 89 * @value: Returns resulting bit-mask value on success 90 * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOSPC if the 91 * string is too long for the (internal) buffer, -EINVAL if no such property 92 */ 93static int ofnode_read_bitmask(ofnode node, const char *prop, 94 const char *const names[], uint count, 95 uint *valuep) 96{ 97 const char **list; 98 const char **strp; 99 uint val; 100 uint bit; 101 int ret; 102 103 ret = ofnode_read_string_list(node, prop, &list); 104 if (ret < 0) 105 return log_msg_ret("rea", ret); 106 107 val = 0; 108 for (strp = list; *strp; strp++) { 109 const char *str = *strp; 110 bool found = false; 111 112 for (bit = 0; bit < count; bit++) { 113 if (!strcmp(str, names[bit])) { 114 found = true; 115 break; 116 } 117 } 118 if (found) 119 val |= BIT(bit); 120 else 121 log_warning("%s/%s: Invalid value '%s'\n", 122 ofnode_get_name(node), prop, str); 123 } 124 *valuep = val; 125 126 return 0; 127} 128 129/** 130 * ofnode_read_value() - Read a string value as an int using a lookup 131 * 132 * @node: Node to read from 133 * @prop: Property name to read 134 * @names: Array of names for each int value 135 * @count: Number of array entries 136 * @valuep: Returns int value read 137 * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOENT if the 138 * property does not exist 139 */ 140static int ofnode_read_value(ofnode node, const char *prop, 141 const char *const names[], uint count, 142 uint *valuep) 143{ 144 const char *str; 145 int i; 146 147 str = ofnode_read_string(node, prop); 148 if (!str) 149 return log_msg_ret("rd", -ENOENT); 150 151 for (i = 0; i < count; i++) { 152 if (!strcmp(names[i], str)) { 153 *valuep = i; 154 return 0; 155 } 156 } 157 158 log_debug("Unnamed value '%s'\n", str); 159 return log_msg_ret("val", -EINVAL); 160} 161 162static int read_uint(ofnode node, const char *prop, uint *valp) 163{ 164 u32 val; 165 int ret; 166 167 ret = ofnode_read_u32(node, prop, &val); 168 if (ret) 169 return ret; 170 *valp = val; 171 172 return 0; 173} 174 175/** 176 * decode_root_props() - Decode root properties from the tree 177 * 178 * @upl: UPL state 179 * @node: Node to decode 180 * Return 0 if OK, -ve on error 181 */ 182static int decode_root_props(struct upl *upl, ofnode node) 183{ 184 int ret; 185 186 ret = read_uint(node, UPLP_ADDRESS_CELLS, &upl->addr_cells); 187 if (!ret) 188 ret = read_uint(node, UPLP_SIZE_CELLS, &upl->size_cells); 189 if (ret) 190 return log_msg_ret("cel", ret); 191 192 return 0; 193} 194 195/** 196 * decode_root_props() - Decode UPL parameters from the tree 197 * 198 * @upl: UPL state 199 * @node: Node to decode 200 * Return 0 if OK, -ve on error 201 */ 202static int decode_upl_params(struct upl *upl, ofnode options) 203{ 204 ofnode node; 205 int ret; 206 207 node = ofnode_find_subnode(options, UPLN_UPL_PARAMS); 208 if (!ofnode_valid(node)) 209 return log_msg_ret("par", -EINVAL); 210 log_debug("decoding '%s'\n", ofnode_get_name(node)); 211 212 ret = read_addr(upl, node, UPLP_SMBIOS, &upl->smbios); 213 if (ret) 214 return log_msg_ret("smb", ret); 215 ret = read_addr(upl, node, UPLP_ACPI, &upl->acpi); 216 if (ret) 217 return log_msg_ret("acp", ret); 218 ret = ofnode_read_bitmask(node, UPLP_BOOTMODE, bootmode_names, 219 UPLBM_COUNT, &upl->bootmode); 220 if (ret) 221 return log_msg_ret("boo", ret); 222 ret = read_uint(node, UPLP_ADDR_WIDTH, &upl->addr_width); 223 if (ret) 224 return log_msg_ret("add", ret); 225 ret = read_uint(node, UPLP_ACPI_NVS_SIZE, &upl->acpi_nvs_size); 226 if (ret) 227 return log_msg_ret("nvs", ret); 228 229 return 0; 230} 231 232/** 233 * decode_upl_images() - Decode /options/upl-image nodes 234 * 235 * @node: /options node in which to look for the node 236 * Return 0 if OK, -ve on error 237 */ 238static int decode_upl_images(struct upl *upl, ofnode options) 239{ 240 ofnode node, images; 241 int ret; 242 243 images = ofnode_find_subnode(options, UPLN_UPL_IMAGE); 244 if (!ofnode_valid(images)) 245 return log_msg_ret("img", -EINVAL); 246 log_debug("decoding '%s'\n", ofnode_get_name(images)); 247 248 ret = read_addr(upl, images, UPLP_FIT, &upl->fit); 249 if (!ret) 250 ret = read_uint(images, UPLP_CONF_OFFSET, &upl->conf_offset); 251 if (ret) 252 return log_msg_ret("cnf", ret); 253 254 ofnode_for_each_subnode(node, images) { 255 struct upl_image img; 256 257 ret = read_addr(upl, node, UPLP_LOAD, &img.load); 258 if (!ret) 259 ret = read_size(upl, node, UPLP_SIZE, &img.size); 260 if (!ret) 261 ret = read_uint(node, UPLP_OFFSET, &img.offset); 262 img.description = ofnode_read_string(node, UPLP_DESCRIPTION); 263 if (!img.description) 264 return log_msg_ret("sim", ret); 265 if (!alist_add(&upl->image, img)) 266 return log_msg_ret("img", -ENOMEM); 267 } 268 269 return 0; 270} 271 272/** 273 * decode_addr_size() - Decide a set of addr/size pairs 274 * 275 * Each base/size value from the devicetree is written to the region list 276 * 277 * @upl: UPL state 278 * @buf: Bytes to decode 279 * @size: Number of bytes to decode 280 * @regions: List of regions to process (struct memregion) 281 * Returns: number of regions found, if OK, else -ve on error 282 */ 283static int decode_addr_size(const struct upl *upl, const char *buf, int size, 284 struct alist *regions) 285{ 286 const char *ptr, *end = buf + size; 287 int i; 288 289 alist_init_struct(regions, struct memregion); 290 ptr = buf; 291 for (i = 0; ptr < end; i++) { 292 struct memregion reg; 293 294 if (upl->addr_cells == 1) 295 reg.base = fdt32_to_cpu(*(u32 *)ptr); 296 else 297 reg.base = fdt64_to_cpu(*(u64 *)ptr); 298 ptr += upl->addr_cells * sizeof(u32); 299 300 if (upl->size_cells == 1) 301 reg.size = fdt32_to_cpu(*(u32 *)ptr); 302 else 303 reg.size = fdt64_to_cpu(*(u64 *)ptr); 304 ptr += upl->size_cells * sizeof(u32); 305 if (ptr > end) 306 return -ENOSPC; 307 308 if (!alist_add(regions, reg)) 309 return log_msg_ret("reg", -ENOMEM); 310 } 311 312 return i; 313} 314 315/** 316 * node_matches_at() - Check if a node name matches "base@..." 317 * 318 * Return: true if the node name matches the base string followed by an @ sign; 319 * false otherwise 320 */ 321static bool node_matches_at(ofnode node, const char *base) 322{ 323 const char *name = ofnode_get_name(node); 324 int len = strlen(base); 325 326 return !strncmp(base, name, len) && name[len] == '@'; 327} 328 329/** 330 * decode_upl_memory_node() - Decode a /memory node from the tree 331 * 332 * @upl: UPL state 333 * @node: Node to decode 334 * Return 0 if OK, -ve on error 335 */ 336static int decode_upl_memory_node(struct upl *upl, ofnode node) 337{ 338 struct upl_mem mem; 339 const char *buf; 340 int size, len; 341 342 buf = ofnode_read_prop(node, UPLP_REG, &size); 343 if (!buf) { 344 log_warning("Node '%s': Missing '%s' property\n", 345 ofnode_get_name(node), UPLP_REG); 346 return log_msg_ret("reg", -EINVAL); 347 } 348 len = decode_addr_size(upl, buf, size, &mem.region); 349 if (len < 0) 350 return log_msg_ret("buf", len); 351 mem.hotpluggable = ofnode_read_bool(node, UPLP_HOTPLUGGABLE); 352 if (!alist_add(&upl->mem, mem)) 353 return log_msg_ret("mem", -ENOMEM); 354 355 return 0; 356} 357 358/** 359 * decode_upl_memmap() - Decode memory-map nodes from the tree 360 * 361 * @upl: UPL state 362 * @root: Parent node containing the /memory-map nodes 363 * Return 0 if OK, -ve on error 364 */ 365static int decode_upl_memmap(struct upl *upl, ofnode root) 366{ 367 ofnode node; 368 369 ofnode_for_each_subnode(node, root) { 370 struct upl_memmap memmap; 371 int size, len, ret; 372 const char *buf; 373 374 memmap.name = ofnode_get_name(node); 375 memmap.usage = 0; 376 377 buf = ofnode_read_prop(node, UPLP_REG, &size); 378 if (!buf) { 379 log_warning("Node '%s': Missing '%s' property\n", 380 ofnode_get_name(node), UPLP_REG); 381 continue; 382 } 383 384 len = decode_addr_size(upl, buf, size, &memmap.region); 385 if (len < 0) 386 return log_msg_ret("buf", len); 387 ret = ofnode_read_bitmask(node, UPLP_USAGE, usage_names, 388 UPLUS_COUNT, &memmap.usage); 389 if (ret && ret != -EINVAL) /* optional property */ 390 return log_msg_ret("bit", ret); 391 392 if (!alist_add(&upl->memmap, memmap)) 393 return log_msg_ret("mmp", -ENOMEM); 394 } 395 396 return 0; 397} 398 399/** 400 * decode_upl_memres() - Decode reserved-memory nodes from the tree 401 * 402 * @upl: UPL state 403 * @root: Parent node containing the reserved-memory nodes 404 * Return 0 if OK, -ve on error 405 */ 406static int decode_upl_memres(struct upl *upl, ofnode root) 407{ 408 ofnode node; 409 410 ofnode_for_each_subnode(node, root) { 411 struct upl_memres memres; 412 const char *buf; 413 int size, len; 414 415 log_debug("decoding '%s'\n", ofnode_get_name(node)); 416 memres.name = ofnode_get_name(node); 417 418 buf = ofnode_read_prop(node, UPLP_REG, &size); 419 if (!buf) { 420 log_warning("Node '%s': Missing 'reg' property\n", 421 ofnode_get_name(node)); 422 continue; 423 } 424 425 len = decode_addr_size(upl, buf, size, &memres.region); 426 if (len < 0) 427 return log_msg_ret("buf", len); 428 memres.no_map = ofnode_read_bool(node, UPLP_NO_MAP); 429 430 if (!alist_add(&upl->memres, memres)) 431 return log_msg_ret("mre", -ENOMEM); 432 } 433 434 return 0; 435} 436 437/** 438 * decode_upl_serial() - Decode the serial node 439 * 440 * @upl: UPL state 441 * @root: Parent node contain node 442 * Return 0 if OK, -ve on error 443 */ 444static int decode_upl_serial(struct upl *upl, ofnode node) 445{ 446 struct upl_serial *ser = &upl->serial; 447 const char *buf; 448 int len, size; 449 int ret; 450 451 ser->compatible = ofnode_read_string(node, UPLP_COMPATIBLE); 452 if (!ser->compatible) { 453 log_warning("Node '%s': Missing compatible string\n", 454 ofnode_get_name(node)); 455 return log_msg_ret("com", -EINVAL); 456 } 457 ret = read_uint(node, UPLP_CLOCK_FREQUENCY, &ser->clock_frequency); 458 if (!ret) 459 ret = read_uint(node, UPLP_CURRENT_SPEED, &ser->current_speed); 460 if (ret) 461 return log_msg_ret("spe", ret); 462 463 buf = ofnode_read_prop(node, UPLP_REG, &size); 464 if (!buf) { 465 log_warning("Node '%s': Missing 'reg' property\n", 466 ofnode_get_name(node)); 467 return log_msg_ret("reg", -EINVAL); 468 } 469 470 len = decode_addr_size(upl, buf, sizeof(buf), &ser->reg); 471 if (len < 0) 472 return log_msg_ret("buf", len); 473 474 /* set defaults */ 475 ser->reg_io_shift = UPLD_REG_IO_SHIFT; 476 ser->reg_offset = UPLD_REG_OFFSET; 477 ser->reg_io_width = UPLD_REG_IO_WIDTH; 478 read_uint(node, UPLP_REG_IO_SHIFT, &ser->reg_io_shift); 479 read_uint(node, UPLP_REG_OFFSET, &ser->reg_offset); 480 read_uint(node, UPLP_REG_IO_WIDTH, &ser->reg_io_width); 481 read_addr(upl, node, UPLP_VIRTUAL_REG, &ser->virtual_reg); 482 ret = ofnode_read_value(node, UPLP_ACCESS_TYPE, access_types, 483 ARRAY_SIZE(access_types), &ser->access_type); 484 if (ret && ret != -ENOENT) 485 return log_msg_ret("ser", ret); 486 487 return 0; 488} 489 490/** 491 * decode_upl_graphics() - Decode graphics node 492 * 493 * @upl: UPL state 494 * @root: Node to decode 495 * Return 0 if OK, -ve on error 496 */ 497static int decode_upl_graphics(struct upl *upl, ofnode node) 498{ 499 struct upl_graphics *gra = &upl->graphics; 500 const char *buf, *compat; 501 int len, size; 502 int ret; 503 504 compat = ofnode_read_string(node, UPLP_COMPATIBLE); 505 if (!compat) { 506 log_warning("Node '%s': Missing compatible string\n", 507 ofnode_get_name(node)); 508 return log_msg_ret("com", -EINVAL); 509 } 510 if (strcmp(UPLC_GRAPHICS, compat)) { 511 log_warning("Node '%s': Ignoring compatible '%s'\n", 512 ofnode_get_name(node), compat); 513 return 0; 514 } 515 516 buf = ofnode_read_prop(node, UPLP_REG, &size); 517 if (!buf) { 518 log_warning("Node '%s': Missing 'reg' property\n", 519 ofnode_get_name(node)); 520 return log_msg_ret("reg", -EINVAL); 521 } 522 523 len = decode_addr_size(upl, buf, size, &gra->reg); 524 if (len < 0) 525 return log_msg_ret("buf", len); 526 527 ret = read_uint(node, UPLP_WIDTH, &gra->width); 528 if (!ret) 529 ret = read_uint(node, UPLP_HEIGHT, &gra->height); 530 if (!ret) 531 ret = read_uint(node, UPLP_STRIDE, &gra->stride); 532 if (!ret) { 533 ret = ofnode_read_value(node, UPLP_GRAPHICS_FORMAT, 534 graphics_formats, 535 ARRAY_SIZE(graphics_formats), 536 &gra->format); 537 } 538 if (ret) 539 return log_msg_ret("pro", ret); 540 541 return 0; 542} 543 544int upl_read_handoff(struct upl *upl, oftree tree) 545{ 546 ofnode root, node; 547 int ret; 548 549 if (!oftree_valid(tree)) 550 return log_msg_ret("tre", -EINVAL); 551 552 root = oftree_root(tree); 553 554 upl_init(upl); 555 ret = decode_root_props(upl, root); 556 if (ret) 557 return log_msg_ret("roo", ret); 558 559 ofnode_for_each_subnode(node, root) { 560 const char *name = ofnode_get_name(node); 561 562 log_debug("decoding '%s'\n", name); 563 if (!strcmp(UPLN_OPTIONS, name)) { 564 ret = decode_upl_params(upl, node); 565 if (ret) 566 return log_msg_ret("opt", ret); 567 568 ret = decode_upl_images(upl, node); 569 } else if (node_matches_at(node, UPLN_MEMORY)) { 570 ret = decode_upl_memory_node(upl, node); 571 } else if (!strcmp(UPLN_MEMORY_MAP, name)) { 572 ret = decode_upl_memmap(upl, node); 573 } else if (!strcmp(UPLN_MEMORY_RESERVED, name)) { 574 ret = decode_upl_memres(upl, node); 575 } else if (node_matches_at(node, UPLN_SERIAL)) { 576 ret = decode_upl_serial(upl, node); 577 } else if (node_matches_at(node, UPLN_GRAPHICS)) { 578 ret = decode_upl_graphics(upl, node); 579 } else { 580 log_debug("Unknown node '%s'\n", name); 581 ret = 0; 582 } 583 if (ret) 584 return log_msg_ret("err", ret); 585 } 586 587 return 0; 588}