diff --git a/CMakeLists.txt b/CMakeLists.txt index aabd871db116..57ae22df9853 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -632,6 +632,9 @@ 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 AND CONFIG_APP_SHARED_MEM ) + set(APP_SMEM_DEP app_smem_linker) + endif() if(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT AND CONFIG_APPLICATION_MEMORY) set(ALIGN_SIZING_DEP app_sizing_prebuilt linker_app_sizing_script) endif() @@ -1008,8 +1011,33 @@ configure_file( $ENV{ZEPHYR_BASE}/include/arch/arm/cortex_m/scripts/app_data_alignment.ld ${PROJECT_BINARY_DIR}/include/generated/app_data_alignment.ld) +configure_file( + $ENV{ZEPHYR_BASE}/include/arch/arm/cortex_m/scripts/app_smem.ld + ${PROJECT_BINARY_DIR}/include/generated/app_smem.ld) + if(CONFIG_CPU_HAS_MPU AND CONFIG_USERSPACE) + if(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT AND CONFIG_APP_SHARED_MEM) + set(GEN_APP_SMEM $ENV{ZEPHYR_BASE}/scripts/gen_app_smem.py) + set(APP_SMEM_LD "${PROJECT_BINARY_DIR}/include/generated/app_smem.ld") + set(OBJ_FILE_DIR "${PROJECT_BINARY_DIR}/../") + + add_custom_target( + ${APP_SMEM_DEP} ALL + DEPENDS zephyr_prebuilt + ) + + add_custom_command( + TARGET ${APP_SMEM_DEP} + COMMAND ${PYTHON_EXECUTABLE} ${GEN_APP_SMEM} + -d ${OBJ_FILE_DIR} + -o ${APP_SMEM_LD} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/ + COMMENT "Generating power of 2 aligned app_smem linker section" + ) + endif() + + if(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT AND CONFIG_APPLICATION_MEMORY) construct_add_custom_command_for_linker_pass(linker_app_sizing custom_command) @@ -1038,7 +1066,7 @@ if(CONFIG_CPU_HAS_MPU AND CONFIG_USERSPACE) 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_dependencies( app_sizing_prebuilt linker_app_sizing_script offsets ) add_custom_command( TARGET app_sizing_prebuilt @@ -1119,7 +1147,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 ${ALIGN_SIZING_DEP} ${PRIV_STACK_DEP} linker_pass_final_script) + add_dependencies( kernel_elf ${ALIGN_SIZING_DEP} ${PRIV_STACK_DEP} ${APP_SMEM_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/x86/core/x86_mmu.c b/arch/x86/core/x86_mmu.c index be5c71e5337b..fb5da643fb28 100644 --- a/arch/x86/core/x86_mmu.c +++ b/arch/x86/core/x86_mmu.c @@ -25,7 +25,10 @@ MMU_BOOT_REGION((u32_t)&_image_rom_start, (u32_t)&_image_rom_size, MMU_BOOT_REGION((u32_t)&__app_ram_start, (u32_t)&__app_ram_size, MMU_ENTRY_WRITE | MMU_ENTRY_USER | MMU_ENTRY_EXECUTE_DISABLE); #endif - +#ifdef CONFIG_APP_SHARED_MEM +MMU_BOOT_REGION((u32_t)&_app_smem_start, (u32_t)&_app_smem_size, + MMU_ENTRY_WRITE | MMU_ENTRY_USER | MMU_ENTRY_EXECUTE_DISABLE); +#endif /* __kernel_ram_size includes all unused memory, which is used for heaps. * User threads cannot access this unless granted at runtime. This is done * automatically for stacks. diff --git a/doc/kernel/usermode/usermode.rst b/doc/kernel/usermode/usermode.rst index b3f04d65f186..235b03f11588 100644 --- a/doc/kernel/usermode/usermode.rst +++ b/doc/kernel/usermode/usermode.rst @@ -190,3 +190,4 @@ for execution after the kernel starts: memory_domain.rst mpu_stack_objects.rst mpu_userspace.rst + usermode_sharedmem.rst diff --git a/doc/kernel/usermode/usermode_sharedmem.rst b/doc/kernel/usermode/usermode_sharedmem.rst new file mode 100644 index 000000000000..702027fe2ccd --- /dev/null +++ b/doc/kernel/usermode/usermode_sharedmem.rst @@ -0,0 +1,104 @@ +.. _usermode_sharedmem: + +Application Shared Memory +######################### + +.. note:: + + In this document, we will cover the basic usage of enabling shared + memory using a template around app_memory subsystem. + +Overview +******** + +The use of subsystem app_memory in userspace allows control of +shared memory between threads. The foundation of the implementation +consists of memory domains and partitions. Memory partitions are created +and used in the definition of variable to group them into a +common space. The memory partitions are linked to domains +that are then assigned to a thead. The process allows selective +access to memory from a thread and sharing of memory between two +threads by assigning a partion to two different domains. By using +the shared memory template, code to protect memory can be used +on different platform without the application needing to implement +specific handlers for each platform. Note the developer should understand +the hardware limitations in context to the maximum number of memory +partitions available to a thread. Specifically processors with MPU's +cannot support the same number of partitions as a MMU. + +This specific implementation adds a wrapper to simplify the programmers +task of using the app_memmory subsystem through the use of macros and +a python script to generate the linker script. The linker script provides +the proper alignment for processors requiring power of two boundaries. +Without the wrapper, a developer is required to implement custom +linker scripts for each processor the project. + +The general usage is as follows. Define CONFIG_APP_SHARED_MEM=y in the +proj.conf file in the project folder. Include app_memory/app_memdomain.h +in the userspace source file. Mark the variable to be placed in +a memory partition. The two markers are for data and bss respectivly: +_app_dmem(id) and _app_bmem(id). The id is used as the partition name. +The resulting section name can be seen in the linker.map as +"data_smem_id" and "data_smem_idb". + +To create a k_mem_partition, call the macro app_mem_partition(part0) +where "part0" is the name then used to refer to that partition. +This macro only creates a function and necessary data structures for +the later "initialization". + +To create a memory domain for the partition, the macro app_mem_domain(dom0) +is called where "dom0" is the name then used for the memory domain. +To initialize the partition (effectively adding the partition +to a linked list), init_part_part0() is called. This is followed +by init_app_memory(), which walks all partitions in the linked +list and calculates the sizes for each partition. + +Once the partition is initialized, the domain can be +initialized with init_domain_dom0(part0) which initializes the +domain with partition part0. + +After the domain has been initialized, the current thread +can be added using add_thread_dom0(k_current_get()). + +Example: + +.. code-block:: c + + /* create partition at top of file outside functions */ + app_mem_partition(part0); + /* create domain */ + app_mem_domain(dom0); + /* assign variables to the domain */ + _app_dmem(dom0) int var1; + _app_bmem(dom0) static volatile int var2; + + int main() + { + init_part_part0(); + init_app_memory(); + init_domain_dom0(part0); + add_thread_dom0(k_current_get()); + ... + } + +If multiple partitions are being created, a variadic +preprocessor macro can be used as provided in +app_macro_support.h: + +.. code-block:: c + + FOR_EACH(app_mem_partition, part0, part1, part2); + +or, for multiple domains, similarly: + +.. code-block:: c + + FOR_EACH(app_mem_domain, dom0, dom1); + +Similarly, the init_part_* can also be used in the macro: + +.. code-block:: c + + FOR_EACH(init_part, part0, part1, part2); + + diff --git a/include/app_memory/app_memdomain.h b/include/app_memory/app_memdomain.h new file mode 100644 index 000000000000..3ca3b75ba52f --- /dev/null +++ b/include/app_memory/app_memdomain.h @@ -0,0 +1,134 @@ +#ifndef _APP_MEMDOMAIN__H_ +#define _APP_MEMDOMAIN__H_ + +#include +#include +#include + +#if defined(CONFIG_X86) +#define MEM_DOMAIN_ALIGN_SIZE _STACK_BASE_ALIGN +#elif defined(STACK_ALIGN) +#define MEM_DOMAIN_ALIGN_SIZE STACK_ALIGN +#else +#error "Not implemented for this architecture" +#endif + +/* + * There has got to be a better way of doing this. This + * tries to ensure that a) each subsection has a + * data_smem_#id_b part and b) that each k_mem_partition + * matches the page size or MPU region. If there is no + * data_smem_#id_b subsection, then the size calculations + * will fail. Additionally, if each k_mem_partition does + * not match the page size or MPU region, then the + * partition will fail to be created. + * checkpatch.pl complains that __aligned(size) is + * preferred, but, if implemented, then complains about + * complex macro without parentheses. + */ +#define _app_dmem_pad(id) \ + __attribute__((aligned(MEM_DOMAIN_ALIGN_SIZE), \ + section("data_smem_" #id))) + +#define _app_bmem_pad(id) \ + __attribute__((aligned(MEM_DOMAIN_ALIGN_SIZE), \ + section("data_smem_" #id "b"))) + +/* + * Qualifier to collect any object preceded with _app + * and place into section "data_smem_". + * _app_dmem(#) is for variables meant to be stored in .data . + * _app_bmem(#) is intended for static variables that are + * initialized to zero. + */ +#define _app_dmem(id) \ + __attribute__((section("data_smem_" #id))) + +#define _app_bmem(id) \ + __attribute__((section("data_smem_" #id "b"))) + +/* + * Creation of a struct to save start addresses, sizes, and + * a pointer to a k_mem_partition. It also adds a linked + * list node. + */ +struct app_region { + char *dmem_start; + char *bmem_start; + u32_t smem_size; + u32_t dmem_size; + u32_t bmem_size; + struct k_mem_partition *partition; + sys_dnode_t lnode; +}; + +/* + * Declares a partition and provides a function to add the + * partition to the linke dlist and initialize the partition. + */ +#define appmem_partition(name) \ + extern char *data_smem_##name; \ + extern char *data_smem_##name##b; \ + _app_dmem_pad(name) char name##_dmem_pad; \ + _app_bmem_pad(name) char name##_bmem_pad; \ + __kernel struct k_mem_partition mem_domain_##name; \ + __kernel struct app_region name; \ + static inline void appmem_init_part_##name(void) \ + { \ + name.dmem_start = (char *)&data_smem_##name; \ + name.bmem_start = (char *)&data_smem_##name##b; \ + sys_dlist_append(&app_mem_list, &name.lnode); \ + mem_domain_##name.start = (u32_t) name.dmem_start; \ + mem_domain_##name.attr = K_MEM_PARTITION_P_RW_U_RW; \ + name.partition = &mem_domain_##name; \ + } + +/* + * A wrapper around the k_mem_domain_* functions. Goal here was + * to a) differentiate these operations from the k_mem_domain* + * functions, and b) to simply the usage and handling of data + * types (i.e. app_region, k_mem_domain, etc). + */ +#define appmem_domain(name) \ + __kernel struct k_mem_domain domain_##name; \ + static inline void appmem_add_thread_##name(k_tid_t thread) \ + { \ + k_mem_domain_add_thread(&domain_##name, thread); \ + } \ + static inline void appmem_rm_thread_##name(k_tid_t thread) \ + { \ + k_mem_domain_remove_thread(thread); \ + } \ + static inline void appmem_add_part_##name(struct app_region region) \ + { \ + k_mem_domain_add_partition(&domain_##name, \ + ®ion.partition[0]); \ + } \ + static inline void appmem_rm_part_##name(struct app_region region) \ + { \ + k_mem_domain_remove_partition(&domain_##name, \ + ®ion.partition[0]); \ + } \ + static inline void appmem_init_domain_##name(struct app_region region) \ + { \ + k_mem_domain_init(&domain_##name, 1, ®ion.partition); \ + } + +/* + * The following allows the FOR_EACH macro to call each partition's + * appmem_init_part_##name . Note: semicolon needed or else compiler + * complains as semicolon needed for function call once expanded by + * macro. + */ +#define appmem_init_part(name) \ + appmem_init_part_##name(); + +extern sys_dlist_t app_mem_list; + +extern void app_bss_zero(void); + +extern void app_calc_size(void); + +extern void appmem_init_app_memory(void); + +#endif /* _APP_MEMDOMAIN__H_ */ diff --git a/include/arch/arc/v2/linker.ld b/include/arch/arc/v2/linker.ld index 34b5f291f705..6de163205ecf 100644 --- a/include/arch/arc/v2/linker.ld +++ b/include/arch/arc/v2/linker.ld @@ -127,6 +127,23 @@ SECTIONS { GROUP_START(RAMABLE_REGION) +#include + SECTION_PROLOGUE(_APP_SMEM_SECTION_NAME, (OPTIONAL),) + { + MPU_MIN_SIZE_ALIGN + _image_ram_start = .; + _app_smem_start = .; +#if defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) + #include +#else + APP_SMEM_SECTION() +#endif + MPU_MIN_SIZE_ALIGN + _app_smem_end = .; + } GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION) + + _app_smem_size = _app_smem_end - _app_smem_start; + _app_smem_rom_start = LOADADDR(_APP_SMEM_SECTION_NAME); #ifdef CONFIG_APPLICATION_MEMORY SECTION_DATA_PROLOGUE(_APP_DATA_SECTION_NAME, (OPTIONAL),) { diff --git a/include/arch/arm/cortex_m/scripts/app_smem.ld b/include/arch/arm/cortex_m/scripts/app_smem.ld new file mode 100644 index 000000000000..513d7abe8c55 --- /dev/null +++ b/include/arch/arm/cortex_m/scripts/app_smem.ld @@ -0,0 +1,2 @@ +/* space holder */ +APP_SMEM_SECTION() diff --git a/include/arch/arm/cortex_m/scripts/linker.ld b/include/arch/arm/cortex_m/scripts/linker.ld index d17c97119d67..7d3fec09847b 100644 --- a/include/arch/arm/cortex_m/scripts/linker.ld +++ b/include/arch/arm/cortex_m/scripts/linker.ld @@ -219,15 +219,29 @@ SECTIONS } #endif +#include + SECTION_PROLOGUE(_APP_SMEM_SECTION_NAME, (OPTIONAL),) + { + . = ALIGN(4); + _image_ram_start = .; + _app_smem_start = .; +#if defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) + #include +#else + APP_SMEM_SECTION() +#endif + _app_smem_end = .; + _app_smem_size = _app_smem_end - _app_smem_start; + . = ALIGN(4); + } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) + + _app_smem_rom_start = LOADADDR(_APP_SMEM_SECTION_NAME); #ifdef CONFIG_APPLICATION_MEMORY SECTION_DATA_PROLOGUE(_APP_DATA_SECTION_NAME, (OPTIONAL),) { -#include - __app_ram_start = .; __app_data_ram_start = .; - _image_ram_start = .; APP_INPUT_SECTION(.data) APP_INPUT_SECTION(".data.*") __app_data_ram_end = .; @@ -271,9 +285,6 @@ SECTIONS */ . = ALIGN(4); __bss_start = .; -#ifndef CONFIG_APPLICATION_MEMORY - _image_ram_start = .; -#endif __kernel_ram_start = .; KERNEL_INPUT_SECTION(.bss) diff --git a/include/arch/x86/linker.ld b/include/arch/x86/linker.ld index 57b39441ea51..bb930d68d1ef 100644 --- a/include/arch/x86/linker.ld +++ b/include/arch/x86/linker.ld @@ -158,13 +158,24 @@ SECTIONS /* RAMABLE_REGION */ GROUP_START(RAMABLE_REGION) + /* APP SHARED MEMORY REGION */ + SECTION_PROLOGUE(_APP_SMEM_SECTION_NAME, (OPTIONAL),) + { + _image_ram_start = .; + _app_smem_start = .; + APP_SMEM_SECTION() + MMU_PAGE_ALIGN + _app_smem_end = .; + } GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION) + + _app_smem_size = _app_smem_end - _app_smem_start; + _app_smem_rom_start = LOADADDR(_APP_SMEM_SECTION_NAME); #ifdef CONFIG_APPLICATION_MEMORY SECTION_DATA_PROLOGUE(_APP_DATA_SECTION_NAME, (OPTIONAL),) { #ifndef CONFIG_XIP MMU_PAGE_ALIGN #endif - _image_ram_start = .; __app_ram_start = .; __app_data_ram_start = .; APP_INPUT_SECTION(.data) @@ -213,9 +224,6 @@ SECTIONS * a multiple of 4 bytes. */ . = ALIGN(4); -#ifndef CONFIG_APPLICATION_MEMORY - _image_ram_start = .; -#endif __kernel_ram_start = .; __bss_start = .; diff --git a/include/linker/linker-defs.h b/include/linker/linker-defs.h index 12fb49bfa828..4aa73581d8f2 100644 --- a/include/linker/linker-defs.h +++ b/include/linker/linker-defs.h @@ -130,10 +130,12 @@ UTIL_LISTIFY(NUM_KERNEL_OBJECT_FILES, X, sect) #define APP_INPUT_SECTION(sect) \ *(EXCLUDE_FILE (UTIL_LISTIFY(NUM_KERNEL_OBJECT_FILES, Y, ~)) sect) +#define APP_SMEM_SECTION() KEEP(*(SORT(data_smem_[_a-zA-Z0-9]*))) #else #define KERNEL_INPUT_SECTION(sect) *(sect) #define APP_INPUT_SECTION(sect) *(sect) +#define APP_SMEM_SECTION() KEEP(*(SORT(data_smem_[_a-zA-Z0-9]*))) #endif @@ -171,6 +173,15 @@ GDATA(__data_num_words) #else /* ! _ASMLANGUAGE */ #include +/* + * The following are externs symbols from the linker. This enables + * the dynamic k_mem_domain and k_mem_partition creation and alignment + * to the section produced in the linker. + */ +extern char _app_smem_start[]; +extern char _app_smem_end[]; +extern char _app_smem_size[]; +extern char _app_smem_rom_start[]; #ifdef CONFIG_APPLICATION_MEMORY /* Memory owned by the application. Start and end will be aligned for memory diff --git a/include/linker/sections.h b/include/linker/sections.h index 3b3a862d2d0e..c8f598f30b20 100644 --- a/include/linker/sections.h +++ b/include/linker/sections.h @@ -23,6 +23,7 @@ #define _BSS_SECTION_NAME bss #define _NOINIT_SECTION_NAME noinit +#define _APP_SMEM_SECTION_NAME app_smem #define _APP_DATA_SECTION_NAME app_datas #define _APP_BSS_SECTION_NAME app_bss #define _APP_NOINIT_SECTION_NAME app_noinit diff --git a/include/misc/util.h b/include/misc/util.h index b84421717eea..eeb1c988118c 100644 --- a/include/misc/util.h +++ b/include/misc/util.h @@ -400,5 +400,31 @@ static inline s64_t arithmetic_shift_right(s64_t value, u8_t shift) #define MACRO_MAP_13(macro, a, ...) macro(a)MACRO_MAP_12(macro, __VA_ARGS__,) #define MACRO_MAP_14(macro, a, ...) macro(a)MACRO_MAP_13(macro, __VA_ARGS__,) #define MACRO_MAP_15(macro, a, ...) macro(a)MACRO_MAP_14(macro, __VA_ARGS__,) +/* + * The following provides variadic preprocessor macro support to + * help eliminate multiple, repetitive function/macro calls. This + * allows up to 10 "arguments" in addition to _call . + * Note - derived from work on: + * https://codecraft.co/2014/11/25/variadic-macros-tricks/ + */ + +#define _GET_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define _for_0(_call, ...) +#define _for_1(_call, x) _call(x) +#define _for_2(_call, x, ...) _call(x) _for_1(_call, ##__VA_ARGS__) +#define _for_3(_call, x, ...) _call(x) _for_2(_call, ##__VA_ARGS__) +#define _for_4(_call, x, ...) _call(x) _for_3(_call, ##__VA_ARGS__) +#define _for_5(_call, x, ...) _call(x) _for_4(_call, ##__VA_ARGS__) +#define _for_6(_call, x, ...) _call(x) _for_5(_call, ##__VA_ARGS__) +#define _for_7(_call, x, ...) _call(x) _for_6(_call, ##__VA_ARGS__) +#define _for_8(_call, x, ...) _call(x) _for_7(_call, ##__VA_ARGS__) +#define _for_9(_call, x, ...) _call(x) _for_8(_call, ##__VA_ARGS__) +#define _for_10(_call, x, ...) _call(x) _for_9(_call, ##__VA_ARGS__) + +#define FOR_EACH(x, ...) \ + _GET_ARG(__VA_ARGS__, \ + _for_10, _for_9, _for_8, _for_7, _for_6, _for_5, \ + _for_4, _for_3, _for_2, _for_1, _for_0)(x, ##__VA_ARGS__) #endif /* _UTIL__H_ */ diff --git a/kernel/init.c b/kernel/init.c index 1a1eefdc25e1..c004ee7ea48c 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -187,6 +187,10 @@ void _data_copy(void) memcpy(&__ccm_data_start, &__ccm_data_rom_start, ((u32_t) &__ccm_data_end - (u32_t) &__ccm_data_start)); #endif +#ifdef CONFIG_APP_SHARED_MEM + memcpy(&_app_smem_start, &_app_smem_rom_start, + ((u32_t) &_app_smem_end - (u32_t) &_app_smem_start)); +#endif #ifdef CONFIG_APPLICATION_MEMORY memcpy(&__app_data_ram_start, &__app_data_rom_start, ((u32_t) &__app_data_ram_end - (u32_t) &__app_data_ram_start)); diff --git a/scripts/gen_app_smem.py b/scripts/gen_app_smem.py new file mode 100755 index 000000000000..e8c16106e61b --- /dev/null +++ b/scripts/gen_app_smem.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +import sys +import argparse +import os +import re +import string +from elftools.elf.elffile import ELFFile + +partitions=[] +variables=[] + +app_smem = "/*\n * The following is a dynamically created linker section.\n" \ + " * Please do not modify or delete.\n */\n" +dRegion_size = { "0x20":32 , \ + "0x40":64, \ + "0x80":128, \ + "0x100":256, \ + "0x200":512, \ + "0x400":1024, \ + "0x800":2048, \ + "0x1000":4096, \ + "0x2000":8192, \ + "0x4000":16384, \ + "0x8000":32768, \ + "0x10000":65536, \ + "0x20000":131072, \ + "0x40000":262144, \ + "0x80000":524288, \ + "0x100000":1048576, \ + "0x200000":2097152, \ + "0x400000":4194304, \ + "0x800000":8388608, \ + "0x1000000":16777216, \ + "0x2000000":33554432, \ + "0x4000000":67108864, \ + "0x8000000":134217728, \ + "0x10000000":268435456, \ + "0x20000000":536870912, \ + "0x40000000":1073741824, \ + "0x80000000":2147483648, \ + "0x100000000":4294967296} + +def build_linker_section(filename, regionSizes): + with open(filename, 'w') as f: + f.write(app_smem) + for part in partitions: + psize = "0" + if (part.endswith("b")): + continue + for partition, size in regionSizes: + if (partition == str(part).strip("b'")): + f.write("\t\t. = ALIGN(" + size + ");\n"); + psize = size + f.write("\t\t" + str(part).strip("b'") + " = .;\n") + f.write("\t\t*(SORT(" + str(part).strip("b'") + "*))\n") + f.write("\t\t. = ALIGN(_app_data_align);\n") + f.write("\t\t" + str(part).strip("b'") + "b_end = .;\n") + f.write("\t\t. = ALIGN(" + psize + ");\n") + + +def find_variables(filename): + flag = 0 + with open(filename, 'rb') as f: + objF = ELFFile( f) + if (not objF): + print("Error parsing file: ",filename) + os.exit(1) + sec = [ x for x in objF.iter_sections()] + for s in sec: + if ("smem" in s.name and not ".rel" in s.name): + variables.append( s) + + +def build_partitions(): + global partitions + s = set() + for var in variables: + s.add(var.name) + for sec in sorted(s): + partitions.append(sec) + +def calc_sec_size(): + d = dict() + if not variables: + return None + for v in variables: + if( v.name in d): + d[v.name] += v.header.sh_size + else: + d[v.name] = v.header.sh_size + return d + +def genRegionDef(): + res = [] + dSecs = calc_sec_size() + if(dSecs is None): + print("\n***\nError: Failed to identify smem regions in the object files\n***\n") + sys.exit(1) + ordered_defs = [] + for key, value in sorted(dRegion_size.items(), key=lambda o: (o[1],o[0]) ): + ordered_defs.append( ( value, key)) + + ltSecs = sorted(dSecs.items()) + szltSecs = len(ltSecs) + if( szltSecs % 2 == 1): + szltSecs += 1 + for r in range(int(szltSecs /2 )): + i = r*2 + sz = ltSecs[i][1] + ltSecs[1+i][1] + for d in ordered_defs: + if( sz < d[0]): + res.append( ( ltSecs[i][0], d[1])) + break + return res + + +def parse_args(): + global args + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("-d", "--directory", required=True, + help="Root build directory") + parser.add_argument("-o", "--output", required=True, + help="Output ld file") + args = parser.parse_args() + + +def main(): + parse_args() + startIndex = args.directory + fileOutput = args.output + for dirpath, dirs, files in os.walk(startIndex): + for filename in files: + if (filename.endswith(".obj") or filename.endswith(".OBJ")): + fullname = os.path.join(dirpath, filename) + find_variables(fullname) + build_partitions() + build_linker_section(fileOutput, genRegionDef()) + +if __name__ == '__main__': + main() diff --git a/scripts/sanitycheck b/scripts/sanitycheck index 7d86c15a21b8..01dcaedd8283 100755 --- a/scripts/sanitycheck +++ b/scripts/sanitycheck @@ -641,7 +641,7 @@ class SizeCalculator: "kobject_data", "mmu_tables", "app_pad", "priv_stacks", "ccm_data", "usb_descriptor", "usb_data", "usb_bos_desc", 'log_backends_sections', 'log_dynamic_sections', - 'log_const_sections'] + 'log_const_sections',"app_smem"] # These get copied into RAM only on non-XIP ro_sections = ["text", "ctors", "init_array", "reset", "object_access", "rodata", "devconfig", "net_l2", "vector", "_bt_settings_area"] diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index c76dadd3241d..3e71bac6f69e 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory_ifdef(CONFIG_APP_SHARED_MEM app_memory) add_subdirectory(debug) add_subdirectory(logging) add_subdirectory_ifdef(CONFIG_BT bluetooth) diff --git a/subsys/Kconfig b/subsys/Kconfig index ed3600419317..0630d7d67518 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -32,3 +32,5 @@ source "subsys/random/Kconfig" source "subsys/storage/Kconfig" source "subsys/settings/Kconfig" + +source "subsys/app_memory/Kconfig" diff --git a/subsys/app_memory/CMakeLists.txt b/subsys/app_memory/CMakeLists.txt new file mode 100644 index 000000000000..c974bbd7cbbf --- /dev/null +++ b/subsys/app_memory/CMakeLists.txt @@ -0,0 +1,2 @@ +zephyr_library() +zephyr_library_sources(app_memdomain.c) diff --git a/subsys/app_memory/Kconfig b/subsys/app_memory/Kconfig new file mode 100644 index 000000000000..a15b41d3a055 --- /dev/null +++ b/subsys/app_memory/Kconfig @@ -0,0 +1,9 @@ + +menu "General Kernel Options" +config APP_SHARED_MEM + bool + prompt "Application shared memory with app_memory" + default n + help + This is a wrapper around app_memory to simplify usage. +endmenu diff --git a/subsys/app_memory/app_memdomain.c b/subsys/app_memory/app_memdomain.c new file mode 100644 index 000000000000..282b53c59504 --- /dev/null +++ b/subsys/app_memory/app_memdomain.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +/* + * Initializes a double linked-list for the calculation of + * memory subsections. + */ +sys_dlist_t app_mem_list = SYS_DLIST_STATIC_INIT(&app_mem_list); + +/* + * The following zeroizes each "bss" part of each subsection + * as per the entries in the list. + */ +void app_bss_zero(void) +{ + sys_dnode_t *node, *next_node; + + SYS_DLIST_FOR_EACH_NODE_SAFE(&app_mem_list, node, next_node) + { + struct app_region *region = + CONTAINER_OF(node, struct app_region, lnode); + memset(region->bmem_start, 0, region->bmem_size); + } +} + +/* + * The following calculates the size of each subsection and adds + * the computed sizes to the region structures. These calculations + * are needed both for zeroizing "bss" parts of the partitions and + * for the creation of the k_mem_partition. + */ +void app_calc_size(void) +{ + sys_dnode_t *node, *next_node; + + SYS_DLIST_FOR_EACH_NODE_SAFE(&app_mem_list, node, next_node) + { + if (sys_dlist_is_tail(&app_mem_list, node)) { + struct app_region *region = + CONTAINER_OF(node, struct app_region, lnode); + region->bmem_size = + _app_smem_end - + (char *)region->bmem_start; + region->dmem_size = + (char *)region->bmem_start - + (char *)region->dmem_start; + region->smem_size = + region->bmem_size + region->dmem_size; + region->partition[0].size = + region->dmem_size + region->bmem_size; + } else { + struct app_region *region = + CONTAINER_OF(node, struct app_region, lnode); + struct app_region *nRegion = + CONTAINER_OF(next_node, struct app_region, + lnode); + region->bmem_size = + (char *)nRegion->dmem_start - + (char *)region->bmem_start; + region->dmem_size = + (char *)region->bmem_start - + (char *)region->dmem_start; + region->smem_size = + region->bmem_size + region->dmem_size; + region->partition[0].size = + region->dmem_size + region->bmem_size; + } + } +} + +/* + * "Initializes" by calculating subsection sizes and then + * zeroizing "bss" regions. + */ +void appmem_init_app_memory(void) +{ + app_calc_size(); + app_bss_zero(); +} diff --git a/tests/kernel/mem_protect/userspace/prj.conf b/tests/kernel/mem_protect/userspace/prj.conf index 020150f0b4fc..43a55c7756f7 100644 --- a/tests/kernel/mem_protect/userspace/prj.conf +++ b/tests/kernel/mem_protect/userspace/prj.conf @@ -1,3 +1,4 @@ CONFIG_ZTEST=y CONFIG_USERSPACE=y -CONFIG_APPLICATION_MEMORY=y +CONFIG_APPLICATION_MEMORY=n +CONFIG_APP_SHARED_MEM=y diff --git a/tests/kernel/mem_protect/userspace/src/main.c b/tests/kernel/mem_protect/userspace/src/main.c index 147ca7ff5f41..8661e2883143 100644 --- a/tests/kernel/mem_protect/userspace/src/main.c +++ b/tests/kernel/mem_protect/userspace/src/main.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #if defined(CONFIG_ARC) #include @@ -26,8 +28,24 @@ K_SEM_DEFINE(uthread_end_sem, 0, 1); K_SEM_DEFINE(test_revoke_sem, 0, 1); K_SEM_DEFINE(expect_fault_sem, 0, 1); -static volatile bool give_uthread_end_sem; -static volatile bool expect_fault; +/* + * Create partitions. part0 is for all variables to run + * ztest and this test suite. part1 and part2 are for + * subsequent test specifically for this new implementation. + */ +FOR_EACH(appmem_partition, part0, part1, part2); + +/* + * Create memory domains. dom0 is for the ztest and this + * test suite, specifically. dom1 is for a specific test + * in this test suite. + */ +FOR_EACH(appmem_domain, dom0, dom1); + +_app_dmem(part0) static volatile bool give_uthread_end_sem; +_app_dmem(part0) bool mem_access_check; + +_app_bmem(part0) static volatile bool expect_fault; #if defined(CONFIG_X86) #define REASON_HW_EXCEPTION _NANO_ERR_CPU_EXCEPTION @@ -41,7 +59,7 @@ static volatile bool expect_fault; #else #error "Not implemented for this architecture" #endif -static volatile unsigned int expected_reason; +_app_bmem(part0) static volatile unsigned int expected_reason; /* * We need something that can act as a memory barrier @@ -243,15 +261,16 @@ static void write_kernel_data(void) /* * volatile to avoid compiler mischief. */ -volatile int *priv_stack_ptr; +_app_dmem(part0) volatile int *priv_stack_ptr; #if defined(CONFIG_X86) /* * We can't inline this in the code or make it static * or local without triggering a warning on -Warray-bounds. */ -size_t size = MMU_PAGE_SIZE; +_app_dmem(part0) size_t size = MMU_PAGE_SIZE; #elif defined(CONFIG_ARC) -int32_t size = (0 - CONFIG_PRIVILEGED_STACK_SIZE - STACK_GUARD_SIZE); +_app_dmem(part0) s32_t size = (0 - CONFIG_PRIVILEGED_STACK_SIZE - + STACK_GUARD_SIZE); #endif static void read_priv_stack(void) @@ -297,7 +316,7 @@ static void write_priv_stack(void) } -static struct k_sem sem; +_app_bmem(part0) static struct k_sem sem; static void pass_user_object(void) { @@ -462,7 +481,7 @@ static void user_mode_enter(void) /* Define and initialize pipe. */ K_PIPE_DEFINE(kpipe, PIPE_LEN, BYTES_TO_READ_WRITE); -static size_t bytes_written_read; +_app_bmem(part0) static size_t bytes_written_read; static void write_kobject_user_pipe(void) { @@ -496,6 +515,56 @@ static void read_kobject_user_pipe(void) "did not fault"); } +/* Removed test for access_non_app_memory + * due to the APPLICATION_MEMORY variable + * defaulting to y, when enabled the + * section app_bss is made available to + * all threads breaking the test + */ + +/* Create bool in part1 partitions */ +_app_dmem(part1) bool thread_bool; + +static void shared_mem_thread(void) +{ + /* + * Try to access thread_bool_1 in denied memory + * domain. + */ + expect_fault = true; + expected_reason = REASON_HW_EXCEPTION; + BARRIER(); + thread_bool = false; + zassert_unreachable("Thread accessed global in other " + "memory domain\n"); +} + +static void access_other_memdomain(void) +{ + /* + * Following tests the ability for a thread to access data + * in a domain that it is denied. + */ + + /* initialize domain dom1 with partition part2 */ + appmem_init_domain_dom1(part2); + /* add partition part0 for test globals */ + appmem_add_part_dom1(part0); + /* remove current thread from domain dom0 */ + appmem_rm_thread_dom0(k_current_get()); + /* initialize domain with current thread*/ + appmem_add_thread_dom1(k_current_get()); + + /* Create user mode thread */ + k_thread_create(&uthread_thread, uthread_stack, STACKSIZE, + (k_thread_entry_t)shared_mem_thread, NULL, + NULL, NULL, -1, K_USER | K_INHERIT_PERMS, K_NO_WAIT); + + k_thread_abort(k_current_get()); + +} + + #if defined(CONFIG_ARM) extern u8_t *_k_priv_stack_find(void *obj); extern k_thread_stack_t ztest_thread_stack[]; @@ -503,6 +572,20 @@ extern k_thread_stack_t ztest_thread_stack[]; void test_main(void) { + /* partitions must be initialized first */ + FOR_EACH(appmem_init_part, part0, part1, part2); + /* + * Next, the app_memory must be initialized in order to + * calculate size of the dynamically created subsections. + */ + appmem_init_app_memory(); + /* Domain is initialized with partition part0 */ + appmem_init_domain_dom0(part0); + /* Next, the partition must be added to the domain */ + appmem_add_part_dom0(part1); + /* Finally, the current thread is added to domain */ + appmem_add_thread_dom0(k_current_get()); + #if defined(CONFIG_ARM) priv_stack_ptr = (int *)_k_priv_stack_find(ztest_thread_stack); #endif @@ -533,7 +616,9 @@ void test_main(void) ztest_user_unit_test(access_after_revoke), ztest_unit_test(user_mode_enter), ztest_user_unit_test(write_kobject_user_pipe), - ztest_user_unit_test(read_kobject_user_pipe) + ztest_user_unit_test(read_kobject_user_pipe), + ztest_user_unit_test(read_kobject_user_pipe), + ztest_unit_test(access_other_memdomain) ); ztest_run_test_suite(userspace); } diff --git a/tests/kernel/mem_protect/userspace/testcase.yaml b/tests/kernel/mem_protect/userspace/testcase.yaml index 3a26c11b2c74..c5d97b7f7dde 100644 --- a/tests/kernel/mem_protect/userspace/testcase.yaml +++ b/tests/kernel/mem_protect/userspace/testcase.yaml @@ -2,3 +2,4 @@ tests: kernel.memory_protection.userspace: filter: CONFIG_ARCH_HAS_USERSPACE tags: core security userspace ignore_faults + extra_sections: app_smem diff --git a/tests/ztest/include/ztest_test.h b/tests/ztest/include/ztest_test.h index 5d270e2f5660..a068da1ae230 100644 --- a/tests/ztest/include/ztest_test.h +++ b/tests/ztest/include/ztest_test.h @@ -13,6 +13,8 @@ #ifndef __ZTEST_TEST_H__ #define __ZTEST_TEST_H__ +#include + struct unit_test { const char *name; void (*test)(void); @@ -139,8 +141,17 @@ static inline void unit_test_noop(void) * * @param name Name of the testing suite */ + +/* definitions for use with testing application shared memory */ +#ifdef CONFIG_APP_SHARED_MEM +#define APPDMEMP0 _app_dmem(part0) +#define APPBMEMP0 _app_bmem(part0) +#else +#define APPDMEMP0 +#define APPBMEMP0 +#endif #define ztest_test_suite(name, ...) \ - static struct unit_test _##name[] = { \ + APPDMEMP0 static struct unit_test _##name[] = { \ __VA_ARGS__, { 0 } \ } /** diff --git a/tests/ztest/src/ztest.c b/tests/ztest/src/ztest.c index 8a9fe6ac96b5..b0916cdf0212 100644 --- a/tests/ztest/src/ztest.c +++ b/tests/ztest/src/ztest.c @@ -6,19 +6,21 @@ #include #include +#include #ifdef KERNEL __kernel static struct k_thread ztest_thread; #endif -enum { +/* APPDMEMP0 and APPBMEMP0 are used for the application shared memory test */ + +APPDMEMP0 enum { TEST_PHASE_SETUP, TEST_PHASE_TEST, TEST_PHASE_TEARDOWN, TEST_PHASE_FRAMEWORK } phase = TEST_PHASE_FRAMEWORK; -static int test_status; - +APPBMEMP0 static int test_status; static int cleanup_test(struct unit_test *test) { @@ -150,8 +152,9 @@ static int run_test(struct unit_test *test) K_THREAD_STACK_DEFINE(ztest_thread_stack, CONFIG_ZTEST_STACKSIZE + CONFIG_TEST_EXTRA_STACKSIZE); +/* APPBMEMP0 is used for the application shared memory test */ +APPBMEMP0 static int test_result; -static int test_result; __kernel static struct k_sem test_end_signal; void ztest_test_fail(void)