From e8860fe8be940ee43f8b89f827e2d0a5e467509a Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Thu, 1 Feb 2018 01:12:32 -0600 Subject: [PATCH] arm: Add app data section alignment constraints This patch adds application data section alignment constraints to match the region definition requirements for ARM MPUs. Most MPUs require a minimum of 32 bytes of alignment for any regions, but some require power of two alignment to the size of a region. This requires that the linker align the application data section to the size of the section. This requires a linker pass to determine the size. Once this is accomplished the correct value is added to a linker include file that is utilized in subsequent linker operations. Signed-off-by: Andy Gross --- CMakeLists.txt | 65 ++++++++++++++- arch/Kconfig | 9 +++ .../cortex_m/scripts/app_data_alignment.ld | 9 +++ include/arch/arm/cortex_m/scripts/linker.ld | 11 +++ scripts/gen_alignment_script.py | 79 +++++++++++++++++++ 5 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 include/arch/arm/cortex_m/scripts/app_data_alignment.ld create mode 100755 scripts/gen_alignment_script.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 85b0cc3f4b1..cf7bc6f1196 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -514,6 +514,14 @@ if(CONFIG_APPLICATION_MEMORY) endforeach() endif() # CONFIG_APPLICATION_MEMORY +# Declare MPU userspace dependencies before the linker scripts to make +# sure the order of dependencies are met +if(CONFIG_CPU_HAS_MPU AND CONFIG_USERSPACE) + if(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) + set(ALIGN_SIZING_DEP app_sizing_prebuilt linker_app_sizing_script) + endif() +endif() + function(construct_add_custom_command_for_linker_pass linker_output_name output_variable) set(linker_cmd_file_name ${linker_output_name}.cmd) @@ -566,9 +574,11 @@ construct_add_custom_command_for_linker_pass(linker custom_command) add_custom_command( ${custom_command} ) + add_custom_target( linker_script DEPENDS + ${ALIGN_SIZING_DEP} linker.cmd offsets_h ) @@ -748,11 +758,59 @@ get_property(GKSF GLOBAL PROPERTY GENERATED_KERNEL_SOURCE_FILES) get_property(TOPT GLOBAL PROPERTY TOPT) set_ifndef( TOPT -T) +configure_file( + $ENV{ZEPHYR_BASE}/include/arch/arm/cortex_m/scripts/app_data_alignment.ld + ${PROJECT_BINARY_DIR}/include/generated/app_data_alignment.ld) + +if(CONFIG_CPU_HAS_MPU AND CONFIG_USERSPACE) + + if(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) + + construct_add_custom_command_for_linker_pass(linker_app_sizing custom_command) + add_custom_command( + ${custom_command} + ) + + add_custom_target( + linker_app_sizing_script + DEPENDS + linker_app_sizing.cmd + offsets_h + ) + + set_property(TARGET + linker_app_sizing_script + PROPERTY INCLUDE_DIRECTORIES + ${ZEPHYR_INCLUDE_DIRS} + ) + + # For systems with MPUs, the size of the application data section must + # be determined so that MPU alignment requirements can be met. + # Create a app_sizing_prebuilt target so we can do this before the + # other ELF files are built + set(GEN_APP_ALIGN $ENV{ZEPHYR_BASE}/scripts/gen_alignment_script.py) + add_executable( app_sizing_prebuilt misc/empty_file.c) + target_link_libraries(app_sizing_prebuilt ${TOPT} ${PROJECT_BINARY_DIR}/linker_app_sizing.cmd ${zephyr_lnk}) + set_property(TARGET app_sizing_prebuilt PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker_app_sizing.cmd) + add_dependencies( app_sizing_prebuilt linker_app_sizing_script offsets) + + add_custom_command( + TARGET app_sizing_prebuilt + POST_BUILD + COMMAND ${PYTHON_EXECUTABLE} ${GEN_APP_ALIGN} + --output ./include/generated/app_data_alignment.ld + --kernel $ + VERBATIM + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/ + ) + endif() +endif() + # FIXME: Is there any way to get rid of empty_file.c? add_executable( zephyr_prebuilt misc/empty_file.c) target_link_libraries(zephyr_prebuilt ${TOPT} ${PROJECT_BINARY_DIR}/linker.cmd ${zephyr_lnk}) set_property(TARGET zephyr_prebuilt PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker.cmd) -add_dependencies( zephyr_prebuilt linker_script offsets) +add_dependencies( zephyr_prebuilt ${ALIGN_SIZING_DEP} linker_script offsets) if(NOT CONFIG_NATIVE_APPLICATION) @@ -769,9 +827,12 @@ if(GKOF OR GKSF) add_custom_command( ${custom_command} ) + add_custom_target( linker_pass_final_script DEPENDS + ${ALIGN_SIZING_DEP} + zephyr_prebuilt linker_pass_final.cmd offsets_h ) @@ -784,7 +845,7 @@ if(GKOF OR GKSF) add_executable( kernel_elf misc/empty_file.c ${GKSF}) target_link_libraries(kernel_elf ${GKOF} ${TOPT} ${PROJECT_BINARY_DIR}/linker_pass_final.cmd ${zephyr_lnk}) set_property(TARGET kernel_elf PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker_pass_final.cmd) - add_dependencies( kernel_elf linker_pass_final_script) + add_dependencies( kernel_elf ${ALIGN_SIZING_DEP} linker_pass_final_script) else() set(logical_target_for_zephyr_elf zephyr_prebuilt) # Use the prebuilt elf as the final elf since we don't have a diff --git a/arch/Kconfig b/arch/Kconfig index 4c49d8431fe..03e6993a28c 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -225,6 +225,15 @@ config CPU_HAS_MPU help This option is enabled when the CPU has a Memory Protection Unit (MPU). +config MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT + bool + # Omit prompt to signify "hidden" option + default n + help + This option is enabled when the MPU requires a power of two alignment + and size for MPU regions. + + menu "Floating Point Options" depends on CPU_HAS_FPU diff --git a/include/arch/arm/cortex_m/scripts/app_data_alignment.ld b/include/arch/arm/cortex_m/scripts/app_data_alignment.ld new file mode 100644 index 00000000000..a9257f14ee8 --- /dev/null +++ b/include/arch/arm/cortex_m/scripts/app_data_alignment.ld @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2017 Linaro Limited. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Set initial alignment to the 32 byte minimum for all MPUs */ +_app_data_align = 32; +. = ALIGN(32); diff --git a/include/arch/arm/cortex_m/scripts/linker.ld b/include/arch/arm/cortex_m/scripts/linker.ld index 87041e8451c..d676f210741 100644 --- a/include/arch/arm/cortex_m/scripts/linker.ld +++ b/include/arch/arm/cortex_m/scripts/linker.ld @@ -215,6 +215,9 @@ SECTIONS #ifdef CONFIG_APPLICATION_MEMORY SECTION_DATA_PROLOGUE(_APP_DATA_SECTION_NAME, (OPTIONAL),) { + +#include + __app_ram_start = .; __app_data_ram_start = .; _image_ram_start = .; @@ -242,7 +245,15 @@ SECTIONS APP_INPUT_SECTION(".noinit.*") } GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION) + __app_last_address_used = .; + + /* Pad out application ram area to make MPU friendly */ + SECTION_PROLOGUE(app_pad, (NOLOAD OPTIONAL),) + { + . = ALIGN(_app_data_align); + } GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION) __app_ram_end = .; + __app_ram_size = __app_ram_end - __app_ram_start; #endif /* CONFIG_APPLICATION_MEMORY */ SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),) diff --git a/scripts/gen_alignment_script.py b/scripts/gen_alignment_script.py new file mode 100755 index 00000000000..aaaa1f4f591 --- /dev/null +++ b/scripts/gen_alignment_script.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 + +import sys +import argparse +import pprint +import os +import struct +from distutils.version import LooseVersion + +import elftools +from elftools.elf.elffile import ELFFile +from elftools.dwarf import descriptions +from elftools.elf.sections import SymbolTableSection + +if LooseVersion(elftools.__version__) < LooseVersion('0.24'): + sys.stderr.write("pyelftools is out of date, need version 0.24 or later\n") + sys.exit(1) + + +def get_symbols(obj): + for section in obj.iter_sections(): + if isinstance(section, SymbolTableSection): + return {sym.name: sym.entry.st_value + for sym in section.iter_symbols()} + + raise LookupError("Could not find symbol table") + + +def parse_args(): + global args + + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument( + "-k", "--kernel", required=True, + help="Input zephyr ELF binary") + parser.add_argument( + "-o", "--output", required=True, + help="Output linker file") + parser.add_argument( + "-v", "--verbose", action="store_true", + help="Print extra debugging information") + args = parser.parse_args() + + +def main(): + parse_args() + + bit_len = None + + with open(args.kernel, "rb") as fp: + elf = ELFFile(fp) + args.little_endian = elf.little_endian + syms = get_symbols(elf) + + app_ram_size = syms['__app_last_address_used'] - \ + syms['__app_ram_start'] + bit_len = app_ram_size.bit_length() + + if bit_len: + align_size = 1 << bit_len + else: + align_size = 32 + + with open(args.output, "w") as fp: + fp.write("/***********************************************\n") + fp.write(" * Generated file, do not modify\n") + fp.write(" **********************************************/\n") + fp.write("_app_data_align = " + str(align_size) + ";\n") + fp.write(". = ALIGN(_app_data_align);\n") + +if __name__ == "__main__": + main()