[RFC PATCH] tools: zynqmp: add build script and documentation for ZynqMP KV260
Jerome Forissier
jerome.forissier at linaro.org
Thu Jun 12 17:32:41 CEST 2025
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.forissier at 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
More information about the U-Boot
mailing list