cmake_minimum_required(VERSION 3.22)

set(SRC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/)
set(COMMON ${SRC_ROOT}/common)
set(CRYPTO ${SRC_ROOT}/cyclone_crypto)
set(BOOT ${SRC_ROOT}/cyclone_boot)
set(THIRD_PARTY ${CMAKE_SOURCE_DIR}/../../../../../third_party)
set(SCRIPTS ${CMAKE_SOURCE_DIR}/../../../../../scripts)

# 2nd-stage image identity: the 1st stage installs a candidate only if its index is higher
set(BOOT_VERSION "1.0.0" CACHE STRING "2nd-stage bootloader firmware version")
string(REPLACE "." ";" BOOT_VERSION_PARTS ${BOOT_VERSION})
list(GET BOOT_VERSION_PARTS 0 BOOT_MAJOR_VERSION)
list(GET BOOT_VERSION_PARTS 1 BOOT_MINOR_VERSION)
list(GET BOOT_VERSION_PARTS 2 BOOT_REV_NUMBER)
set(BOOT_IMAGE_INDEX "0" CACHE STRING "2nd-stage bootloader image index of the flashed (bootable) image")
set(BOOT_RUNTIME_SIGN_KEY ${SCRIPTS}/keys/my_rsa_private_key.pem CACHE FILEPATH "Private key signing the 2nd-stage image")
string(REPLACE "." "_" BOOT_VERSION_TAG ${BOOT_VERSION})
set(TOOLS ${CMAKE_SOURCE_DIR}/../../../../../tools)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()

# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})

# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})

