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