Alper Nebi Yasak has uploaded this change for review.

View Change

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
broken coreboot on these emulation boards.

The qemu-aarch64 mainboard is intended for the "virt" model and had this
issue, which was fixed by using exception handlers in the RAM detection
process [2].

The qemu-riscv mainboard is also for "virt" and still has this issue.
There is a potential fix based on the exception handler approach [3],
but it fails to build for 32-bit RISC-V. There's also a WIP attempt on
parsing the in-memory device-tree that QEMU provides us [4], but it
relies on unflattening which wouldn't work on romstage.

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 [5][6] recommend reading the
device tree blob it provides for device information (incl. RAM size).
Implement a function that parses the device tree blob to find 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.

This assumes a single memory node with a single reg range which seems
to be enough for what QEMU provides for now, support for more complex
device-trees (as supported by specification) is left as future work.

[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://review.coreboot.org/c/coreboot/+/78981
[5] https://qemu-project.gitlab.io/qemu/system/arm/virt.html
[6] https://qemu-project.gitlab.io/qemu/system/riscv/virt.html

Change-Id: I8bef09bc1bc4e324ebeaa37f78d67d3aa315f52c
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
---
M src/include/device_tree.h
M src/lib/Makefile.mk
M src/lib/device_tree.c
3 files changed, 102 insertions(+), 1 deletion(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/22/80322/1
diff --git a/src/include/device_tree.h b/src/include/device_tree.h
index 02fcaa7..95fb30c 100644
--- a/src/include/device_tree.h
+++ b/src/include/device_tree.h
@@ -100,6 +100,9 @@
void fdt_print_node(const void *blob, uint32_t offset);
int fdt_skip_node(const void *blob, uint32_t offset);

+ /* Find top of memory from a flat device-tree. */
+uintptr_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 2a95be9..ae149e4 100644
--- a/src/lib/Makefile.mk
+++ b/src/lib/Makefile.mk
@@ -162,10 +162,12 @@
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 ab9c937b..ed2b56f 100644
--- a/src/lib/device_tree.c
+++ b/src/lib/device_tree.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
+#include <limits.h>

/*
* Functions for picking apart flattened trees.
@@ -166,6 +167,101 @@
return offset - start_offset + sizeof(uint32_t);
}

+/*
+ * Find top of memory from a flat device-tree.
+ */
+uintptr_t fdt_get_memory_top(const void *blob)
+{
+ struct fdt_property prop;
+ struct fdt_header *header;
+ int size, offset;
+ const char *name;
+
+ header = (struct fdt_header *)blob;
+ if (be32_to_cpu(header->magic) != FDT_HEADER_MAGIC)
+ return 0;
+
+ offset = be32toh(header->structure_offset);
+ size = fdt_node_name(blob, offset, &name);
+ offset += size;
+
+ int addr_cells = 0;
+ int size_cells = 0;
+ while ((size = fdt_next_property(blob, offset, &prop))) {
+ if (!strncmp(prop.name, "#address-cells", sizeof("#address-cells")))
+ addr_cells = be32toh(*(uint32_t *)prop.data);
+ else if (!strncmp(prop.name, "#size-cells", sizeof("#size-cells")))
+ size_cells = be32toh(*(uint32_t *)prop.data);
+
+ offset += size;
+ }
+ if (!addr_cells || !size_cells)
+ return 0;
+
+ /* TODO: Handle multiple memory nodes */
+ uint32_t mem_offset = 0;
+ while ((size = fdt_skip_node(blob, offset))) {
+ fdt_node_name(blob, offset, &name);
+ if (!strncmp(name, "memory", sizeof("memory") ||
+ !strncmp(name, "memory@", sizeof("memory@") - 1))) {
+ mem_offset = offset;
+ break;
+ }
+
+ offset += size;
+ }
+ if (!mem_offset)
+ return 0;
+
+ uint32_t reg_size = 0;
+ uint32_t *reg_data = NULL;
+ size = fdt_node_name(blob, mem_offset, &name);
+ offset = mem_offset + size;
+ while ((size = fdt_next_property(blob, offset, &prop))) {
+ offset += size;
+ if (!strncmp(prop.name, "reg", sizeof("reg"))) {
+ reg_data = (uint32_t *)prop.data;
+ reg_size = prop.size;
+ break;
+ }
+ }
+ if (!reg_size || !reg_data)
+ return 0;
+
+ /* TODO: Handle multiple ranges in reg property */
+ int i;
+ uintptr_t mem_base = 0;
+ uintptr_t mem_size = 0;
+ uintptr_t top = 0;
+ for (i = 0; i < addr_cells; i++) {
+ if (i >= (addr_cells - sizeof(uintptr_t) / 4)) {
+ mem_base = (uintptr_t)((uint64_t)mem_base << 32);
+ mem_base |= be32toh(reg_data[i]);
+ } else if (be32toh(reg_data[i])) {
+ mem_base = UINTPTR_MAX;
+ break;
+ }
+ }
+ for (i = 0; i < size_cells; i++) {
+ if (i >= (size_cells - sizeof(uintptr_t) / 4)) {
+ mem_size = (uintptr_t)((uint64_t)mem_size << 32);
+ mem_size |= be32toh(reg_data[addr_cells + i]);
+ } else if (be32toh(reg_data[addr_cells + i])) {
+ mem_size = UINTPTR_MAX;
+ break;
+ }
+ }
+
+ if (mem_size > UINTPTR_MAX - mem_base)
+ top = UINTPTR_MAX;
+ else
+ top = mem_base + mem_size;
+
+ printk(BIOS_DEBUG, "FDT: Found %u MiB of addressable RAM\n",
+ (uint32_t)((top - mem_base) / MiB));
+
+ return top;
+}


/*

To view, visit change 80322. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: main
Gerrit-Change-Id: I8bef09bc1bc4e324ebeaa37f78d67d3aa315f52c
Gerrit-Change-Number: 80322
Gerrit-PatchSet: 1
Gerrit-Owner: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Gerrit-MessageType: newchange