target_sources(${CMAKE_PROJECT_NAME} PRIVATE
        ${COMMON}/cpu_endian.c
        ${COMMON}/os_port_none.c
        ${COMMON}/date_time.c
        ${COMMON}/str.c
        ${COMMON}/path.c
        ${COMMON}/resource_manager.c
        ${BOOT}/core/crc32.c
        ${BOOT}/core/mailbox.c
        ${BOOT}/drivers/mcu_core/stm32l4xx_mcu_driver.c
        ${BOOT}/drivers/flash/internal/stm32l4xx_flash_driver.c
        ${BOOT}/modules/image/image.c
        ${BOOT}/modules/image/image_process.c
        ${BOOT}/modules/image/image_utils.c
        ${BOOT}/modules/memory/memory.c
        ${BOOT}/modules/memory/memory_ex.c
        ${BOOT}/core/verify.c
        ${BOOT}/modules/security/verify_auth.c
        ${BOOT}/modules/security/verify_sign.c
        ${BOOT}/modules/security/cipher.c
        ${BOOT}/bootloader/second_stage/boot.c
        ${BOOT}/bootloader/second_stage/boot_common.c
        ${BOOT}/bootloader/second_stage/boot_fallback.c
        ${BOOT}/bootloader/second_stage/boot_verify.c
        ${BOOT}/modules/security/verify_runtime.c
        ${BOOT}/modules/security/verify_integrity.c

        ${CRYPTO}/hardware/stm32l4xx/stm32l4xx_crypto.c
        ${CRYPTO}/hardware/stm32l4xx/stm32l4xx_crypto_trng.c
        ${CRYPTO}/hash/md5.c
        ${CRYPTO}/hash/sha1.c
        ${CRYPTO}/hash/sha224.c
        ${CRYPTO}/hash/sha256.c
        ${CRYPTO}/hash/sha384.c
        ${CRYPTO}/hash/sha512.c
        ${CRYPTO}/mac/hmac.c
        ${CRYPTO}/cipher/rc4.c
        ${CRYPTO}/cipher/idea.c
        ${CRYPTO}/cipher/des.c
        ${CRYPTO}/cipher/des3.c
        ${CRYPTO}/cipher/aes.c
        ${CRYPTO}/cipher/camellia.c
        ${CRYPTO}/cipher/seed.c
        ${CRYPTO}/cipher/aria.c
        ${CRYPTO}/cipher_modes/cbc.c
        ${CRYPTO}/aead/ccm.c
        ${CRYPTO}/aead/gcm.c
        ${CRYPTO}/cipher/chacha.c
        ${CRYPTO}/mac/poly1305.c
        ${CRYPTO}/aead/chacha20_poly1305.c
        ${CRYPTO}/pkc/dh.c
        ${CRYPTO}/pkc/rsa.c
        ${CRYPTO}/pkc/dsa.c
        ${CRYPTO}/ecc/ec.c
        ${CRYPTO}/ecc/ec_curves.c
        ${CRYPTO}/ecc/ecdh.c
        ${CRYPTO}/ecc/ecdsa.c
        ${CRYPTO}/ecc/eddsa.c
        ${CRYPTO}/ecc/curve25519.c
        ${CRYPTO}/ecc/curve448.c
        ${CRYPTO}/ecc/x25519.c
        ${CRYPTO}/ecc/x448.c
        ${CRYPTO}/ecc/ed25519.c
        ${CRYPTO}/ecc/ed448.c
        ${CRYPTO}/mpi/mpi.c
        ${CRYPTO}/mpi/mpi_misc.c
        ${CRYPTO}/encoding/base64.c
        ${CRYPTO}/encoding/asn1.c
        ${CRYPTO}/encoding/oid.c
        ${CRYPTO}/pkix/pem_import.c
        ${CRYPTO}/pkix/pem_key_import.c
        ${CRYPTO}/pkix/pem_decrypt.c
        ${CRYPTO}/pkix/pem_common.c
        ${CRYPTO}/pkix/pkcs5_decrypt.c
        ${CRYPTO}/pkix/pkcs5_common.c
        ${CRYPTO}/pkix/pkcs8_key_parse.c
        ${CRYPTO}/pkix/x509_key_parse.c
        ${CRYPTO}/pkix/x509_cert_parse.c
        ${CRYPTO}/pkix/x509_cert_validate.c
        ${CRYPTO}/pkix/x509_cert_ext_parse.c
        ${CRYPTO}/pkix/x509_common.c
        ${CRYPTO}/pkix/x509_sign_parse.c
        ${CRYPTO}/pkix/x509_sign_verify.c
        ${CRYPTO}/kdf/hkdf.c
        
        ${CRYPTO}/ecc/ec_misc.c
        ${CRYPTO}/pkc/rsa_misc.c

        ${THIRD_PARTY}/st/boards/stm32l4xx_nucleo/stm32l4xx_nucleo.c

        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_rcc.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_rcc_ex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_flash.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_flash_ex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_flash_ramfunc.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_gpio.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_i2c.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_i2c_ex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_dma.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_dma_ex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_pwr.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_pwr_ex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_cortex.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_exti.c
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/src/stm32l4xx_hal_uart.c

        ${CMAKE_SOURCE_DIR}/cmake/startup_stm32l476xx.s
        ${CMAKE_SOURCE_DIR}/cmake/syscalls.c
        ${CMAKE_SOURCE_DIR}/src/system_stm32l4xx.c
        ${CMAKE_SOURCE_DIR}/src/stm32l4xx_it.c
        ${CMAKE_SOURCE_DIR}/src/main.c
        ${CMAKE_SOURCE_DIR}/src/board.c
        ${CMAKE_SOURCE_DIR}/src/debug.c
)

target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
        ${CMAKE_SOURCE_DIR}/src
        ${THIRD_PARTY}/cmsis/include
        ${THIRD_PARTY}/st/devices/stm32l4xx
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/inc
        ${THIRD_PARTY}/st/drivers/stm32l4xx_hal_driver/inc/legacy
        ${THIRD_PARTY}/st/boards/stm32l4xx_nucleo
        ${COMMON}
        ${CRYPTO}
        ${BOOT}
        ${BOOT}/bootloader/
        ${BOOT}/modules
)

target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    STM32L476xx
    STM32L47xG
    USE_HAL_DRIVER
    USE_STM32L4XX_NUCLEO
    __USE_C99_MATH

    BOOT_VERSION_STRING="${BOOT_VERSION}"
    BOOT_MAJOR_VERSION=${BOOT_MAJOR_VERSION}
    BOOT_MINOR_VERSION=${BOOT_MINOR_VERSION}
    BOOT_REV_NUMBER=${BOOT_REV_NUMBER}
    $<$<CONFIG:Debug>:DEBUG>
)

