Add a script to help build a functional U-Boot binary for the ZynqMP
Kria KV260. Also add some documentation.

Signed-off-by: Jerome Forissier <jerome.foriss...@linaro.org>
---

 doc/board/xilinx/index.rst        |  1 +
 doc/board/xilinx/zynqmp-kv260.rst | 27 +++++++++
 tools/zynqmp_kv260_build.sh       | 43 ++++++++++++++
 tools/zynqmp_pmufw_elf_convert.py | 96 +++++++++++++++++++++++++++++++
 4 files changed, 167 insertions(+)
 create mode 100644 doc/board/xilinx/zynqmp-kv260.rst
 create mode 100755 tools/zynqmp_kv260_build.sh
 create mode 100755 tools/zynqmp_pmufw_elf_convert.py

diff --git a/doc/board/xilinx/index.rst b/doc/board/xilinx/index.rst
index 2e31fe3f3a4..3f3a85b709c 100644
--- a/doc/board/xilinx/index.rst
+++ b/doc/board/xilinx/index.rst
@@ -9,4 +9,5 @@ Xilinx
    xilinx
    zynq
    zynqmp
+   zynqmp-kv260
    zynqmp-r5
diff --git a/doc/board/xilinx/zynqmp-kv260.rst 
b/doc/board/xilinx/zynqmp-kv260.rst
new file mode 100644
index 00000000000..219bbd602e9
--- /dev/null
+++ b/doc/board/xilinx/zynqmp-kv260.rst
@@ -0,0 +1,27 @@
+.. SPDX-License-Identifier: GPL-2.0
+..  (C) Copyright 2025 Linaro Ltd.
+
+ZYNQMP KV260
+============
+
+Building
+--------
+
+To build for the KV260:
+
+   $ ./tools/zynqmp_kv260_build.sh
+
+The first invocation will fetch and build some required binaries (bl31.bin,
+pm_cfg_obj.o, pmufw.bin). Subsequently `make` can be invoked directly as
+follows:
+
+   $ export BL31=bl31.bin
+   # export CROSS_COMPILE=aarch64-linux-gnu-
+   $ make -j$(nproc) BINMAN_ALLOW_MISSING=1
+
+Flashing
+--------
+
+Press the FWUEN button on the carrier board, the press and release the RESET
+button, then release FWUEN. Connect to the embedded HTTP server and upload
+`qspi.bin`. Press and release RESET again.
diff --git a/tools/zynqmp_kv260_build.sh b/tools/zynqmp_kv260_build.sh
new file mode 100755
index 00000000000..3cf7147e3a6
--- /dev/null
+++ b/tools/zynqmp_kv260_build.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 Linaro Ltd.
+
+set -ex
+
+if [ "$1" == "-c" ]; then
+  rm -f bl31.bin pmufw.elf pmufw.bin pm_cfg_obj.c pm_cfg_obj.o
+  exit 0
+fi
+[ -e bl31.bin ] || {
+  ATF_SRC=$(mktemp -d /tmp/arm-trusted-firmware.XXXXXXXXXX)
+  git clone https://github.com/ARM-software/arm-trusted-firmware ${ATF_SRC}
+  make -C ${ATF_SRC} -j$(nproc) bl31 CROSS_COMPILE=aarch64-linux-gnu- \
+         LOG_LEVEL=40 ZYNQMP_CONSOLE=cadence1 CONSOLE_RUNTIME=cadence1 \
+         RESET_TO_BL31=1 PLAT=zynqmp ZYNQMP_ATF_MEM_BASE=0x10000 \
+         ZYNQMP_ATF_MEM_SIZE=0x2ffff ENABLE_LTO=1
+  cp ${ATF_SRC}/build/zynqmp/release/bl31.bin .
+  rm -rf ${ATF_SRC}
+}
+[ -e pmufw.bin ] || {
+  wget 
https://github.com/Xilinx/soc-prebuilt-firmware/raw/refs/heads/xlnx_rel_v2023.1/kv260-kria/pmufw.elf
+  ./tools/zynqmp_pmufw_elf_convert.py pmufw.elf pmufw.bin
+}
+[ -e pm_cfg_obj.o ] || {
+  wget 
https://github.com/Xilinx/embeddedsw/raw/refs/heads/xlnx_rel_v2023.1/lib/sw_apps/zynqmp_fsbl/misc/pm_cfg_obj.c
+  ./tools/zynqmp_pm_cfg_obj_convert.py pm_cfg_obj.c pm_cfg_obj.o
+}
+[ -e .config ] || {
+  make xilinx_zynqmp_kria_defconfig
+  cat << EOF >> .config
+CONFIG_PMUFW_INIT_FILE="pmufw.bin"
+CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE="pm_cfg_obj.o"
+CONFIG_BL31_LOAD_ADDR=0x10000
+CONFIG_BL32_LOAD_ADDR=0
+EOF
+}
+export BL31=bl31.bin
+export BL32=
+export TEE=
+export CROSS_COMPILE=aarch64-linux-gnu-
+make -j$(nproc) BINMAN_ALLOW_MISSING=1
diff --git a/tools/zynqmp_pmufw_elf_convert.py 
b/tools/zynqmp_pmufw_elf_convert.py
new file mode 100755
index 00000000000..b4eb2695831
--- /dev/null
+++ b/tools/zynqmp_pmufw_elf_convert.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2025 Linaro Ltd.
+#
+# Written by Gemini (Google AI) then reviewed and edited to remove some 
comments
+
+import sys
+from elftools.elf.elffile import ELFFile
+from elftools.elf.segments import Segment
+from elftools.common.exceptions import ELFError
+
+def elf_to_bin_with_elftools(elf_path, bin_path):
+    """
+    Converts a Microblaze ELF file to a raw binary (.bin)
+    by extracting LOAD segments using the elftools library.
+
+    Args:
+        elf_path (str): Path to the input .elf file.
+        bin_path (str): Path for the output .bin file.
+    """
+    try:
+        with open(elf_path, 'rb') as f:
+            elffile = ELFFile(f)
+
+            if elffile.header['e_machine'] not in ('EM_MICROBLAZE', 
'EM_MICROBLAZE_OLD'):
+                print(f"Error: ELF machine type is 
{elffile.header['e_machine']}, "
+                      f"not typical Microblaze.")
+                return
+
+            print(f"ELF Header Info:")
+            print(f"  Entry Point: 0x{elffile.header['e_entry']:08X}")
+            print(f"  Endianness: {elffile.little_endian and 'Little-endian' 
or 'Big-endian'}")
+
+            load_segments = []
+            for segment in elffile.iter_segments():
+                if segment.header['p_type'] == 'PT_LOAD':
+                    load_segments.append(segment)
+                    print(f"  Found LOAD segment:")
+                    print(f"    Offset: 0x{segment.header['p_offset']:08X}, 
VAddr: 0x{segment.header['p_vaddr']:08X}")
+                    print(f"    File Size: {segment.header['p_filesz']} bytes, 
Memory Size: {segment.header['p_memsz']} bytes")
+
+            if not load_segments:
+                print("Error: No LOADable segments found in the ELF file. 
Cannot create binary.")
+                return
+
+            load_segments.sort(key=lambda seg: seg.header['p_vaddr'])
+
+            min_vaddr = load_segments[0].header['p_vaddr']
+            max_vaddr = 0
+            for segment in load_segments:
+                end_vaddr = segment.header['p_vaddr'] + 
segment.header['p_memsz']
+                if end_vaddr > max_vaddr:
+                    max_vaddr = end_vaddr
+
+            total_bin_size = max_vaddr - min_vaddr
+            print(f"Calculated binary memory range: 0x{min_vaddr:08X} to 
0x{max_vaddr:08X} (Size: {total_bin_size} bytes)")
+
+            output_buffer = bytearray(total_bin_size)
+
+            for segment in load_segments:
+                buffer_start = segment.header['p_vaddr'] - min_vaddr
+                buffer_end = buffer_start + segment.header['p_memsz'] # Using 
memsz for bounds check
+
+                if buffer_end > total_bin_size:
+                    print(f"Warning: Segment at 
0x{segment.header['p_vaddr']:08X} "
+                          f"extends beyond calculated total binary size. 
Truncating.")
+                    buffer_end = total_bin_size
+
+                segment_data = segment.data()
+
+                output_buffer[buffer_start : buffer_start + len(segment_data)] 
= segment_data
+
+                # The remaining part of the segment in memory (if p_memsz > 
p_filesz)
+                # is already zeroed due to output_buffer initialization.
+
+            with open(bin_path, 'wb') as out_f:
+                out_f.write(output_buffer)
+
+            print(f"Successfully created '{bin_path}' (size: 
{len(output_buffer)} bytes) from '{elf_path}'.")
+
+    except FileNotFoundError:
+        print(f"Error: ELF file not found at '{elf_path}'")
+    except ELFError as e:
+        print(f"Error parsing ELF file with elftools: {e}. The file might be 
malformed or not a valid ELF.")
+    except Exception as e:
+        print(f"An unexpected error occurred: {e}")
+
+if __name__ == "__main__":
+    if len(sys.argv) != 3:
+        print("Usage: python elf_to_bin.py <input_elf_file> <output_bin_file>")
+        sys.exit(1)
+
+    input_elf = sys.argv[1]
+    output_bin = sys.argv[2]
+
+    elf_to_bin_with_elftools(input_elf, output_bin)
-- 
2.43.0

base-commit: fcdb871cb79e21d53fa8ef241dd0f06336c27d47
branch: kv260-spl-build

Reply via email to