From 377157c7fba0c68e2b5d273066713caa5af95f80 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Mon, 5 Feb 2024 17:31:20 +0300 Subject: device_tree: Add function to get top of memory from a FDT blob coreboot needs to figure out top of memory to place CBMEM data. On some non-x86 QEMU virtual machines, this is achieved by probing the RAM space to find where the VM starts discarding data since it's not backed by actual RAM. This behaviour seems to have changed on the QEMU side since then, VMs using the "virt" model have started raising exceptions/errors instead of silently discarding data (likely [1] for example) which has previously broken coreboot on these emulation boards. The qemu-aarch64 and qemu-riscv mainboards are intended for the "virt" models and had this issue, which were mostly fixed by using exception handlers in the RAM detection process [2][3]. But on 32-bit RISC-V we fail to initialize CBMEM if we have 2048 MiB or more of RAM, and on 64-bit RISC-V we had to limit probing to 16383 MiB because it can run into MMIO regions otherwise. The qemu-armv7 mainboard code is intended for the "vexpress-a9" model VM which doesn't appear to suffer from this issue. Still, the issue can be observed on the ARMv7 "virt" model via a port based on qemu-aarch64. QEMU docs for ARM and RISC-V "virt" models [4][5] recommend reading the device tree blob it provides for device information (incl. RAM size). Implement functions that parse the device tree blob to find described memory regions and calculate the top of memory in order to use it in mainboard code as an alternative to probing RAM space. ARM64 code initializes CBMEM in romstage where malloc isn't available, so take care to do parsing without unflattening the blob and make the code available in romstage as well. [1] https://lore.kernel.org/qemu-devel/1504626814-23124-1-git-send-email-peter.maydell@linaro.org/T/#u [2] https://review.coreboot.org/c/coreboot/+/34774 [3] https://review.coreboot.org/c/coreboot/+/36486 [4] https://qemu-project.gitlab.io/qemu/system/arm/virt.html [5] https://qemu-project.gitlab.io/qemu/system/riscv/virt.html Change-Id: I8bef09bc1bc4e324ebeaa37f78d67d3aa315f52c Signed-off-by: Alper Nebi Yasak Reviewed-on: https://review.coreboot.org/c/coreboot/+/80322 Reviewed-by: Maximilian Brune Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- src/include/device_tree.h | 6 +++ src/lib/Makefile.mk | 4 +- src/lib/device_tree.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/include/device_tree.h b/src/include/device_tree.h index 6d2d65600f..bb522bf1da 100644 --- a/src/include/device_tree.h +++ b/src/include/device_tree.h @@ -129,6 +129,12 @@ u32 fdt_find_node_by_alias(const void *blob, const char *alias_name, */ int fdt_next_node_name(const void *blob, uint32_t node_offset, const char **name); + /* Read memory regions from a flat device-tree. */ +size_t fdt_read_memory_regions(const void *blob, struct device_tree_region regions[], + size_t regions_count); + /* Find top of memory from a flat device-tree. */ +uint64_t fdt_get_memory_top(const void *blob); + /* Read a flattened device tree into a hierarchical structure which refers to the contents of the flattened tree in place. Modifying the flat tree invalidates the unflattened one. */ diff --git a/src/lib/Makefile.mk b/src/lib/Makefile.mk index e22fd08723..59e2116e30 100644 --- a/src/lib/Makefile.mk +++ b/src/lib/Makefile.mk @@ -160,10 +160,12 @@ ramstage-$(CONFIG_GENERIC_GPIO_LIB) += gpio.c ramstage-$(CONFIG_GENERIC_UDELAY) += timer.c ramstage-y += b64_decode.c ramstage-$(CONFIG_ACPI_NHLT) += nhlt.c -ramstage-$(CONFIG_FLATTENED_DEVICE_TREE) += device_tree.c ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) += fit.c ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) += fit_payload.c +romstage-$(CONFIG_FLATTENED_DEVICE_TREE) += device_tree.c +ramstage-$(CONFIG_FLATTENED_DEVICE_TREE) += device_tree.c + romstage-$(CONFIG_TIMER_QUEUE) += timer_queue.c ramstage-$(CONFIG_TIMER_QUEUE) += timer_queue.c diff --git a/src/lib/device_tree.c b/src/lib/device_tree.c index 4f5cc07e91..5087d3940d 100644 --- a/src/lib/device_tree.c +++ b/src/lib/device_tree.c @@ -12,9 +12,12 @@ #include #include #include +#include #define FDT_PATH_MAX_DEPTH 10 // should be a good enough upper bound #define FDT_PATH_MAX_LEN 128 // should be a good enough upper bound +#define FDT_MAX_MEMORY_NODES 4 // should be a good enough upper bound +#define FDT_MAX_MEMORY_REGIONS 16 // should be a good enough upper bound /* * Functions for picking apart flattened trees. @@ -503,6 +506,96 @@ void fdt_print_node(const void *blob, uint32_t offset) print_flat_node(blob, offset, 0); } +/* + * fdt_read_memory_regions finds memory ranges from a flat device-tree + * + * @params blob address of FDT + * @params regions all regions that are read inside the reg property of + * memory nodes are saved inside this array + * @params regions_count maximum number of entries that can be saved inside + * the regions array. + * + * Returns: Either 0 on error or returns the number of regions put into the regions array. + */ +size_t fdt_read_memory_regions(const void *blob, + struct device_tree_region regions[], + size_t regions_count) +{ + u32 node, root, addrcp, sizecp; + u32 nodes[FDT_MAX_MEMORY_NODES] = {0}; + size_t region_idx = 0; + size_t node_count = 0; + + if (!fdt_is_valid(blob)) + return 0; + + node = fdt_find_node_by_path(blob, "/memory", &addrcp, &sizecp); + if (node) { + region_idx += fdt_read_reg_prop(blob, node, addrcp, sizecp, + regions, regions_count); + if (region_idx >= regions_count) { + printk(BIOS_WARNING, "FDT: Too many memory regions\n"); + goto out; + } + } + + root = fdt_find_node_by_path(blob, "/", &addrcp, &sizecp); + node_count = fdt_find_subnodes_by_prefix(blob, root, "memory@", + &addrcp, &sizecp, nodes, + FDT_MAX_MEMORY_NODES); + if (node_count >= FDT_MAX_MEMORY_NODES) { + printk(BIOS_WARNING, "FDT: Too many memory nodes\n"); + /* Can still reading the regions for those we got */ + } + + for (size_t i = 0; i < MIN(node_count, FDT_MAX_MEMORY_NODES); i++) { + region_idx += fdt_read_reg_prop(blob, nodes[i], addrcp, sizecp, + ®ions[region_idx], + regions_count - region_idx); + if (region_idx >= regions_count) { + printk(BIOS_WARNING, "FDT: Too many memory regions\n"); + goto out; + } + } + +out: + for (size_t i = 0; i < MIN(region_idx, regions_count); i++) { + printk(BIOS_DEBUG, "FDT: Memory region [%#llx - %#llx]\n", + regions[i].addr, regions[i].addr + regions[i].size); + } + + return region_idx; +} + +/* + * fdt_get_memory_top finds top of memory from a flat device-tree + * + * @params blob address of FDT + * + * Returns: Either 0 on error or returns the maximum memory address + */ +uint64_t fdt_get_memory_top(const void *blob) +{ + struct device_tree_region regions[FDT_MAX_MEMORY_REGIONS] = {0}; + uint64_t top = 0; + uint64_t total = 0; + size_t count; + + if (!fdt_is_valid(blob)) + return 0; + + count = fdt_read_memory_regions(blob, regions, FDT_MAX_MEMORY_REGIONS); + for (size_t i = 0; i < MIN(count, FDT_MAX_MEMORY_REGIONS); i++) { + top = MAX(top, regions[i].addr + regions[i].size); + total += regions[i].size; + } + + printk(BIOS_DEBUG, "FDT: Found %u MiB of RAM\n", + (uint32_t)(total / MiB)); + + return top; +} + /* * Functions to turn a flattened tree into an unflattened one. */ -- cgit v1.2.3