# Convert .elf to .bin
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -O binary
        ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf
        ${CMAKE_CURRENT_BINARY_DIR}/2nd_stage_${CMAKE_PROJECT_NAME}.bin
        COMMENT "Generating binary file from ELF"
)


# Copy the generated .bin file to ../
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy
            ${CMAKE_CURRENT_BINARY_DIR}/2nd_stage_${CMAKE_PROJECT_NAME}.bin
            ${CMAKE_CURRENT_SOURCE_DIR}/../
    COMMENT "Copying ${CMAKE_PROJECT_NAME}.bin to parent directory"
)

# Package the 2nd stage as a BOOT-type image (CRC32 + RSA runtime signature) for the 1st stage, and
# as the encrypted + signed update image delivered through the application. A custom target (not a
# POST_BUILD step) so that a rebuild with new cache values, e.g. -DBOOT_VERSION=2.0.0
# -DBOOT_IMAGE_INDEX=1, regenerates the images even when nothing needs relinking.
if(WIN32)
    set(IMAGE_BUILDER ${TOOLS}/ImageBuilder/bin/image_builder_windows.exe)
else()
    set(IMAGE_BUILDER ${TOOLS}/ImageBuilder/bin/image_builder_linux)
endif()

add_custom_target(bootloader_images ALL
        COMMAND ${IMAGE_BUILDER}
        --input ${CMAKE_CURRENT_BINARY_DIR}/2nd_stage_${CMAKE_PROJECT_NAME}.bin
        --output ${CMAKE_CURRENT_SOURCE_DIR}/../bootloader_${BOOT_VERSION_TAG}_bootable.img.bin
        --update-type boot
        --image-index ${BOOT_IMAGE_INDEX}
        --firmware-version ${BOOT_VERSION}
        --integrity-algo crc32
        --runtime-sign-algo rsa-sha256
        --runtime-sign-key ${BOOT_RUNTIME_SIGN_KEY}
        --vtor-align
        COMMAND ${IMAGE_BUILDER}
        --input ${CMAKE_CURRENT_BINARY_DIR}/2nd_stage_${CMAKE_PROJECT_NAME}.bin
        --output ${CMAKE_CURRENT_SOURCE_DIR}/../bootloader_${BOOT_VERSION_TAG}_update.img
        --update-type boot
        --firmware-version ${BOOT_VERSION}
        --runtime-sign-algo rsa-sha256
        --runtime-sign-key ${BOOT_RUNTIME_SIGN_KEY}
        --enc-algo aes-cbc
        --enc-key aa3ff7d43cc015682c7dfd00de9379e7
        --sign-algo rsa-sha256
        --sign-key ${SCRIPTS}/keys/rsa_private_key.pem
        --vtor-align
        DEPENDS ${CMAKE_PROJECT_NAME}
        COMMENT "Generating bootloader_${BOOT_VERSION_TAG}_bootable.img.bin and bootloader_${BOOT_VERSION_TAG}_update.img using ImageBuilder"
)

# Refresh the footprint report on a Release build. A Debug build leaves the committed
# numbers alone, so a report always describes the configuration customers ship.
if (CMAKE_BUILD_TYPE STREQUAL "Release")
	add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
		COMMAND python3 ${CMAKE_SOURCE_DIR}/footprint.py
		--map "${CMAKE_CURRENT_BINARY_DIR}/bootloader.map"
		--ld "${CMAKE_SOURCE_DIR}/cmake/STM32L476XX_FLASH.ld"
		--elf "${CMAKE_CURRENT_BINARY_DIR}/bootloader.elf"
		--output-summary "${CMAKE_SOURCE_DIR}/reports/footprint_summary.json"
		--build-type Release
		--folders ${SRC_ROOT}/cyclone_boot ${SRC_ROOT}/cyclone_crypto ${SRC_ROOT}/cyclone_tcp ${SRC_ROOT}/common
	)
endif ()
