blob: 8ee1244227f7ade1b19153bba86c1900c8559450 [file]
#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#
cmake_minimum_required(VERSION 3.7.2)
project(elfloader C ASM)
if(KernelArchX86)
# This project is only used on Arm or RISC-V
return()
endif()
include(${KERNEL_FLAGS_PATH})
include(cpio)
set(configure_string "")
config_choice(
ElfloaderImage
ELFLOADER_IMAGE
"Boot image type"
"elf;ElfloaderImageELF;IMAGE_ELF;KernelArchARM OR KernelArchRiscV"
"binary;ElfloaderImageBinary;IMAGE_BINARY;KernelArchARM OR KernelArchRiscV"
"efi;ElfloaderImageEFI;IMAGE_EFI;KernelArchARM"
"uimage;ElfloaderImageUimage;IMAGE_UIMAGE;KernelArchARM"
)
config_choice(
ElfloaderMode
ELFLOADER_MODE
"seL4 mode"
"secure supervisor;ElfloaderModeSSup;ARM_S_SUPERVISOR_MODE;KernelPlatImx6"
"monitor;ElfloaderModeMonitor;ARM_MONITOR_MODE;KernelPlatformTK1 OR KernelPlatImx6"
"hypervisor;ElfloaderModeHyp;ARM_HYPERVISOR_MODE;KernelPlatformTK1"
"non-secure supervisor;ElfloaderModeNSSup;ARM_NS_SUPERVISOR_MODE;KernelPlatformTK1 OR KernelPlatImx6"
)
config_option(
ElfloaderMonitorHook ARM_MONITOR_HOOK "Install SMC call handlers in monitor mode."
DEFAULT OFF
DEPENDS "KernelPlatformTK1 OR KernelPlatImx6"
)
config_option(
ElfloaderGPTPtimersNSPL1Access GPT_PTIMER_NS_PL1_ACCESS
"Enable the GPT physical timer access for NS PL1"
DEFAULT ON
DEPENDS "ElfloaderModeNSSup"
DEFAULT_DISABLED OFF
)
config_option(
ElfloaderErrata764369 ARM_ERRATA_764369
"Work around for a Cortex-A9 errata. Derived from Linux kernel."
DEFAULT ON
DEPENDS "KernelArmCortexA9"
DEFAULT_DISABLED OFF
)
config_choice(
ElfloaderHashInstructions
HASH_INSTRUCTIONS
"Perform a SHA256/MD5 Hash of the of each elf file that the elfloader checks on load"
"hash_none;ElfloaderHashNone;HASH_NONE"
"hash_sha;ElfloaderHashSHA;HASH_SHA"
"hash_md5;ElfloaderHashMD5;HASH_MD5"
)
config_option(
ElfloaderIncludeDtb ELFLOADER_INCLUDE_DTB
"Include DTB in the CPIO in case bootloader doesn't provide one"
DEFAULT ON
DEPENDS "KernelArchARM OR KernelArchRiscV"
DEFAULT_DISABLED OFF
)
config_option(
ElfloaderRootserversLast ELFLOADER_ROOTSERVERS_LAST
"Place the rootserver images at the end of memory"
DEFAULT OFF
DEFAULT_DISABLED OFF
)
config_option(
ElfloaderArmV8LeaveAarch64 ELFLOADER_ARMV8_LEAVE_AARCH64
"Insert aarch64 code to switch to aarch32. Requires the elfloader to be in EL2"
DEFAULT OFF
DEPENDS KernelArchArmV8a
)
add_config_library(elfloader "${configure_string}")
add_compile_options(-D_XOPEN_SOURCE=700 -ffreestanding -Wall -Werror -W -Wextra)
set(linkerScript "${CMAKE_CURRENT_LIST_DIR}/src/arch-${KernelArch}/linker.lds")
if(KernelArchRiscV)
add_compile_options(-mcmodel=medany)
endif()
if(ElfloaderArmV8LeaveAarch64)
# We need to build a aarch64 assembly file during an aarch32 build. We have
# to write custom rules to do this as CMake doesn't support multiple compilers
# within a single build config.
find_program(AARCH64_COMPILER aarch64-linux-gnu-gcc)
if("${AARCH64_COMPILER}" STREQUAL "AARCH64_COMPILER-NOTFOUND")
message(
FATAL_ERROR
"Cannot find 'aarch64-linux-gnu-gcc' program. Use -DAARCH64_COMPILER=compiler"
)
endif()
find_program(AARCH64_OBJCOPY aarch64-linux-gnu-objcopy)
if("${AARCH64_OBJCOPY}" STREQUAL "AARCH64_OBJCOPY-NOTFOUND")
message(
FATAL_ERROR
"Cannot find 'aarch64-linux-gnu-objcopy' program. Use -DAARCH64_OBJCOPY=objcopy"
)
endif()
# Compile crt0_64.S and convert to a binary. This way the actual crt0.S can use
# the .incbin directive and insert the aarch64 instructions before its own.
add_custom_command(
OUTPUT crt0_64.bin crt0_64.o
COMMAND
${AARCH64_COMPILER} -I${CMAKE_CURRENT_SOURCE_DIR}/include/
-I${CMAKE_CURRENT_SOURCE_DIR}/include/arch-arm/64/
-I${CMAKE_CURRENT_SOURCE_DIR}/include/arch-arm/armv/armv8-a/64/ -c
${CMAKE_CURRENT_SOURCE_DIR}/src/arch-arm/32/crt0_64.S -o
${CMAKE_CURRENT_BINARY_DIR}/crt0_64.o
COMMAND
${AARCH64_OBJCOPY} -O binary ${CMAKE_CURRENT_BINARY_DIR}/crt0_64.o crt0_64.bin
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/arch-arm/32/crt0_64.S
)
# We set the OBJECT_DEPENDS property on the crt0.S source file that tells CMake
# any object files created from crt0.S also depend on crt0_64.bin. This causes
# our builds to be rerun correctly.
set(armv8_leave_arch_bin ${CMAKE_CURRENT_BINARY_DIR}/crt0_64.bin)
set_property(SOURCE src/arch-arm/32/crt0.S PROPERTY OBJECT_DEPENDS ${armv8_leave_arch_bin})
endif()
if(KernelSel4ArchAarch64)
# NEON registers aren't necessarily initialized for use before elfloader starts
add_compile_options(-mgeneral-regs-only)
endif()
# Don't allow unaligned data store/load instructions as this will cause an alignment
# fault before the MMU is turned on.
if(KernelSel4ArchAarch64)
add_compile_options(-mstrict-align)
elseif(KernelSel4ArchAarch32)
add_compile_options(-mno-unaligned-access)
endif()
file(
GLOB
files
src/*.c
src/drivers/*.c
src/drivers/smp/*.c
src/drivers/uart/*.c
src/utils/*.c
src/arch-${KernelArch}/*.c
src/arch-${KernelArch}/*.S
src/arch-${KernelArch}/drivers/*.c
src/plat/${KernelPlatform}/*.c
src/binaries/elf/*.c
src/arch-${KernelArch}/${KernelWordSize}/*.c
src/plat/${KernelPlatform}/*.S
src/arch-${KernelArch}/${KernelWordSize}/*.S
)
# We never want to give crt0_64.S to add_executable
list(FILTER files EXCLUDE REGEX "src/arch-arm/32/crt0_64\.S")
if(NOT ElfloaderMonitorHook)
# The CMake documentation is no clear on this, but it seems all variables
# of the form ${xxx} get evaluated first and then the regex is applied
list(FILTER files EXCLUDE REGEX "src/plat/${KernelPlatform}/monitor\.S")
endif()
if(KernelArchARM)
file(
GLOB
arm_files src/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}/*.c
src/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}/*.S
)
list(APPEND files ${arm_files})
endif()
# Prevent any global variables to be placed in *COM* instead of .bss.
# This causes linker errors for duplicate symbol definitions correctly
# and allows the _bss and _bss_end symbols to capture all .bss variables properly.
add_compile_options(-fno-common)
if(ElfloaderImageEFI)
# We cannot control where EFI loads the image and so we must make it relocatable
add_compile_options(-fpic)
if(KernelSel4ArchAarch32)
set(gnuefiArch "arm")
# on aarch32 building with -fno-pie results in the compiler generating
# movt/movw pairs that we can't easily relocate.
add_compile_options(-fpie)
# This flag is not supported by clang but add it in for gcc
if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_compile_options(-mno-single-pic-base)
endif()
else()
set(gnuefiArch "aarch64")
# on aarch64 building with -fno-pie will just use pc-relative addressing.
add_compile_options(-fno-pie -fPIC)
endif()
file(GLOB efi_files src/binaries/efi/*.c)
list(
APPEND
files
${efi_files}
src/binaries/efi/gnuefi/crt0-efi-${gnuefiArch}.S
src/binaries/efi/gnuefi/reloc_${gnuefiArch}.c
)
# We use gnu-efi's linker script on EFI.
set(linkerScript ${CMAKE_CURRENT_LIST_DIR}/src/binaries/efi/gnuefi/elf_${gnuefiArch}_efi.lds)
else()
add_compile_options(-fno-pic)
add_compile_options(-fno-pie)
endif()
# Sort files to make build reproducible
list(SORT files)
set(cpio_files "")
list(APPEND cpio_files "$<TARGET_FILE:kernel.elf>")
if(ElfloaderIncludeDtb)
list(APPEND cpio_files "${KernelDTBPath}")
endif()
list(APPEND cpio_files "$<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE>")
if(NOT ${ElfloaderHashInstructions} STREQUAL "hash_none")
set(hash_command "")
if(ElfloaderHashSHA)
set(hash_command "sha256sum")
else()
set(hash_command "md5sum")
endif()
add_custom_command(
OUTPUT "kernel.bin"
COMMAND
bash -c
"${hash_command} $<TARGET_FILE:kernel.elf> | cut -d ' ' -f 1 | xxd -r -p > ${CMAKE_CURRENT_BINARY_DIR}/kernel.bin"
VERBATIM
DEPENDS "$<TARGET_FILE:kernel.elf>"
)
add_custom_command(
OUTPUT "app.bin"
COMMAND
bash -c
"${hash_command} $<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE> | cut -d ' ' -f 1 | xxd -r -p > ${CMAKE_CURRENT_BINARY_DIR}/app.bin"
VERBATIM
DEPENDS "$<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE>"
)
list(APPEND cpio_files "${CMAKE_CURRENT_BINARY_DIR}/kernel.bin")
list(APPEND cpio_files "${CMAKE_CURRENT_BINARY_DIR}/app.bin")
endif()
# Construct the ELF loader's payload.
MakeCPIO(archive.o "${cpio_files}" CPIO_SYMBOL _archive_start)
set(PLATFORM_HEADER_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen_headers")
set(PLATFORM_INFO_H "${PLATFORM_HEADER_DIR}/platform_info.h")
set(IMAGE_START_ADDR_H "${PLATFORM_HEADER_DIR}/image_start_addr.h")
if(NOT "${IMAGE_START_ADDR}" STREQUAL "")
# Generate static header files. Their timestamps will change only if
# their contents have changed on subsequent CMake reruns.
file(GENERATE OUTPUT ${PLATFORM_INFO_H} CONTENT "
#pragma once
/* no platform YAML file available */
")
file(GENERATE OUTPUT ${IMAGE_START_ADDR_H} CONTENT "
#pragma once
#define IMAGE_START_ADDR ${IMAGE_START_ADDR}
")
elseif(NOT DEFINED platform_yaml)
message(FATAL_ERROR "IMAGE_START_ADDR not set and 'platform_yaml' undefined")
else()
# The following does two things. These commands are put together as for some weird
# reason, CMake does not execute the first command if it's by itself. This could be
# due to the CMakeLists.txt file in the capDL repository also invoking a similar command
# causing CMake to think that the first command with PLATFORM_SIFT is unnecessary.
set(CMAKE_TOOL_HELPERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../cmake-tool/helpers")
set(PLATFORM_SIFT "${CMAKE_TOOL_HELPERS_DIR}/platform_sift.py")
set(ELF_SIFT "${CMAKE_TOOL_HELPERS_DIR}/elf_sift.py")
set(SHOEHORN "${CMAKE_TOOL_HELPERS_DIR}/shoehorn.py")
set(ARCHIVE_O "${CMAKE_CURRENT_BINARY_DIR}/archive.o")
add_custom_command(
OUTPUT "${IMAGE_START_ADDR_H}" "${PLATFORM_INFO_H}"
COMMAND
# Take the platform's YAML description can create a C header file with
# information of interest to the ELF-loader, e.g. a physical memory map. We
# must put the ELF-loader's payload in memory at a place that will be out of
# the way of the kernel and user images that are extracted. In other words,
# we don't want the ELF-loader (with its payload) to clobber itself.
"${PLATFORM_SIFT}"
--emit-c-syntax "${platform_yaml}" > "${PLATFORM_INFO_H}"
COMMAND
# The `shoehorn` tool computes a reasonable image start address. It calls
# `elf_sift` to obtain details about where the extracted payloads will be
# and how big they are.
"${SHOEHORN}" "${platform_yaml}" "${ARCHIVE_O}" > "${IMAGE_START_ADDR_H}"
VERBATIM
DEPENDS
# First command's dependencies
"${platform_yaml}" "${PLATFORM_SIFT}"
# Second command's dependencies
"${ARCHIVE_O}"
"${platform_yaml}"
"${ELF_SIFT}"
"${SHOEHORN}"
)
endif()
if(DEFINED KernelDTBPath)
get_filename_component(KernelTools ${HARDWARE_GEN_PATH} DIRECTORY)
set(config_file "${KernelTools}/hardware.yml")
set(schema_file "${KernelTools}/hardware_schema.yml")
set(DEVICES_GEN_H "${PLATFORM_HEADER_DIR}/devices_gen.h")
add_custom_command(
OUTPUT ${DEVICES_GEN_H}
COMMAND
${PYTHON3} ${HARDWARE_GEN_PATH}
--elfloader
--elfloader-out "${DEVICES_GEN_H}"
--hardware-config "${config_file}"
--hardware-schema "${schema_file}"
--dtb "${KernelDTBPath}"
VERBATIM
DEPENDS ${KernelDTBPath} ${config_file} ${schema_file}
)
set_property(SOURCE src/drivers/driver.c PROPERTY OBJECT_DEPENDS ${DEVICES_GEN_H})
endif()
# Generate linker script
separate_arguments(c_arguments NATIVE_COMMAND "${CMAKE_C_FLAGS}")
# Add extra compilation flags required for clang
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
list(APPEND c_arguments "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}")
endif()
add_custom_command(
OUTPUT "linker.lds_pp"
COMMAND
${CMAKE_C_COMPILER} "${c_arguments}" "-I${PLATFORM_HEADER_DIR}"
"-I$<JOIN:$<TARGET_PROPERTY:sel4_autoconf,INTERFACE_INCLUDE_DIRECTORIES>,;-I>"
"-I$<JOIN:$<TARGET_PROPERTY:elfloader_Config,INTERFACE_INCLUDE_DIRECTORIES>,;-I>" -P -E -o
linker.lds_pp -x c ${linkerScript}
DEPENDS
sel4_autoconf
${linkerScript}
elfloader_Config
${IMAGE_START_ADDR_H}
VERBATIM COMMAND_EXPAND_LISTS
)
add_custom_target(elfloader_linker DEPENDS linker.lds_pp)
add_executable(elfloader EXCLUDE_FROM_ALL ${files} archive.o)
if(ElfloaderImageEFI)
set_property(TARGET elfloader APPEND_STRING PROPERTY LINK_FLAGS " -pie ")
set_target_properties(elfloader PROPERTIES LINK_DEPENDS ${linkerScript})
set_property(
TARGET elfloader
APPEND_STRING
PROPERTY
LINK_FLAGS
# -Bsymbolic forces symbols to bind to their definitions within the elfloader
# EFI_SUBSYSTEM=0xa indicates that we're building an EFI application.
" -Wl,-T ${linkerScript} -nostdlib -shared -Wl,-Bsymbolic,--defsym=EFI_SUBSYSTEM=0xa -Wl,--build-id=none"
)
else()
set_target_properties(
elfloader
PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp
)
add_dependencies(elfloader elfloader_linker)
set_property(
TARGET elfloader
APPEND_STRING
PROPERTY
LINK_FLAGS
" -Wl,-T ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp -nostdlib -static -Wl,--build-id=none"
)
endif()
target_include_directories(
elfloader
PRIVATE
"include"
"include/plat/${KernelPlatform}"
"include/arch-${KernelArch}"
"include/arch-${KernelArch}/${KernelWordSize}"
"${CMAKE_CURRENT_BINARY_DIR}/gen_headers"
"${CMAKE_CURRENT_BINARY_DIR}"
)
if(KernelArchARM)
target_include_directories(
elfloader
PRIVATE
"include/arch-${KernelArch}/armv/${KernelArmArmV}"
"include/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}"
)
endif()
target_link_libraries(
elfloader
PRIVATE
cpio
gcc
elfloader_Config
sel4_autoconf
)