[PATCH kvmtool] riscv, arm64, powerpc: Align the FDT staging buffer to 8 bytes
Mika Tammi
mikatammi at gmail.com
Wed Oct 28 12:05:31 PDT 2026
setup_fdt() creates the guest's device tree in staging_fdt, a u8 array
on the stack, and then copies it into guest memory with fdt_open_into().
The Devicetree Specification requires the blob to be at an 8-byte
aligned address [1], and libfdt has checked this since dtc v1.6.1:
fdt_ro_probe_() fails with -FDT_ERR_ALIGNMENT for any other address.
The sequential-write functions don't call it, so the tree is created
without errors and fdt_open_into() is the first call to fail.
A u8 array has no alignment requirement beyond its element type, so
whether staging_fdt is 8-byte aligned depends on how the compiler lays
out the stack frame. In lkvm built for 32-bit RISC-V with GCC 15 and
musl, it isn't, and lkvm fails every time:
Fatal: Error creating device tree: fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE): FDT_ERR_ALIGNMENT
arm64 and powerpc create their device trees the same way. Nothing
guarantees the alignment there either, though it hasn't been seen to
fail.
Ask the compiler for the alignment that the specification requires.
[1] Devicetree Specification v0.4, section 5.6 "Alignment": "The
devicetree blob shall be located at an 8-byte-aligned address."
https://github.com/devicetree-org/devicetree-specification/releases/download/v0.4/devicetree-specification-v0.4.pdf
Fixes: 7c9aac003925 ("riscv: Generate FDT at runtime for Guest/VM")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Mika Tammi <mikatammi at gmail.com>
---
I used Claude Opus 5.5 to track down the failure, write this patch
and its description, and run the tests below; I have reviewed the
result.
Tested with QEMU rv32 and rv64 machines with the H extension as KVM
hosts, running a guest with lkvm built with GCC 15.3, musl 1.2.5 and
libfdt 1.7.2. Without this patch, lkvm on rv32 fails with the error
above; with it, the guest boots on both. Build-tested for x86_64,
arm64, riscv64 and ppc64le.
arm64/fdt.c | 2 +-
powerpc/kvm.c | 2 +-
riscv/fdt.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arm64/fdt.c b/arm64/fdt.c
index 3cbd36e..ec83caa 100644
--- a/arm64/fdt.c
+++ b/arm64/fdt.c
@@ -115,7 +115,7 @@ static struct psci_fns psci_0_2_aarch64_fns = {
static int setup_fdt(struct kvm *kvm)
{
struct device_header *dev_hdr;
- u8 staging_fdt[FDT_MAX_SIZE];
+ u8 staging_fdt[FDT_MAX_SIZE] __attribute__((aligned(8)));
u64 resv_mem_prop, mem_reg_prop[] = {
cpu_to_fdt64(kvm->arch.memory_guest_start),
cpu_to_fdt64(kvm->ram_size),
diff --git a/powerpc/kvm.c b/powerpc/kvm.c
index 7b0d066..fb374dd 100644
--- a/powerpc/kvm.c
+++ b/powerpc/kvm.c
@@ -296,7 +296,7 @@ static int setup_fdt(struct kvm *kvm)
"hcall-splpar\0hcall-bulk\0hcall-set-mode";
int i, j;
char cpu_name[30];
- u8 staging_fdt[FDT_MAX_SIZE];
+ u8 staging_fdt[FDT_MAX_SIZE] __attribute__((aligned(8)));
struct cpu_info *cpu_info = find_cpu_info(kvm);
struct fdt_prop segment_page_sizes;
u32 segment_sizes_1T[] = {cpu_to_be32(0x1c), cpu_to_be32(0x28), 0xffffffff, 0xffffffff};
diff --git a/riscv/fdt.c b/riscv/fdt.c
index 1eb45fc..4a2c456 100644
--- a/riscv/fdt.c
+++ b/riscv/fdt.c
@@ -341,7 +341,7 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
static int setup_fdt(struct kvm *kvm)
{
struct device_header *dev_hdr;
- u8 staging_fdt[FDT_MAX_SIZE];
+ u8 staging_fdt[FDT_MAX_SIZE] __attribute__((aligned(8)));
u64 mem_reg_prop[] = {
cpu_to_fdt64(kvm->arch.memory_guest_start),
cpu_to_fdt64(kvm->ram_size),
--
2.54.0 (Apple Git-157)
More information about the kvm-riscv
mailing list