283 lines
10 KiB
Plaintext
283 lines
10 KiB
Plaintext
#
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
#
|
|
# Copyright (c) 2013-2026 Igor Pecovnik, [email protected]
|
|
#
|
|
# This file is a part of the Armbian Build Framework
|
|
# https://github.com/armbian/build/
|
|
#
|
|
|
|
# Nuvoton MA35D1 SoC family - ARM Cortex-A35 dual-core + Cortex-M4
|
|
# Boot flow: Mask ROM (IBR) -> BL2 (TF-A) -> BL31 -> BL32 (OP-TEE) -> BL33 (U-Boot) -> Linux
|
|
declare -g ARCH="arm64"
|
|
|
|
# Disable BTF - 5.10.y kernel has issues with newer pahole/toolchain
|
|
declare -g KERNEL_BTF="no"
|
|
|
|
# Use NetworkManager for network configuration
|
|
declare -g NETWORKING_STACK="network-manager"
|
|
|
|
declare -g ATF_PLAT="ma35d1"
|
|
declare -g BOOTDELAY=1
|
|
declare -g BOOTPATCHDIR="u-boot-nuvoton-ma35d1"
|
|
|
|
# U-Boot source from Nuvoton
|
|
declare -g BOOTSOURCE='https://github.com/OpenNuvoton/MA35D1_u-boot-v2020.07.git'
|
|
declare -g BOOTBRANCH='branch:master'
|
|
declare -g BOOTDIR='u-boot-nuvoton-ma35d1'
|
|
declare -g UBOOT_TARGET_MAP=";;u-boot.bin header.bin fip.bin bl2.bin bl31.bin bl2-ma35d1.dtb tee-header_v2.bin tee-pager_v2.bin"
|
|
|
|
# TF-A source from Nuvoton
|
|
declare -g ATFSOURCE="https://github.com/OpenNuvoton/MA35D1_arm-trusted-firmware-v2.3.git"
|
|
declare -g ATFDIR="arm-trusted-firmware-ma35d1"
|
|
declare -g ATFBRANCH="branch:master"
|
|
declare -g ATFPATCHDIR="atf-nuvoton-ma35d1"
|
|
declare -g ATF_SKIP_LDFLAGS_WL="yes"
|
|
|
|
# DDR configuration DTB - 512MB for IoT board
|
|
MA35D1_DDR_DTB="${MA35D1_DDR_DTB:-ma35d1-cpu800-wb-512m}"
|
|
|
|
# TF-A build: BL2 + BL31 + DTB (FIP is created later in uboot_custom_postprocess)
|
|
declare -g ATF_TARGET_MAP="PLAT=ma35d1 DEBUG=1 DTB_FILE_NAME=${MA35D1_DDR_DTB}.dtb bl2 bl31 dtbs;;build/ma35d1/debug/bl2.bin build/ma35d1/debug/bl31.bin build/ma35d1/debug/fdts/${MA35D1_DDR_DTB}.dtb:bl2-ma35d1.dtb"
|
|
|
|
declare -g LINUXFAMILY="nuvoton-ma35d1"
|
|
declare -g IMAGE_PARTITION_TABLE="msdos"
|
|
|
|
# Boot script for loading kernel/initrd/dtb from partition
|
|
declare -g BOOTSCRIPT="boot-nuvoton-ma35d1.cmd:boot.cmd"
|
|
declare -g BOOTENV_FILE="nuvoton-ma35d1.txt"
|
|
|
|
case "${BRANCH}" in
|
|
vendor)
|
|
declare -g KERNEL_MAJOR_MINOR="5.10"
|
|
declare -g KERNELSOURCE='https://github.com/OpenNuvoton/MA35D1_linux-5.10.y.git'
|
|
declare -g KERNELBRANCH="branch:master"
|
|
declare -g KERNELPATCHDIR="nuvoton-ma35d1-${BRANCH}"
|
|
declare -g LINUXCONFIG="linux-nuvoton-ma35d1-${BRANCH}"
|
|
;;
|
|
esac
|
|
|
|
declare -g KERNEL_TARGET="vendor"
|
|
declare -g CPUMIN=180000
|
|
declare -g CPUMAX=800000
|
|
declare -g GOVERNOR="ondemand"
|
|
|
|
# Host dependencies for OP-TEE build
|
|
function add_host_dependencies__ma35d1_optee_deps() {
|
|
display_alert "Adding MA35D1 OP-TEE host dependencies" "python3-pycryptodome python3-pyelftools" "info"
|
|
declare -g EXTRA_BUILD_DEPS="${EXTRA_BUILD_DEPS} python3-pycryptodome python3-pyelftools"
|
|
}
|
|
|
|
# Fetch OP-TEE source using Armbian's fetch_from_repo mechanism
|
|
function fetch_sources_tools__ma35d1_fetch_optee() {
|
|
local optee_source="https://github.com/OpenNuvoton/MA35D1_optee_os-v3.9.0.git"
|
|
local optee_dir="optee-os-ma35d1"
|
|
local optee_branch="branch:master"
|
|
|
|
display_alert "Fetching OP-TEE source" "for MA35D1" "info"
|
|
fetch_from_repo "${optee_source}" "${optee_dir}" "${optee_branch}"
|
|
}
|
|
|
|
# ATF post-processing: verify build outputs and build fiptool
|
|
function atf_custom_postprocess() {
|
|
display_alert "Post-processing ATF" "MA35D1" "info"
|
|
|
|
local atf_src_dir="$(pwd)"
|
|
local atf_build_dir="${atf_src_dir}/build/ma35d1/debug"
|
|
|
|
if [[ ! -d "${atf_build_dir}" ]]; then
|
|
exit_with_error "ATF build directory not found: ${atf_build_dir}"
|
|
fi
|
|
|
|
# Verify required files
|
|
[[ -f "${atf_build_dir}/bl2.bin" ]] || exit_with_error "bl2.bin not found in ${atf_build_dir}"
|
|
[[ -f "${atf_build_dir}/bl31.bin" ]] || exit_with_error "bl31.bin not found in ${atf_build_dir}"
|
|
[[ -f "${atf_build_dir}/fdts/${MA35D1_DDR_DTB}.dtb" ]] || exit_with_error "DTB ${MA35D1_DDR_DTB}.dtb not found"
|
|
|
|
# Build fiptool for use in uboot_custom_postprocess
|
|
display_alert "Building fiptool" "MA35D1" "info"
|
|
run_host_command_logged make -C "${atf_src_dir}/tools/fiptool" HOSTCC=gcc
|
|
[[ -x "${atf_src_dir}/tools/fiptool/fiptool" ]] || exit_with_error "fiptool build failed"
|
|
|
|
display_alert "ATF post-processing complete" "bl2.bin + bl31.bin + fiptool ready" "info"
|
|
}
|
|
|
|
# Post-process U-Boot: create FIP and Nuvoton boot header
|
|
function uboot_custom_postprocess() {
|
|
# Compile OP-TEE for MA35D1 (defined inside for correct hashing)
|
|
function compile_optee_ma35d1() {
|
|
local optee_src_dir="${SRC}/cache/sources/optee-os-ma35d1"
|
|
local optee_out_dir="${optee_src_dir}/out/arm-plat-nuvoton/core"
|
|
|
|
display_alert "Compiling OP-TEE" "MA35D1" "info"
|
|
|
|
[[ -d "${optee_src_dir}" ]] || exit_with_error "OP-TEE source not found at ${optee_src_dir}"
|
|
|
|
local cross_compile="aarch64-linux-gnu-"
|
|
|
|
# Check if linker supports --no-warn-rwx-segments (binutils 2.39+)
|
|
# Workaround for ubuntu:jammy older toolchain
|
|
local ldflags=""
|
|
if ${cross_compile}ld --help 2>&1 | grep -q "no-warn-rwx-segments"; then
|
|
ldflags="--no-warn-rwx-segments"
|
|
fi
|
|
|
|
run_host_command_logged make -C "${optee_src_dir}" clean PLATFORM=nuvoton-MA35D1 2>/dev/null || true
|
|
|
|
display_alert "Building OP-TEE" "PLATFORM=nuvoton-MA35D1" "info"
|
|
run_host_command_logged make -C "${optee_src_dir}" \
|
|
CROSS_COMPILE_core="${cross_compile}" \
|
|
CROSS_COMPILE_ta_arm64="${cross_compile}" \
|
|
PLATFORM=nuvoton-MA35D1 \
|
|
CFG_ARM64_core=y \
|
|
CFG_TEE_CORE_LOG_LEVEL=1 \
|
|
LDFLAGS="${ldflags}" \
|
|
NOWERROR=1 \
|
|
-j$(nproc)
|
|
|
|
[[ -f "${optee_out_dir}/tee-header_v2.bin" ]] || exit_with_error "OP-TEE build failed: tee-header_v2.bin not found"
|
|
[[ -f "${optee_out_dir}/tee-pager_v2.bin" ]] || exit_with_error "OP-TEE build failed: tee-pager_v2.bin not found"
|
|
|
|
display_alert "OP-TEE built successfully" "MA35D1" "info"
|
|
}
|
|
|
|
display_alert "Creating MA35D1 boot images" "BL2 + OP-TEE + FIP + Header" "info"
|
|
|
|
local atf_dir="${SRC}/cache/sources/${ATFDIR}/${ATFBRANCH##*:}"
|
|
local atf_build_dir="${atf_dir}/build/ma35d1/debug"
|
|
local fiptool="${atf_dir}/tools/fiptool/fiptool"
|
|
|
|
# Verify fiptool exists (built in atf_custom_postprocess)
|
|
[[ -x "${fiptool}" ]] || exit_with_error "fiptool not found at ${fiptool}"
|
|
|
|
# Copy DTB from ATF build if not already present
|
|
if [[ ! -f "bl2-ma35d1.dtb" ]] && [[ -f "${atf_build_dir}/fdts/${MA35D1_DDR_DTB}.dtb" ]]; then
|
|
cp "${atf_build_dir}/fdts/${MA35D1_DDR_DTB}.dtb" "bl2-ma35d1.dtb"
|
|
fi
|
|
|
|
# Verify required files
|
|
[[ -f "bl2.bin" ]] || exit_with_error "bl2.bin not found"
|
|
[[ -f "bl31.bin" ]] || exit_with_error "bl31.bin not found"
|
|
[[ -f "bl2-ma35d1.dtb" ]] || exit_with_error "bl2-ma35d1.dtb not found"
|
|
|
|
# Build OP-TEE
|
|
compile_optee_ma35d1
|
|
|
|
# Copy OP-TEE binaries
|
|
local optee_out_dir="${SRC}/cache/sources/optee-os-ma35d1/out/arm-plat-nuvoton/core"
|
|
cp "${optee_out_dir}/tee-header_v2.bin" .
|
|
cp "${optee_out_dir}/tee-pager_v2.bin" .
|
|
|
|
# Create FIP
|
|
display_alert "Creating FIP" "bl31 + optee + u-boot" "info"
|
|
run_host_command_logged "${fiptool}" create \
|
|
--soc-fw bl31.bin \
|
|
--tos-fw tee-header_v2.bin \
|
|
--tos-fw-extra1 tee-pager_v2.bin \
|
|
--nt-fw u-boot.bin \
|
|
fip.bin
|
|
|
|
# Create Nuvoton boot header for SD card boot
|
|
display_alert "Creating Nuvoton boot header" "SD card" "info"
|
|
|
|
python3 <<- 'HEADER_SCRIPT'
|
|
import struct
|
|
import os
|
|
import binascii
|
|
|
|
HEADER_VERSION = 0x20210107
|
|
BL2_ENTRY = 0x28000000
|
|
BL2_DTB_LOAD = 0x28023000
|
|
BL2_LOAD = 0x28000000
|
|
IMG_TYPE_DATA = 3
|
|
IMG_TYPE_EXEC = 4
|
|
|
|
dtb_offset = 0x20000
|
|
bl2_offset = 0x30000
|
|
|
|
bl2_size = os.path.getsize('bl2.bin')
|
|
dtb_size = os.path.getsize('bl2-ma35d1.dtb') if os.path.exists('bl2-ma35d1.dtb') else 0
|
|
|
|
header = bytearray(512)
|
|
struct.pack_into('<I', header, 0, 0x4E565420)
|
|
struct.pack_into('<I', header, 8, 0xC0)
|
|
struct.pack_into('<I', header, 12, HEADER_VERSION)
|
|
|
|
struct.pack_into('<H', header, 16, 2048)
|
|
struct.pack_into('<H', header, 18, 64)
|
|
struct.pack_into('<H', header, 20, 64)
|
|
struct.pack_into('<B', header, 22, 0x6B)
|
|
struct.pack_into('<B', header, 23, 0x05)
|
|
struct.pack_into('<B', header, 24, 0x01)
|
|
struct.pack_into('<B', header, 25, 0x02)
|
|
struct.pack_into('<B', header, 26, 0)
|
|
struct.pack_into('<B', header, 27, 1)
|
|
struct.pack_into('<B', header, 28, 1)
|
|
for i in range(29, 32):
|
|
header[i] = 0xFF
|
|
|
|
struct.pack_into('<I', header, 32, BL2_ENTRY)
|
|
struct.pack_into('<I', header, 36, 2)
|
|
|
|
desc_offset = 40
|
|
DESC_SIZE = 80
|
|
|
|
struct.pack_into('<I', header, desc_offset + 0, dtb_offset)
|
|
struct.pack_into('<I', header, desc_offset + 4, BL2_DTB_LOAD)
|
|
struct.pack_into('<I', header, desc_offset + 8, dtb_size)
|
|
struct.pack_into('<I', header, desc_offset + 12, IMG_TYPE_DATA)
|
|
for i in range(desc_offset + 16, desc_offset + DESC_SIZE):
|
|
header[i] = 0xFF
|
|
desc_offset += DESC_SIZE
|
|
|
|
struct.pack_into('<I', header, desc_offset + 0, bl2_offset)
|
|
struct.pack_into('<I', header, desc_offset + 4, BL2_LOAD)
|
|
struct.pack_into('<I', header, desc_offset + 8, bl2_size)
|
|
struct.pack_into('<I', header, desc_offset + 12, IMG_TYPE_EXEC)
|
|
for i in range(desc_offset + 16, desc_offset + DESC_SIZE):
|
|
header[i] = 0xFF
|
|
|
|
total_header_size = 200
|
|
checksum = binascii.crc32(header[8:total_header_size]) & 0xFFFFFFFF
|
|
struct.pack_into('<I', header, 4, checksum)
|
|
|
|
with open('header.bin', 'wb') as f:
|
|
f.write(header[:total_header_size])
|
|
|
|
print(f"Header created: {total_header_size} bytes, CRC=0x{checksum:08x}")
|
|
HEADER_SCRIPT
|
|
|
|
[[ -f "header.bin" ]] || exit_with_error "Failed to create boot header"
|
|
|
|
display_alert "MA35D1 boot images created" "header.bin + bl2.bin + fip.bin" "info"
|
|
}
|
|
|
|
# Write bootloader to SD card/image
|
|
# Note: This only works during image creation, not on target board
|
|
function write_uboot_platform() {
|
|
local src_dir="$1"
|
|
local target_dev="$2"
|
|
|
|
local header_bin="${src_dir}/header.bin"
|
|
local bl2_dtb_bin="${src_dir}/bl2-ma35d1.dtb"
|
|
local bl2_bin="${src_dir}/bl2.bin"
|
|
local fip_bin="${src_dir}/fip.bin"
|
|
|
|
[[ -f "${header_bin}" ]] || { echo "Missing header.bin"; return 1; }
|
|
[[ -f "${bl2_bin}" ]] || { echo "Missing bl2.bin"; return 1; }
|
|
[[ -f "${fip_bin}" ]] || { echo "Missing fip.bin"; return 1; }
|
|
|
|
# Write header at 1KB and 1.5KB offsets (sectors 2 and 3)
|
|
dd if="${header_bin}" of="${target_dev}" bs=512 seek=2 conv=notrunc status=none
|
|
dd if="${header_bin}" of="${target_dev}" bs=512 seek=3 conv=notrunc status=none
|
|
|
|
# Write DTB at 128KB offset (0x20000)
|
|
[[ -f "${bl2_dtb_bin}" ]] && dd if="${bl2_dtb_bin}" of="${target_dev}" bs=1024 seek=128 conv=notrunc status=none
|
|
|
|
# Write BL2 at 192KB offset (0x30000)
|
|
dd if="${bl2_bin}" of="${target_dev}" bs=1024 seek=192 conv=notrunc status=none
|
|
|
|
# Write FIP at 768KB offset (0xC0000)
|
|
dd if="${fip_bin}" of="${target_dev}" bs=1024 seek=768 conv=notrunc status=none
|
|
}
|