1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6 7cmake_minimum_required(VERSION 3.7.2) 8 9project(elfloader C ASM) 10 11if(KernelArchX86) 12 # This project is only used on Arm or RISC-V 13 return() 14endif() 15 16include(${KERNEL_FLAGS_PATH}) 17include(cpio) 18 19set(configure_string "") 20 21config_choice( 22 ElfloaderImage 23 ELFLOADER_IMAGE 24 "Boot image type" 25 "elf;ElfloaderImageELF;IMAGE_ELF;KernelArchARM OR KernelArchRiscV" 26 "binary;ElfloaderImageBinary;IMAGE_BINARY;KernelArchARM OR KernelArchRiscV" 27 "efi;ElfloaderImageEFI;IMAGE_EFI;KernelArchARM" 28 "uimage;ElfloaderImageUimage;IMAGE_UIMAGE;KernelArchARM" 29) 30 31config_choice( 32 ElfloaderMode 33 ELFLOADER_MODE 34 "seL4 mode" 35 "secure supervisor;ElfloaderModeSSup;ARM_S_SUPERVISOR_MODE;KernelPlatImx6" 36 "monitor;ElfloaderModeMonitor;ARM_MONITOR_MODE;KernelPlatformTK1 OR KernelPlatImx6" 37 "hypervisor;ElfloaderModeHyp;ARM_HYPERVISOR_MODE;KernelPlatformTK1" 38 "non-secure supervisor;ElfloaderModeNSSup;ARM_NS_SUPERVISOR_MODE;KernelPlatformTK1 OR KernelPlatImx6" 39) 40 41config_option( 42 ElfloaderMonitorHook ARM_MONITOR_HOOK "Install SMC call handlers in monitor mode." 43 DEFAULT OFF 44 DEPENDS "KernelPlatformTK1 OR KernelPlatImx6" 45) 46 47config_option( 48 ElfloaderGPTPtimersNSPL1Access GPT_PTIMER_NS_PL1_ACCESS 49 "Enable the GPT physical timer access for NS PL1" 50 DEFAULT ON 51 DEPENDS "ElfloaderModeNSSup" 52 DEFAULT_DISABLED OFF 53) 54 55config_option( 56 ElfloaderErrata764369 ARM_ERRATA_764369 57 "Work around for a Cortex-A9 errata. Derived from Linux kernel." 58 DEFAULT ON 59 DEPENDS "KernelArmCortexA9" 60 DEFAULT_DISABLED OFF 61) 62 63config_choice( 64 ElfloaderHashInstructions 65 HASH_INSTRUCTIONS 66 "Perform a SHA256/MD5 Hash of the of each elf file that the elfloader checks on load" 67 "hash_none;ElfloaderHashNone;HASH_NONE" 68 "hash_sha;ElfloaderHashSHA;HASH_SHA" 69 "hash_md5;ElfloaderHashMD5;HASH_MD5" 70) 71 72config_option( 73 ElfloaderIncludeDtb ELFLOADER_INCLUDE_DTB 74 "Include DTB in the CPIO in case bootloader doesn't provide one" 75 DEFAULT ON 76 DEPENDS "KernelArchARM OR KernelArchRiscV" 77 DEFAULT_DISABLED OFF 78) 79 80config_option( 81 ElfloaderRootserversLast ELFLOADER_ROOTSERVERS_LAST 82 "Place the rootserver images at the end of memory" 83 DEFAULT OFF 84 DEFAULT_DISABLED OFF 85) 86 87config_option( 88 ElfloaderArmV8LeaveAarch64 ELFLOADER_ARMV8_LEAVE_AARCH64 89 "Insert aarch64 code to switch to aarch32. Requires the elfloader to be in EL2" 90 DEFAULT OFF 91 DEPENDS KernelArchArmV8a 92) 93 94add_config_library(elfloader "${configure_string}") 95 96add_compile_options(-D_XOPEN_SOURCE=700 -ffreestanding -Wall -Werror -W -Wextra) 97set(linkerScript "${CMAKE_CURRENT_LIST_DIR}/src/arch-${KernelArch}/linker.lds") 98if(KernelArchRiscV) 99 add_compile_options(-mcmodel=medany) 100endif() 101 102if(ElfloaderArmV8LeaveAarch64) 103 # We need to build a aarch64 assembly file during an aarch32 build. We have 104 # to write custom rules to do this as CMake doesn't support multiple compilers 105 # within a single build config. 106 find_program(AARCH64_COMPILER aarch64-linux-gnu-gcc) 107 if("${AARCH64_COMPILER}" STREQUAL "AARCH64_COMPILER-NOTFOUND") 108 message( 109 FATAL_ERROR 110 "Cannot find 'aarch64-linux-gnu-gcc' program. Use -DAARCH64_COMPILER=compiler" 111 ) 112 endif() 113 find_program(AARCH64_OBJCOPY aarch64-linux-gnu-objcopy) 114 if("${AARCH64_OBJCOPY}" STREQUAL "AARCH64_OBJCOPY-NOTFOUND") 115 message( 116 FATAL_ERROR 117 "Cannot find 'aarch64-linux-gnu-objcopy' program. Use -DAARCH64_OBJCOPY=objcopy" 118 ) 119 endif() 120 # Compile crt0_64.S and convert to a binary. This way the actual crt0.S can use 121 # the .incbin directive and insert the aarch64 instructions before its own. 122 add_custom_command( 123 OUTPUT crt0_64.bin crt0_64.o 124 COMMAND 125 ${AARCH64_COMPILER} -I${CMAKE_CURRENT_SOURCE_DIR}/include/ 126 -I${CMAKE_CURRENT_SOURCE_DIR}/include/arch-arm/64/ 127 -I${CMAKE_CURRENT_SOURCE_DIR}/include/arch-arm/armv/armv8-a/64/ -c 128 ${CMAKE_CURRENT_SOURCE_DIR}/src/arch-arm/32/crt0_64.S -o 129 ${CMAKE_CURRENT_BINARY_DIR}/crt0_64.o 130 COMMAND 131 ${AARCH64_OBJCOPY} -O binary ${CMAKE_CURRENT_BINARY_DIR}/crt0_64.o crt0_64.bin 132 DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/arch-arm/32/crt0_64.S 133 ) 134 135 # We set the OBJECT_DEPENDS property on the crt0.S source file that tells CMake 136 # any object files created from crt0.S also depend on crt0_64.bin. This causes 137 # our builds to be rerun correctly. 138 set(armv8_leave_arch_bin ${CMAKE_CURRENT_BINARY_DIR}/crt0_64.bin) 139 set_property(SOURCE src/arch-arm/32/crt0.S PROPERTY OBJECT_DEPENDS ${armv8_leave_arch_bin}) 140endif() 141 142if(KernelSel4ArchAarch64) 143 # NEON registers aren't necessarily initialized for use before elfloader starts 144 add_compile_options(-mgeneral-regs-only) 145endif() 146 147# Don't allow unaligned data store/load instructions as this will cause an alignment 148# fault before the MMU is turned on. 149if(KernelSel4ArchAarch64) 150 add_compile_options(-mstrict-align) 151elseif(KernelSel4ArchAarch32) 152 add_compile_options(-mno-unaligned-access) 153endif() 154 155file( 156 GLOB 157 files 158 src/*.c 159 src/drivers/*.c 160 src/drivers/smp/*.c 161 src/drivers/uart/*.c 162 src/utils/*.c 163 src/arch-${KernelArch}/*.c 164 src/arch-${KernelArch}/*.S 165 src/arch-${KernelArch}/drivers/*.c 166 src/plat/${KernelPlatform}/*.c 167 src/binaries/elf/*.c 168 src/arch-${KernelArch}/${KernelWordSize}/*.c 169 src/plat/${KernelPlatform}/*.S 170 src/arch-${KernelArch}/${KernelWordSize}/*.S 171) 172 173# We never want to give crt0_64.S to add_executable 174list(FILTER files EXCLUDE REGEX src/arch-arm/32/crt0_64.S) 175 176if(KernelArchARM) 177 file( 178 GLOB 179 arm_files src/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}/*.c 180 src/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}/*.S 181 ) 182 list(APPEND files ${arm_files}) 183endif() 184 185if(ElfloaderImageEFI) 186 # We cannot control where EFI loads the image and so we must make it relocatable 187 add_compile_options(-fpic) 188 if(KernelSel4ArchAarch32) 189 set(gnuefiArch "arm") 190 # on aarch32 building with -fno-pie results in the compiler generating 191 # movt/movw pairs that we can't easily relocate. 192 add_compile_options(-fpie) 193 # This flag is not supported by clang but add it in for gcc 194 if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") 195 add_compile_options(-mno-single-pic-base) 196 endif() 197 else() 198 set(gnuefiArch "aarch64") 199 # on aarch64 building with -fno-pie will just use pc-relative addressing. 200 add_compile_options(-fno-pie -fPIC) 201 endif() 202 203 file(GLOB efi_files src/binaries/efi/*.c) 204 list( 205 APPEND 206 files 207 ${efi_files} 208 src/binaries/efi/gnuefi/crt0-efi-${gnuefiArch}.S 209 src/binaries/efi/gnuefi/reloc_${gnuefiArch}.c 210 ) 211 # We use gnu-efi's linker script on EFI. 212 set(linkerScript ${CMAKE_CURRENT_LIST_DIR}/src/binaries/efi/gnuefi/elf_${gnuefiArch}_efi.lds) 213else() 214 add_compile_options(-fno-pic) 215 add_compile_options(-fno-pie) 216endif() 217 218# Sort files to make build reproducible 219list(SORT files) 220 221set(cpio_files "") 222list(APPEND cpio_files "$<TARGET_FILE:kernel.elf>") 223if(ElfloaderIncludeDtb) 224 list(APPEND cpio_files "${KernelDTBPath}") 225endif() 226list(APPEND cpio_files "$<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE>") 227if(NOT ${ElfloaderHashInstructions} STREQUAL "hash_none") 228 set(hash_command "") 229 if(ElfloaderHashSHA) 230 set(hash_command "sha256sum") 231 else() 232 set(hash_command "md5sum") 233 endif() 234 add_custom_command( 235 OUTPUT "kernel.bin" 236 COMMAND 237 bash -c 238 "${hash_command} $<TARGET_FILE:kernel.elf> | cut -d ' ' -f 1 | xxd -r -p > ${CMAKE_CURRENT_BINARY_DIR}/kernel.bin" 239 VERBATIM 240 DEPENDS "$<TARGET_FILE:kernel.elf>" 241 ) 242 add_custom_command( 243 OUTPUT "app.bin" 244 COMMAND 245 bash -c 246 "${hash_command} $<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE> | cut -d ' ' -f 1 | xxd -r -p > ${CMAKE_CURRENT_BINARY_DIR}/app.bin" 247 VERBATIM 248 DEPENDS "$<TARGET_PROPERTY:rootserver_image,ROOTSERVER_IMAGE>" 249 ) 250 list(APPEND cpio_files "${CMAKE_CURRENT_BINARY_DIR}/kernel.bin") 251 list(APPEND cpio_files "${CMAKE_CURRENT_BINARY_DIR}/app.bin") 252endif() 253 254# Construct the ELF loader's payload. 255MakeCPIO(archive.o "${cpio_files}" CPIO_SYMBOL _archive_start) 256 257# If our platform has a YAML description, create a C header file to include 258# information about the platform that is of interest to the ELF-loader, such as 259# a physical memory map. 260# 261# We also need to put the ELF-loader's payload in memory at a place that will be 262# out of the way of the kernel and user images that the elfloader extracts. In 263# other words, we don't want the ELF-loader (with its payload) to clobber 264# itself. 265# 266# Formerly there was a complex set of conditionals driving a table of 267# hard-coded addresses. 268# 269# Now, instead, we compute a reasonable image start address, using a tool called 270# `shoehorn`, based on knowledge of how where and how big the extracted payloads 271# will be, obtained by a tool called `elf_sift`. 272set(PLATFORM_HEADER_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen_headers/") 273if(DEFINED platform_yaml) 274 set(PLATFORM_SIFT "${CMAKE_CURRENT_SOURCE_DIR}/../cmake-tool/helpers/platform_sift.py") 275 set(PLATFORM_INFO_H "${PLATFORM_HEADER_DIR}/platform_info.h") 276 add_custom_command( 277 OUTPUT ${PLATFORM_INFO_H} 278 COMMAND ${PLATFORM_SIFT} --emit-c-syntax ${platform_yaml} > ${PLATFORM_INFO_H} 279 VERBATIM 280 DEPENDS ${platform_yaml} ${PLATFORM_SIFT} 281 ) 282 set_property(SOURCE src/common.c PROPERTY OBJECT_DEPENDS ${PLATFORM_INFO_H}) 283 284 # Construct the `shoehorn` command line. 285 set(ARCHIVE_O "${CMAKE_CURRENT_BINARY_DIR}/archive.o") 286 # `shoehorn` calls `elf_sift`, so we'll need to depend on it. 287 set(ELF_SIFT "${CMAKE_CURRENT_SOURCE_DIR}/../cmake-tool/helpers/elf_sift.py") 288 set(SHOEHORN "${CMAKE_CURRENT_SOURCE_DIR}/../cmake-tool/helpers/shoehorn.py") 289 290 set(IMAGE_START_ADDR_H "${PLATFORM_HEADER_DIR}/image_start_addr.h") 291 set(SHOEHORN_COMMAND "${SHOEHORN} ${platform_yaml} ${ARCHIVE_O} > ${IMAGE_START_ADDR_H}") 292 if(NOT "${IMAGE_START_ADDR}" STREQUAL "") 293 file(WRITE ${IMAGE_START_ADDR_H} "#define IMAGE_START_ADDR ${IMAGE_START_ADDR}") 294 else() 295 add_custom_command( 296 OUTPUT ${IMAGE_START_ADDR_H} 297 COMMAND sh -c "${SHOEHORN_COMMAND}" 298 VERBATIM 299 DEPENDS archive.o ${ELF_SIFT} ${SHOEHORN} 300 ) 301 endif() 302else() 303 message( 304 FATAL_ERROR 305 "no image start address computed for platform; use platform YAML file ${platform_yaml} or replace this diagnostic with a CMake custom command to generate a file \"${IMAGE_START_ADDR_H}\" that hard-codes it; the same goes for \"${PLATFORM_INFO_H}\"" 306 ) 307endif() 308 309if(DEFINED KernelDTBPath) 310 get_filename_component(KernelTools ${HARDWARE_GEN_PATH} DIRECTORY) 311 set(config_file "${KernelTools}/hardware.yml") 312 set(schema_file "${KernelTools}/hardware_schema.yml") 313 set(DEVICES_GEN_H "${PLATFORM_HEADER_DIR}/devices_gen.h") 314 add_custom_command( 315 OUTPUT ${DEVICES_GEN_H} 316 COMMAND 317 ${PYTHON3} ${HARDWARE_GEN_PATH} 318 --elfloader 319 --elfloader-out "${DEVICES_GEN_H}" 320 --hardware-config "${config_file}" 321 --hardware-schema "${schema_file}" 322 --dtb "${KernelDTBPath}" 323 VERBATIM 324 DEPENDS ${KernelDTBPath} ${config_file} ${schema_file} 325 ) 326 set_property(SOURCE src/drivers/driver.c PROPERTY OBJECT_DEPENDS ${DEVICES_GEN_H}) 327endif() 328 329# Generate linker script 330separate_arguments(c_arguments NATIVE_COMMAND "${CMAKE_C_FLAGS}") 331# Add extra compilation flags required for clang 332if(CMAKE_C_COMPILER_ID STREQUAL "Clang") 333 list(APPEND c_arguments "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") 334endif() 335add_custom_command( 336 OUTPUT "linker.lds_pp" 337 COMMAND 338 ${CMAKE_C_COMPILER} "${c_arguments}" "-I${PLATFORM_HEADER_DIR}" 339 "-I$<JOIN:$<TARGET_PROPERTY:sel4_autoconf,INTERFACE_INCLUDE_DIRECTORIES>,;-I>" 340 "-I$<JOIN:$<TARGET_PROPERTY:elfloader_Config,INTERFACE_INCLUDE_DIRECTORIES>,;-I>" -P -E -o 341 linker.lds_pp -x c ${linkerScript} 342 DEPENDS 343 sel4_autoconf 344 ${linkerScript} 345 elfloader_Config 346 ${IMAGE_START_ADDR_H} 347 VERBATIM COMMAND_EXPAND_LISTS 348) 349add_custom_target(elfloader_linker DEPENDS linker.lds_pp) 350 351add_executable(elfloader EXCLUDE_FROM_ALL ${files} archive.o) 352if(ElfloaderImageEFI) 353 set_property(TARGET elfloader APPEND_STRING PROPERTY LINK_FLAGS " -pie ") 354 set_target_properties(elfloader PROPERTIES LINK_DEPENDS ${linkerScript}) 355 set_property( 356 TARGET elfloader 357 APPEND_STRING 358 PROPERTY 359 LINK_FLAGS 360 # -Bsymbolic forces symbols to bind to their definitions within the elfloader 361 # EFI_SUBSYSTEM=0xa indicates that we're building an EFI application. 362 " -Wl,-T ${linkerScript} -nostdlib -shared -Wl,-Bsymbolic,--defsym=EFI_SUBSYSTEM=0xa -Wl,--build-id=none" 363 ) 364else() 365 set_target_properties( 366 elfloader 367 PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp 368 ) 369 add_dependencies(elfloader elfloader_linker) 370 set_property( 371 TARGET elfloader 372 APPEND_STRING 373 PROPERTY 374 LINK_FLAGS 375 " -Wl,-T ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp -nostdlib -static -Wl,--build-id=none" 376 ) 377endif() 378 379target_include_directories( 380 elfloader 381 PRIVATE 382 "include" 383 "include/plat/${KernelPlatform}" 384 "include/arch-${KernelArch}" 385 "include/arch-${KernelArch}/${KernelWordSize}" 386 "${CMAKE_CURRENT_BINARY_DIR}/gen_headers" 387 "${CMAKE_CURRENT_BINARY_DIR}" 388) 389if(KernelArchARM) 390 target_include_directories( 391 elfloader 392 PRIVATE 393 "include/arch-${KernelArch}/armv/${KernelArmArmV}" 394 "include/arch-${KernelArch}/armv/${KernelArmArmV}/${KernelWordSize}" 395 ) 396endif() 397 398target_link_libraries( 399 elfloader 400 PRIVATE 401 cpio 402 gcc 403 elfloader_Config 404 sel4_autoconf 405) 406