diff --git a/CMakeLists.txt b/CMakeLists.txt index 96b499909ce3d..94a4634f53c11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -351,9 +351,6 @@ endif() if(CONFIG_USERSPACE) set(APP_SMEM_ALIGNED_DEP app_smem_aligned_linker) set(APP_SMEM_UNALIGNED_DEP app_smem_unaligned_linker) - if(CONFIG_ARM) - set(PRIV_STACK_DEP priv_stacks_prebuilt) - endif() endif() get_property(TOPT GLOBAL PROPERTY TOPT) @@ -706,7 +703,6 @@ endif() # CONFIG_CODE_DATA_RELOCATION configure_linker_script( linker.cmd "" - ${PRIV_STACK_DEP} ${APP_SMEM_ALIGNED_DEP} ${CODE_RELOCATION_DEP} zephyr_generated_headers @@ -786,147 +782,7 @@ if(CONFIG_USERSPACE) get_property(compile_definitions_interface TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS) -endif() - - -# Warning most of this gperf code is duplicated below for -# gen_kobject_list.py / output_lib -if(CONFIG_ARM AND CONFIG_USERSPACE) - set(GEN_PRIV_STACKS $ENV{ZEPHYR_BASE}/scripts/gen_priv_stacks.py) - set(PROCESS_PRIV_STACKS_GPERF $ENV{ZEPHYR_BASE}/scripts/process_gperf.py) - - set(PRIV_STACKS priv_stacks_hash.gperf) - set(PRIV_STACKS_OUTPUT_SRC_PRE priv_stacks_hash_preprocessed.c) - set(PRIV_STACKS_OUTPUT_SRC priv_stacks_hash.c) - set(PRIV_STACKS_OUTPUT_OBJ priv_stacks_hash.c.obj) - set(PRIV_STACKS_OUTPUT_OBJ_RENAMED priv_stacks_hash_renamed.o) - - # Essentially what we are doing here is extracting some information - # out of the nearly finished elf file, generating the source code - # for a hash table based on that information, and then compiling and - # linking the hash table back into a now even more nearly finished - # elf file. - - # Use the script GEN_PRIV_STACKS to scan the kernel binary's - # (${ZEPHYR_PREBUILT_EXECUTABLE}) DWARF information to produce a table of kernel - # objects (PRIV_STACKS) which we will then pass to gperf - add_custom_command( - OUTPUT ${PRIV_STACKS} - COMMAND - ${PYTHON_EXECUTABLE} - ${GEN_PRIV_STACKS} - --kernel $ - --output ${PRIV_STACKS} - $<$:--verbose> - DEPENDS priv_stacks_prebuilt - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - add_custom_target(priv_stacks DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS}) - - if(${GPERF} STREQUAL GPERF-NOTFOUND) - message(FATAL_ERROR "Unable to find gperf") - endif() - - # Use gperf to generate C code (PRIV_STACKS_OUTPUT_SRC_PRE) which implements a - # perfect hashtable based on PRIV_STACKS - add_custom_command( - OUTPUT ${PRIV_STACKS_OUTPUT_SRC_PRE} - COMMAND - ${GPERF} -C - --output-file ${PRIV_STACKS_OUTPUT_SRC_PRE} - ${PRIV_STACKS} - DEPENDS priv_stacks ${PRIV_STACKS} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - add_custom_target(priv_stacks_output_src_pre DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_SRC_PRE}) - - # For our purposes the code/data generated by gperf is not optimal. - # - # The script PROCESS_GPERF creates a new c file OUTPUT_SRC based on - # OUTPUT_SRC_PRE to greatly reduce the amount of code/data generated - # since we know we are always working with pointer values - add_custom_command( - OUTPUT ${PRIV_STACKS_OUTPUT_SRC} - COMMAND - ${PYTHON_EXECUTABLE} - ${PROCESS_PRIV_STACKS_GPERF} - -i ${PRIV_STACKS_OUTPUT_SRC_PRE} - -o ${PRIV_STACKS_OUTPUT_SRC} - -p "struct _k_priv_stack_map" - $<$:--verbose> - DEPENDS priv_stacks_output_src_pre ${PRIV_STACKS_OUTPUT_SRC_PRE} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - add_custom_target(priv_stacks_output_src DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_SRC}) - - set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_SRC} - PROPERTIES COMPILE_DEFINITIONS "${compile_definitions_interface}") - - set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_SRC} - PROPERTIES COMPILE_FLAGS - "${NO_COVERAGE_FLAGS} -fno-function-sections -fno-data-sections ") - - # We need precise control of where generated text/data ends up in the final - # kernel image. Disable function/data sections and use objcopy to move - # generated data into special section names - add_library(priv_stacks_output_lib STATIC - ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_SRC} - ) - - # Turn off -ffunction-sections, etc. - # NB: Using a library instead of target_compile_options(priv_stacks_output_lib - # [...]) because a library's options have precedence - add_library(priv_stacks_output_lib_interface INTERFACE) - foreach(incl ${include_dir_in_interface}) - target_include_directories(priv_stacks_output_lib_interface INTERFACE ${incl}) - endforeach() - foreach(incl ${sys_include_dir_in_interface}) - target_include_directories(priv_stacks_output_lib_interface SYSTEM INTERFACE ${incl}) - endforeach() - - target_link_libraries(priv_stacks_output_lib priv_stacks_output_lib_interface) - - set(PRIV_STACKS_OUTPUT_OBJ_PATH ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/priv_stacks_output_lib.dir/${PRIV_STACKS_OUTPUT_OBJ}) - - set(obj_copy_cmd "") - set(obj_copy_sections_rename - .bss=.priv_stacks.noinit - .data=.priv_stacks.data - .text=.priv_stacks.text - .rodata=.priv_stacks.rodata - ) - bintools_objcopy( - RESULT_CMD_LIST obj_copy_cmd - SECTION_RENAME ${obj_copy_sections_rename} - FILE_INPUT ${PRIV_STACKS_OUTPUT_OBJ_PATH} - FILE_OUTPUT ${PRIV_STACKS_OUTPUT_OBJ_RENAMED} - ) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_OBJ_RENAMED} - ${obj_copy_cmd} - DEPENDS priv_stacks_output_lib - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - add_custom_target(priv_stacks_output_obj_renamed DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_OBJ_RENAMED}) - - add_library(priv_stacks_output_obj_renamed_lib STATIC IMPORTED GLOBAL) - set_property( - TARGET priv_stacks_output_obj_renamed_lib - PROPERTY - IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${PRIV_STACKS_OUTPUT_OBJ_RENAMED} - ) - add_dependencies( - priv_stacks_output_obj_renamed_lib - priv_stacks_output_obj_renamed - ) - - set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_OBJECT_FILES priv_stacks_output_obj_renamed_lib) -endif() - -# Warning: most of this gperf code is duplicated above for -# gen_priv_stacks.py / priv_stacks_output_lib -if(CONFIG_USERSPACE) set(GEN_KOBJ_LIST ${ZEPHYR_BASE}/scripts/gen_kobject_list.py) set(PROCESS_GPERF ${ZEPHYR_BASE}/scripts/process_gperf.py) @@ -986,7 +842,7 @@ if(CONFIG_USERSPACE) ${PROCESS_GPERF} -i ${OUTPUT_SRC_PRE} -o ${OUTPUT_SRC} - -p "struct _k_object" + -p "struct z_object" $<$:--verbose> DEPENDS output_src_pre ${OUTPUT_SRC_PRE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -1168,42 +1024,6 @@ if(CONFIG_USERSPACE) ) endif() -if(CONFIG_USERSPACE AND CONFIG_ARM) - configure_linker_script( - linker_priv_stacks.cmd - "" - ${CODE_RELOCATION_DEP} - ${APP_SMEM_ALIGNED_DEP} - ${APP_SMEM_ALIGNED_LD} - zephyr_generated_headers - ) - - add_custom_target( - linker_priv_stacks_script - DEPENDS - linker_priv_stacks.cmd - ) - - set_property(TARGET - linker_priv_stacks_script - PROPERTY INCLUDE_DIRECTORIES - ${ZEPHYR_INCLUDE_DIRS} - ) - - set(PRIV_STACK_LIB priv_stacks_output_obj_renamed_lib) - add_executable( priv_stacks_prebuilt misc/empty_file.c) - toolchain_ld_link_elf( - TARGET_ELF priv_stacks_prebuilt - OUTPUT_MAP ${PROJECT_BINARY_DIR}/priv_stacks_prebuilt.map - LIBRARIES_PRE_SCRIPT "" - LINKER_SCRIPT ${PROJECT_BINARY_DIR}/linker_priv_stacks.cmd - LIBRARIES_POST_SCRIPT "" - DEPENDENCIES ${CODE_RELOCATION_DEP} - ) - set_property(TARGET priv_stacks_prebuilt PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker_priv_stacks.cmd) - add_dependencies( priv_stacks_prebuilt linker_priv_stacks_script ${OFFSETS_LIB}) -endif() - # FIXME: Is there any way to get rid of empty_file.c? add_executable( ${ZEPHYR_PREBUILT_EXECUTABLE} misc/empty_file.c) toolchain_ld_link_elf( @@ -1211,11 +1031,10 @@ toolchain_ld_link_elf( OUTPUT_MAP ${PROJECT_BINARY_DIR}/${ZEPHYR_PREBUILT_EXECUTABLE}.map LIBRARIES_PRE_SCRIPT "" LINKER_SCRIPT ${PROJECT_BINARY_DIR}/linker.cmd - LIBRARIES_POST_SCRIPT ${PRIV_STACK_LIB} DEPENDENCIES ${CODE_RELOCATION_DEP} ) set_property(TARGET ${ZEPHYR_PREBUILT_EXECUTABLE} PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker.cmd) -add_dependencies( ${ZEPHYR_PREBUILT_EXECUTABLE} ${PRIV_STACK_DEP} ${LINKER_SCRIPT_TARGET} ${OFFSETS_LIB}) +add_dependencies( ${ZEPHYR_PREBUILT_EXECUTABLE} ${LINKER_SCRIPT_TARGET} ${OFFSETS_LIB}) set(generated_kernel_files ${GKSF} ${GKOF}) @@ -1230,7 +1049,6 @@ else() configure_linker_script( linker_pass_final.cmd "-DLINKER_PASS2" - ${PRIV_STACK_DEP} ${CODE_RELOCATION_DEP} ${ZEPHYR_PREBUILT_EXECUTABLE} zephyr_generated_headers @@ -1258,7 +1076,7 @@ else() DEPENDENCIES ${CODE_RELOCATION_DEP} ) set_property(TARGET ${ZEPHYR_FINAL_EXECUTABLE} PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker_pass_final.cmd) - add_dependencies( ${ZEPHYR_FINAL_EXECUTABLE} ${PRIV_STACK_DEP} ${LINKER_PASS_FINAL_SCRIPT_TARGET}) + add_dependencies( ${ZEPHYR_FINAL_EXECUTABLE} ${LINKER_PASS_FINAL_SCRIPT_TARGET}) # Use the pass2 elf as the final elf set(logical_target_for_zephyr_elf ${ZEPHYR_FINAL_EXECUTABLE}) diff --git a/CODEOWNERS b/CODEOWNERS index d1557d45aec7e..a6560b97a0be7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -375,7 +375,6 @@ /scripts/coccicheck @himanshujha199640 @JuliaLawall /scripts/coccinelle/ @himanshujha199640 @JuliaLawall /scripts/kconfig/ @ulfalizer -/scripts/elf_helper.py @andrewboie /scripts/sanity_chk/expr_parser.py @nashif /scripts/gen_app_partitions.py @andrewboie /scripts/dts/ @ulfalizer @galak @@ -383,7 +382,6 @@ /arch/x86/gen_gdt.py @andrewboie /arch/x86/gen_idt.py @andrewboie /scripts/gen_kobject_list.py @andrewboie -/scripts/gen_priv_stacks.py @andrewboie @agross-oss @ioannisg /scripts/gen_syscalls.py @andrewboie /scripts/net/ @jukkar @pfl /scripts/process_gperf.py @andrewboie diff --git a/arch/Kconfig b/arch/Kconfig index 938431654d52f..85882505953c7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -27,6 +27,9 @@ config ARM bool select ARCH_IS_SET select HAS_DTS + # FIXME: current state of the code for all ARM requires this, but + # is really only necessary for Cortex-M with ARM MPU! + select GEN_PRIV_STACKS help ARM architecture @@ -218,15 +221,8 @@ config PRIVILEGED_STACK_SIZE This option sets the privileged stack region size that will be used in addition to the user mode thread stack. During normal execution, this region will be inaccessible from user mode. During system calls, - this region will be utilized by the system call. - -config PRIVILEGED_STACK_TEXT_AREA - int "Privileged stacks text area" - default 512 if COVERAGE_GCOV - default 256 - depends on ARCH_HAS_USERSPACE - help - Stack text area size for privileged stacks. + this region will be utilized by the system call. This value must be + a multiple of the minimum stack alignment. config KOBJECT_TEXT_AREA int "Size if kobject text area" @@ -237,6 +233,17 @@ config KOBJECT_TEXT_AREA help Size of kernel object text area. Used in linker script. +config GEN_PRIV_STACKS + bool + help + Selected if the architecture requires that privilege elevation stacks + be allocated in a separate memory area. This is typical of arches + whose MPUs require regions to be power-of-two aligned/sized. + + FIXME: This should be removed and replaced with checks against + CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT, but both ARM and ARC + changes will be necessary for this. + config STACK_GROWS_UP bool "Stack grows towards higher memory addresses" help diff --git a/arch/arm/core/aarch32/thread.c b/arch/arm/core/aarch32/thread.c index e7751d226bc12..95e5f232436b1 100644 --- a/arch/arm/core/aarch32/thread.c +++ b/arch/arm/core/aarch32/thread.c @@ -16,10 +16,6 @@ #include #include -#ifdef CONFIG_USERSPACE -extern u8_t *z_priv_stack_find(void *obj); -#endif - /* An initial context, to be "restored" by z_arm_pendsv(), is put at the other * end of the stack, and thus reusable by the stack when not needed anymore. * diff --git a/doc/guides/build/index.rst b/doc/guides/build/index.rst index 81190aa6322bb..9dd19bb2b5ba2 100644 --- a/doc/guides/build/index.rst +++ b/doc/guides/build/index.rst @@ -230,15 +230,6 @@ The following is a detailed description of the scripts used during the build pro :start-after: """ :end-before: """ -.. _gen_priv_stacks.py: - -:zephyr_file:`scripts/gen_priv_stacks.py` -========================================== - -.. include:: ../../../scripts/gen_priv_stacks.py - :start-after: """ - :end-before: """ - .. _gen_idt.py: :zephyr_file:`arch/x86/gen_idt.py` diff --git a/doc/guides/documentation/index.rst b/doc/guides/documentation/index.rst index eb17b5773ce2f..df2f4b13361cc 100644 --- a/doc/guides/documentation/index.rst +++ b/doc/guides/documentation/index.rst @@ -396,7 +396,7 @@ For example:: .. code-block:: c - struct _k_object { + struct z_object { char *name; u8_t perms[CONFIG_MAX_THREAD_BYTES]; u8_t type; @@ -412,7 +412,7 @@ This would be rendered as: .. code-block:: c - struct _k_object { + struct z_object { char *name; u8_t perms[CONFIG_MAX_THREAD_BYTES]; u8_t type; diff --git a/doc/reference/usermode/kernelobjects.rst b/doc/reference/usermode/kernelobjects.rst index f493d32ac4ebd..d9477c4d768c4 100644 --- a/doc/reference/usermode/kernelobjects.rst +++ b/doc/reference/usermode/kernelobjects.rst @@ -6,7 +6,7 @@ Kernel Objects A kernel object can be one of three classes of data: * A core kernel object, such as a semaphore, thread, pipe, etc. -* A thread stack, which is an array of :c:type:`struct _k_thread_stack_element` +* A thread stack, which is an array of :c:type:`struct z_thread_stack_element` and declared with :c:macro:`K_THREAD_STACK_DEFINE()` * A device driver instance (struct device) that belongs to one of a defined set of subsystems @@ -116,7 +116,7 @@ be prevented. When a device struct is found, its API pointer is examined to determine what subsystem the driver belongs to. The table itself maps kernel object memory addresses to instances of -:c:type:`struct _k_object`, which has all the metadata for that object. This +:c:type:`struct z_object`, which has all the metadata for that object. This includes: * A bitfield indicating permissions on that object. All threads have a @@ -128,9 +128,8 @@ includes: instance of :cpp:enum:`k_objects`. * A set of flags for that object. This is currently used to track initialization state and whether an object is public or not. -* An extra data field. This is currently used for thread stack objects - to denote how large the stack is, and for thread objects to indicate - the thread's index in kernel object permission bitfields. +* An extra data field. The semantics of this field vary by object type, see + the definition of :c:type:`union z_object_data`. Dynamic objects allocated at runtime are tracked in a runtime red/black tree which is used in parallel to the gperf table when validating object pointers. @@ -210,8 +209,8 @@ Some objects will be implicitly initialized at boot: * Device driver objects are considered initialized after their init function is run by the kernel early in the boot process. -If a kernel object is initialized with a private static initializer, the -object must have :c:func:`_k_object_init()` called on it at some point by a supervisor +If a kernel object is initialized with a private static initializer, the object +must have :c:func:`z_object_init()` called on it at some point by a supervisor thread, otherwise the kernel will consider the object uninitialized if accessed by a user thread. This is very uncommon, typically only for kernel objects that are embedded within some larger struct and initialized statically. @@ -229,7 +228,7 @@ are embedded within some larger struct and initialized statically. }; ... - _k_object_init(&my_foo.sem); + z_object_init(&my_foo.sem); ... diff --git a/doc/reference/usermode/mpu_userspace.rst b/doc/reference/usermode/mpu_userspace.rst index df1d74013a6d8..25107c36c5ef6 100644 --- a/doc/reference/usermode/mpu_userspace.rst +++ b/doc/reference/usermode/mpu_userspace.rst @@ -8,7 +8,7 @@ set of stacks. These stacks exist in a 1:1 relationship with each thread stack defined in the system. The privileged stacks are created as a part of the build process. -A post-build script :ref:`gen_priv_stacks.py` scans the generated +A post-build script :ref:`gen_kobject_list.py` scans the generated ELF file and finds all of the thread stack objects. A set of privileged stacks, a lookup table, and a set of helper functions are created and added to the image. diff --git a/include/arch/arc/arch.h b/include/arch/arc/arch.h index a2676eb55134b..216e67a006ea1 100644 --- a/include/arch/arc/arch.h +++ b/include/arch/arc/arch.h @@ -139,17 +139,17 @@ extern "C" { #define ARCH_THREAD_STACK_DEFINE(sym, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(Z_ARC_THREAD_STACK_ALIGN(size)) \ sym[ARCH_THREAD_STACK_LEN(size)] #define ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(Z_ARC_THREAD_STACK_ALIGN(size)) \ sym[nmemb][Z_ARC_THREAD_STACK_ARRAY_LEN(size)] #define ARCH_THREAD_STACK_MEMBER(sym, size) \ - struct _k_thread_stack_element \ + struct z_thread_stack_element \ __aligned(Z_ARC_THREAD_STACK_ALIGN(size)) \ sym[ARCH_THREAD_STACK_LEN(size)] diff --git a/include/arch/arm/aarch32/arch.h b/include/arch/arm/aarch32/arch.h index 71ba6045c4bf8..fa985318b2610 100644 --- a/include/arch/arm/aarch32/arch.h +++ b/include/arch/arm/aarch32/arch.h @@ -198,11 +198,11 @@ extern "C" { #if defined(CONFIG_USERSPACE) && \ defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) #define ARCH_THREAD_STACK_DEFINE(sym, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(POW2_CEIL(size)) sym[POW2_CEIL(size)] #else #define ARCH_THREAD_STACK_DEFINE(sym, size) \ - struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) \ + struct z_thread_stack_element __noinit __aligned(STACK_ALIGN) \ sym[size+MPU_GUARD_ALIGN_AND_SIZE] #endif @@ -216,12 +216,12 @@ extern "C" { #if defined(CONFIG_USERSPACE) && \ defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) #define ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(POW2_CEIL(size)) \ sym[nmemb][ARCH_THREAD_STACK_LEN(size)] #else #define ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(STACK_ALIGN) \ sym[nmemb][ARCH_THREAD_STACK_LEN(size)] #endif @@ -229,11 +229,11 @@ extern "C" { #if defined(CONFIG_USERSPACE) && \ defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) #define ARCH_THREAD_STACK_MEMBER(sym, size) \ - struct _k_thread_stack_element __aligned(POW2_CEIL(size)) \ + struct z_thread_stack_element __aligned(POW2_CEIL(size)) \ sym[POW2_CEIL(size)] #else #define ARCH_THREAD_STACK_MEMBER(sym, size) \ - struct _k_thread_stack_element __aligned(STACK_ALIGN) \ + struct z_thread_stack_element __aligned(STACK_ALIGN) \ sym[size+MPU_GUARD_ALIGN_AND_SIZE] #endif diff --git a/include/arch/arm/aarch32/cortex_m/scripts/linker.ld b/include/arch/arm/aarch32/cortex_m/scripts/linker.ld index 77129bdcb3441..6ffe5f126cdda 100644 --- a/include/arch/arm/aarch32/cortex_m/scripts/linker.ld +++ b/include/arch/arm/aarch32/cortex_m/scripts/linker.ld @@ -156,7 +156,6 @@ SECTIONS { _image_text_start = .; -#include #include *(.text) @@ -221,7 +220,6 @@ SECTIONS #include #endif -#include #include /* @@ -373,10 +371,8 @@ SECTIONS __data_rom_start = LOADADDR(_DATA_SECTION_NAME); #include -#include #include -#include #include __data_ram_end = .; diff --git a/include/arch/arm/aarch32/cortex_r/scripts/linker.ld b/include/arch/arm/aarch32/cortex_r/scripts/linker.ld index b961c9fbdb6dd..e47352349495f 100644 --- a/include/arch/arm/aarch32/cortex_r/scripts/linker.ld +++ b/include/arch/arm/aarch32/cortex_r/scripts/linker.ld @@ -143,7 +143,6 @@ SECTIONS */ *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) -#include #include } GROUP_LINK_IN(ROMABLE_REGION) @@ -198,7 +197,6 @@ SECTIONS #include #endif -#include #include /* @@ -333,11 +331,7 @@ SECTIONS __data_rom_start = LOADADDR(_DATA_SECTION_NAME); #include -#include #include - -#include - #include __data_ram_end = .; diff --git a/include/arch/arm/aarch64/scripts/linker.ld b/include/arch/arm/aarch64/scripts/linker.ld index 6024579047df0..6f270968e5647 100644 --- a/include/arch/arm/aarch64/scripts/linker.ld +++ b/include/arch/arm/aarch64/scripts/linker.ld @@ -132,7 +132,6 @@ SECTIONS */ *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) -#include #include MMU_ALIGN; @@ -194,7 +193,6 @@ SECTIONS #include #endif -#include #include } GROUP_LINK_IN(ROMABLE_REGION) @@ -319,11 +317,7 @@ SECTIONS __data_rom_start = LOADADDR(_DATA_SECTION_NAME); #include -#include #include - -#include - #include __data_ram_end = .; diff --git a/include/arch/x86/thread_stack.h b/include/arch/x86/thread_stack.h index 979f7999dd465..d73288df2b6b5 100644 --- a/include/arch/x86/thread_stack.h +++ b/include/arch/x86/thread_stack.h @@ -199,7 +199,7 @@ struct z_x86_thread_stack_header { sizeof(struct z_x86_thread_stack_header) #define ARCH_THREAD_STACK_DEFINE(sym, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(Z_X86_STACK_BASE_ALIGN) \ sym[ROUND_UP((size), Z_X86_STACK_SIZE_ALIGN) + \ ARCH_THREAD_STACK_RESERVED] @@ -211,12 +211,12 @@ struct z_x86_thread_stack_header { ARCH_THREAD_STACK_RESERVED) #define ARCH_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(Z_X86_STACK_BASE_ALIGN) \ sym[nmemb][ARCH_THREAD_STACK_LEN(size)] #define ARCH_THREAD_STACK_MEMBER(sym, size) \ - struct _k_thread_stack_element __aligned(Z_X86_STACK_BASE_ALIGN) \ + struct z_thread_stack_element __aligned(Z_X86_STACK_BASE_ALIGN) \ sym[ROUND_UP((size), Z_X86_STACK_SIZE_ALIGN) + \ ARCH_THREAD_STACK_RESERVED] diff --git a/include/kernel.h b/include/kernel.h index 9d1d4e076e475..44f85ec1dfab7 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -135,6 +135,7 @@ struct k_poll_signal; struct k_mem_domain; struct k_mem_partition; struct k_futex; +struct z_futex_data; /** * @brief Kernel Object Types @@ -165,17 +166,51 @@ enum k_objects { */ #ifdef CONFIG_USERSPACE +#ifdef CONFIG_GEN_PRIV_STACKS +/* Metadata struct for K_OBJ_THREAD_STACK_ELEMENT */ +struct z_stack_data { + /* Size of the entire stack object, including reserved areas */ + size_t size; + + /* Stack buffer for privilege mode elevations */ + u8_t *priv; +}; +#endif /* CONFIG_GEN_PRIV_STACKS */ + +/* Object extra data. Only some objects use this, determined by object type */ +union z_object_data { + /* Backing mutex for K_OBJ_SYS_MUTEX */ + struct k_mutex *mutex; + + /* Numerical thread ID for K_OBJ_THREAD */ + unsigned int thread_id; + +#ifdef CONFIG_GEN_PRIV_STACKS + /* Metadata for K_OBJ_THREAD_STACK_ELEMENT */ + struct z_stack_data *stack_data; +#else + /* Stack buffer size for K_OBJ_THREAD_STACK_ELEMENT */ + size_t stack_size; +#endif /* CONFIG_GEN_PRIV_STACKS */ + + /* Futex wait queue and spinlock for K_OBJ_FUTEX */ + struct z_futex_data *futex_data; + + /* All other objects */ + int unused; +}; + /* Table generated by gperf, these objects are retrieved via * z_object_find() */ -struct _k_object { +struct z_object { void *name; u8_t perms[CONFIG_MAX_THREAD_BYTES]; u8_t type; u8_t flags; - uintptr_t data; + union z_object_data data; } __packed __aligned(4); -struct _k_object_assignment { +struct z_object_assignment { struct k_thread *thread; void * const *objects; }; @@ -195,7 +230,7 @@ struct _k_object_assignment { #define K_THREAD_ACCESS_GRANT(name_, ...) \ static void * const _CONCAT(_object_list_, name_)[] = \ { __VA_ARGS__, NULL }; \ - static const Z_STRUCT_SECTION_ITERABLE(_k_object_assignment, \ + static const Z_STRUCT_SECTION_ITERABLE(z_object_assignment, \ _CONCAT(_object_access_, name_)) = \ { (&_k_thread_obj_ ## name_), \ (_CONCAT(_object_list_, name_)) } @@ -384,15 +419,15 @@ static inline void k_obj_free(void *obj) * * Stacks should always be created with K_THREAD_STACK_DEFINE(). */ -struct __packed _k_thread_stack_element { +struct __packed z_thread_stack_element { char data; }; /** * @typedef k_thread_stack_t - * @brief Typedef of struct _k_thread_stack_element + * @brief Typedef of struct z_thread_stack_element * - * @see _k_thread_stack_element + * @see z_thread_stack_element */ /** @@ -5136,7 +5171,7 @@ static inline char *Z_THREAD_STACK_BUFFER(k_thread_stack_t *sym) * @req K-TSTACK-001 */ #define K_THREAD_STACK_DEFINE(sym, size) \ - struct _k_thread_stack_element __noinit __aligned(STACK_ALIGN) sym[size] + struct z_thread_stack_element __noinit __aligned(STACK_ALIGN) sym[size] /** * @brief Calculate size of stacks to be allocated in a stack array @@ -5166,7 +5201,7 @@ static inline char *Z_THREAD_STACK_BUFFER(k_thread_stack_t *sym) * @req K-TSTACK-001 */ #define K_THREAD_STACK_ARRAY_DEFINE(sym, nmemb, size) \ - struct _k_thread_stack_element __noinit \ + struct z_thread_stack_element __noinit \ __aligned(STACK_ALIGN) sym[nmemb][K_THREAD_STACK_LEN(size)] /** @@ -5183,7 +5218,7 @@ static inline char *Z_THREAD_STACK_BUFFER(k_thread_stack_t *sym) * @req K-TSTACK-001 */ #define K_THREAD_STACK_MEMBER(sym, size) \ - struct _k_thread_stack_element __aligned(STACK_ALIGN) sym[size] + struct z_thread_stack_element __aligned(STACK_ALIGN) sym[size] /** * @brief Return the size in bytes of a stack memory region diff --git a/include/linker/common-rom.ld b/include/linker/common-rom.ld index 3ed9e6d476c0f..f7e94955f86a6 100644 --- a/include/linker/common-rom.ld +++ b/include/linker/common-rom.ld @@ -53,9 +53,9 @@ */ SECTION_PROLOGUE(object_access,,) { - __k_object_assignment_list_start = .; - KEEP(*(".__k_object_assignment.*")) - __k_object_assignment_list_end = .; + _z_object_assignment_list_start = .; + KEEP(*("._z_object_assignment.*")) + _z_object_assignment_list_end = .; } GROUP_LINK_IN(ROMABLE_REGION) #endif diff --git a/include/linker/kobject.ld b/include/linker/kobject.ld index a75be3419191c..d5499efc6e4fd 100644 --- a/include/linker/kobject.ld +++ b/include/linker/kobject.ld @@ -39,5 +39,13 @@ *(".kobject_data.rodata*") #endif } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) -#endif /* CONFIG_USERSPACE */ +#ifdef CONFIG_GEN_PRIV_STACKS + SECTION_DATA_PROLOGUE(priv_stacks_noinit,,) + { + z_priv_stacks_ram_start = .; + *(".priv_stacks.noinit") + z_priv_stacks_ram_end = .; + } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) +#endif /* CONFIG_GEN_PRIV_STACKS */ +#endif /* CONFIG_USERSPACE */ diff --git a/include/linker/priv_stacks-noinit.ld b/include/linker/priv_stacks-noinit.ld deleted file mode 100644 index e275b194c9c11..0000000000000 --- a/include/linker/priv_stacks-noinit.ld +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2017 Linaro Limited. - * - * SPDX-License-Identifier: Apache-2.0 - */ - - SECTION_DATA_PROLOGUE(priv_stacks_noinit,,) - { - z_priv_stacks_ram_start = .; - *(".priv_stacks.noinit") - z_priv_stacks_ram_end = .; - } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) diff --git a/include/linker/priv_stacks-rom.ld b/include/linker/priv_stacks-rom.ld deleted file mode 100644 index 1f1196c23dab1..0000000000000 --- a/include/linker/priv_stacks-rom.ld +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2017 Linaro Limited. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifdef CONFIG_USERSPACE - /* Kept in RAM on non-XIP */ -#ifdef CONFIG_XIP - *(".priv_stacks.rodata*") -#endif -#endif /* CONFIG_USERSPACE */ - diff --git a/include/linker/priv_stacks-text.ld b/include/linker/priv_stacks-text.ld deleted file mode 100644 index 6fc4cc1d52e59..0000000000000 --- a/include/linker/priv_stacks-text.ld +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2017 Linaro Limited. - * - * SPDX-License-Identifier: Apache-2.0 - */ -#ifdef CONFIG_USERSPACE - /* We need to reserve room for the gperf generated hash functions. - * Fortunately, unlike the data tables, the size of the code is - * reasonably predictable. - */ - _priv_stacks_text_area_start = .; - *(".priv_stacks.text*") - _priv_stacks_text_area_end = .; - - _priv_stacks_text_area_used = _priv_stacks_text_area_end - _priv_stacks_text_area_start; - -#ifndef LINKER_PASS2 - PROVIDE(z_priv_stack_find = .); -#endif - - /* In a valid build the MAX function will always evaluate to the - second argument below, but to give the user a good error message - when the area overflows we need to temporarily corrupt the - location counter, and then detect the overflow with an assertion - later on. */ - - . = MAX(., _priv_stacks_text_area_start + CONFIG_PRIVILEGED_STACK_TEXT_AREA); - - ASSERT( - CONFIG_PRIVILEGED_STACK_TEXT_AREA >= _priv_stacks_text_area_used, -"The configuration system has incorrectly set -'CONFIG_PRIVILEGED_STACK_TEXT_AREA' to -CONFIG_PRIVILEGED_STACK_TEXT_AREA, which is not big enough. You must -through Kconfig either disable 'CONFIG_USERSPACE', or set -'CONFIG_PRIVILEGED_STACK_TEXT_AREA' to a value larger than -CONFIG_PRIVILEGED_STACK_TEXT_AREA." - ); -#endif /* CONFIG_USERSPACE */ diff --git a/include/linker/priv_stacks.ld b/include/linker/priv_stacks.ld deleted file mode 100644 index fe62f022c1a65..0000000000000 --- a/include/linker/priv_stacks.ld +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017 Linaro Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifdef CONFIG_USERSPACE - /* Constraints: - * - * - changes to the size of this section between build phases - * *must not* shift the memory address of any kernel objects, - * since it contains a hashtable of the memory addresses of those - * kernel objects - * - * - It is OK if this section itself is shifted in between builds; for - * example some arches may precede this section with generated MMU - * page tables which are also unpredictable in size. - * - * The size of the - * gperf tables is both a function of the number of kernel objects, - * *and* the specific memory addresses being hashed. It is not something - * that can be predicted without actually building and compiling it. - */ - SECTION_DATA_PROLOGUE(priv_stacks,,) - { - *(".priv_stacks.data*") - - /* This is also unpredictable in size, and has the same constraints. - * On XIP systems this will get put at the very end of ROM. - */ -#ifndef CONFIG_XIP - *(".priv_stacks.rodata*") -#endif - } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) -#endif /* CONFIG_USERSPACE */ - diff --git a/include/sys/arch_interface.h b/include/sys/arch_interface.h index fad6f92762d8c..4d1b921c6795a 100644 --- a/include/sys/arch_interface.h +++ b/include/sys/arch_interface.h @@ -41,7 +41,7 @@ extern "C" { struct k_thread; struct k_mem_domain; -typedef struct _k_thread_stack_element k_thread_stack_t; +typedef struct z_thread_stack_element k_thread_stack_t; typedef void (*k_thread_entry_t)(void *p1, void *p2, void *p3); diff --git a/include/syscall_handler.h b/include/syscall_handler.h index 4a0ec651a1e6c..60b14f8894179 100644 --- a/include/syscall_handler.h +++ b/include/syscall_handler.h @@ -46,19 +46,19 @@ enum _obj_init_check { * -EPERM If the caller does not have permissions * -EINVAL Object is not initialized */ -int z_object_validate(struct _k_object *ko, enum k_objects otype, - enum _obj_init_check init); +int z_object_validate(struct z_object *ko, enum k_objects otype, + enum _obj_init_check init); /** * Dump out error information on failed z_object_validate() call * * @param retval Return value from z_object_validate() * @param obj Kernel object we were trying to verify - * @param ko If retval=-EPERM, struct _k_object * that was looked up, or NULL + * @param ko If retval=-EPERM, struct z_object * that was looked up, or NULL * @param otype Expected type of the kernel object */ -extern void z_dump_object_error(int retval, void *obj, struct _k_object *ko, - enum k_objects otype); +extern void z_dump_object_error(int retval, void *obj, struct z_object *ko, + enum k_objects otype); /** * Kernel object validation function @@ -70,14 +70,14 @@ extern void z_dump_object_error(int retval, void *obj, struct _k_object *ko, * @return Kernel object's metadata, or NULL if the parameter wasn't the * memory address of a kernel object */ -extern struct _k_object *z_object_find(void *obj); +extern struct z_object *z_object_find(void *obj); -typedef void (*_wordlist_cb_func_t)(struct _k_object *ko, void *context); +typedef void (*_wordlist_cb_func_t)(struct z_object *ko, void *context); /** * Iterate over all the kernel object metadata in the system * - * @param func function to run on each struct _k_object + * @param func function to run on each struct z_object * @param context Context pointer to pass to each invocation */ extern void z_object_wordlist_foreach(_wordlist_cb_func_t func, void *context); @@ -97,7 +97,7 @@ extern void z_thread_perms_inherit(struct k_thread *parent, * @param ko Kernel object metadata to update * @param thread The thread to grant permission */ -extern void z_thread_perms_set(struct _k_object *ko, struct k_thread *thread); +extern void z_thread_perms_set(struct z_object *ko, struct k_thread *thread); /** * Revoke a thread's permission to a kernel object @@ -105,7 +105,7 @@ extern void z_thread_perms_set(struct _k_object *ko, struct k_thread *thread); * @param ko Kernel object metadata to update * @param thread The thread to grant permission */ -extern void z_thread_perms_clear(struct _k_object *ko, struct k_thread *thread); +extern void z_thread_perms_clear(struct z_object *ko, struct k_thread *thread); /* * Revoke access to all objects for the provided thread @@ -393,10 +393,10 @@ extern int z_user_string_copy(char *dst, const char *src, size_t maxlen); #define Z_SYSCALL_MEMORY_ARRAY_WRITE(ptr, nmemb, size) \ Z_SYSCALL_MEMORY_ARRAY(ptr, nmemb, size, 1) -static inline int z_obj_validation_check(struct _k_object *ko, - void *obj, - enum k_objects otype, - enum _obj_init_check init) +static inline int z_obj_validation_check(struct z_object *ko, + void *obj, + enum k_objects otype, + enum _obj_init_check init) { int ret; diff --git a/kernel/futex.c b/kernel/futex.c index c31fb0892acda..3ec3b05d2f6eb 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -14,14 +14,14 @@ static struct z_futex_data *k_futex_find_data(struct k_futex *futex) { - struct _k_object *obj; + struct z_object *obj; obj = z_object_find(futex); if (obj == NULL || obj->type != K_OBJ_FUTEX) { return NULL; } - return (struct z_futex_data *)obj->data; + return obj->data.futex_data; } int z_impl_k_futex_wake(struct k_futex *futex, bool wake_all) diff --git a/kernel/include/kernel_internal.h b/kernel/include/kernel_internal.h index 15f6d4ac3c0f7..c1fd0e2cf0bb8 100644 --- a/kernel/include/kernel_internal.h +++ b/kernel/include/kernel_internal.h @@ -124,6 +124,10 @@ extern struct k_thread z_idle_threads[CONFIG_MP_NUM_CPUS]; extern K_THREAD_STACK_ARRAY_DEFINE(z_interrupt_stacks, CONFIG_MP_NUM_CPUS, CONFIG_ISR_STACK_SIZE); +#ifdef CONFIG_GEN_PRIV_STACKS +extern u8_t *z_priv_stack_find(k_thread_stack_t *stack); +#endif + #ifdef __cplusplus } #endif diff --git a/kernel/sched.c b/kernel/sched.c index 00b94d60eea33..25bbb9463f0f0 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1455,7 +1455,7 @@ int z_impl_k_thread_join(struct k_thread *thread, s32_t timeout) */ static bool thread_obj_validate(struct k_thread *thread) { - struct _k_object *ko = z_object_find(thread); + struct z_object *ko = z_object_find(thread); int ret = z_object_validate(ko, K_OBJ_THREAD, _OBJ_INIT_TRUE); switch (ret) { diff --git a/kernel/thread.c b/kernel/thread.c index 751cc392e8140..ce14b20a1c763 100644 --- a/kernel/thread.c +++ b/kernel/thread.c @@ -325,7 +325,7 @@ static inline int z_vrfy_k_thread_name_copy(k_tid_t thread, { #ifdef CONFIG_THREAD_NAME size_t len; - struct _k_object *ko = z_object_find(thread); + struct z_object *ko = z_object_find(thread); /* Special case: we allow reading the names of initialized threads * even if we don't have permission on them @@ -641,14 +641,14 @@ k_tid_t z_vrfy_k_thread_create(struct k_thread *new_thread, void *p1, void *p2, void *p3, int prio, u32_t options, s32_t delay) { - size_t total_size; - struct _k_object *stack_object; + size_t total_size, stack_obj_size; + struct z_object *stack_object; /* The thread and stack objects *must* be in an uninitialized state */ Z_OOPS(Z_SYSCALL_OBJ_NEVER_INIT(new_thread, K_OBJ_THREAD)); stack_object = z_object_find(stack); Z_OOPS(Z_SYSCALL_VERIFY_MSG(z_obj_validation_check(stack_object, stack, - K_OBJ__THREAD_STACK_ELEMENT, + K_OBJ_THREAD_STACK_ELEMENT, _OBJ_INIT_FALSE) == 0, "bad stack object")); @@ -664,9 +664,14 @@ k_tid_t z_vrfy_k_thread_create(struct k_thread *new_thread, /* Testing less-than-or-equal since additional room may have been * allocated for alignment constraints */ - Z_OOPS(Z_SYSCALL_VERIFY_MSG(total_size <= stack_object->data, - "stack size %zu is too big, max is %lu", - total_size, stack_object->data)); +#ifdef CONFIG_GEN_PRIV_STACKS + stack_obj_size = stack_object->data.stack_data->size; +#else + stack_obj_size = stack_object->data.stack_size; +#endif + Z_OOPS(Z_SYSCALL_VERIFY_MSG(total_size <= stack_obj_size, + "stack size %zu is too big, max is %zu", + total_size, stack_obj_size)); /* User threads may only create other user threads and they can't * be marked as essential @@ -699,7 +704,7 @@ k_tid_t z_vrfy_k_thread_create(struct k_thread *new_thread, static void grant_static_access(void) { - Z_STRUCT_SECTION_FOREACH(_k_object_assignment, pos) { + Z_STRUCT_SECTION_FOREACH(z_object_assignment, pos) { for (int i = 0; pos->objects[i] != NULL; i++) { k_object_access_grant(pos->objects[i], pos->thread); diff --git a/kernel/userspace.c b/kernel/userspace.c index 240f4fbea4584..281f072c8858a 100644 --- a/kernel/userspace.c +++ b/kernel/userspace.c @@ -58,7 +58,7 @@ static struct k_spinlock obj_lock; /* kobj struct data */ extern u8_t _thread_idx_map[CONFIG_MAX_THREAD_BYTES]; #endif -static void clear_perms_cb(struct _k_object *ko, void *ctx_ptr); +static void clear_perms_cb(struct z_object *ko, void *ctx_ptr); const char *otype_to_str(enum k_objects otype) { @@ -90,15 +90,34 @@ struct perm_ctx { struct k_thread *parent; }; +#ifdef CONFIG_GEN_PRIV_STACKS +/* See write_gperf_table() in scripts/gen_kobject_list.py. The privilege + * mode stacks are allocated as an array. The base of the array is + * aligned to Z_PRIVILEGE_STACK_ALIGN, and all members must be as well. + */ +BUILD_ASSERT(CONFIG_PRIVILEGED_STACK_SIZE % Z_PRIVILEGE_STACK_ALIGN == 0); + +u8_t *z_priv_stack_find(k_thread_stack_t *stack) +{ + struct z_object *obj = z_object_find(stack); + + __ASSERT(obj != NULL, "stack object not found"); + __ASSERT(obj->type == K_OBJ_THREAD_STACK_ELEMENT, + "bad stack object"); + + return obj->data.stack_data->priv; +} +#endif /* CONFIG_GEN_PRIV_STACKS */ + #ifdef CONFIG_DYNAMIC_OBJECTS struct dyn_obj { - struct _k_object kobj; + struct z_object kobj; sys_dnode_t obj_list; struct rbnode node; /* must be immediately before data member */ u8_t data[]; /* The object itself */ }; -extern struct _k_object *z_object_gperf_find(void *obj); +extern struct z_object *z_object_gperf_find(void *obj); extern void z_object_gperf_wordlist_foreach(_wordlist_cb_func_t func, void *context); @@ -242,7 +261,7 @@ void *z_impl_k_object_alloc(enum k_objects otype) * to request memory that is aligned */ __ASSERT(otype > K_OBJ_ANY && otype < K_OBJ_LAST && - otype != K_OBJ__THREAD_STACK_ELEMENT, + otype != K_OBJ_THREAD_STACK_ELEMENT, "bad object type requested"); dyn_obj = z_thread_malloc(sizeof(*dyn_obj) + obj_size_get(otype)); @@ -263,7 +282,7 @@ void *z_impl_k_object_alloc(enum k_objects otype) return NULL; } - dyn_obj->kobj.data = tidx; + dyn_obj->kobj.data.thread_id = tidx; } /* The allocating thread implicitly gets permission on kernel objects @@ -297,7 +316,7 @@ void k_object_free(void *obj) sys_dlist_remove(&dyn_obj->obj_list); if (dyn_obj->kobj.type == K_OBJ_THREAD) { - thread_idx_free(dyn_obj->kobj.data); + thread_idx_free(dyn_obj->kobj.data.thread_id); } } k_spin_unlock(&objfree_lock, key); @@ -307,9 +326,9 @@ void k_object_free(void *obj) } } -struct _k_object *z_object_find(void *obj) +struct z_object *z_object_find(void *obj) { - struct _k_object *ret; + struct z_object *ret; ret = z_object_gperf_find(obj); @@ -340,9 +359,9 @@ void z_object_wordlist_foreach(_wordlist_cb_func_t func, void *context) } #endif /* CONFIG_DYNAMIC_OBJECTS */ -static int thread_index_get(struct k_thread *thread) +static unsigned int thread_index_get(struct k_thread *thread) { - struct _k_object *ko; + struct z_object *ko; ko = z_object_find(thread); @@ -350,10 +369,10 @@ static int thread_index_get(struct k_thread *thread) return -1; } - return ko->data; + return ko->data.thread_id; } -static void unref_check(struct _k_object *ko, uintptr_t index) +static void unref_check(struct z_object *ko, uintptr_t index) { k_spinlock_key_t key = k_spin_lock(&obj_lock); @@ -401,7 +420,7 @@ static void unref_check(struct _k_object *ko, uintptr_t index) k_spin_unlock(&obj_lock, key); } -static void wordlist_cb(struct _k_object *ko, void *ctx_ptr) +static void wordlist_cb(struct z_object *ko, void *ctx_ptr) { struct perm_ctx *ctx = (struct perm_ctx *)ctx_ptr; @@ -424,7 +443,7 @@ void z_thread_perms_inherit(struct k_thread *parent, struct k_thread *child) } } -void z_thread_perms_set(struct _k_object *ko, struct k_thread *thread) +void z_thread_perms_set(struct z_object *ko, struct k_thread *thread) { int index = thread_index_get(thread); @@ -433,7 +452,7 @@ void z_thread_perms_set(struct _k_object *ko, struct k_thread *thread) } } -void z_thread_perms_clear(struct _k_object *ko, struct k_thread *thread) +void z_thread_perms_clear(struct z_object *ko, struct k_thread *thread) { int index = thread_index_get(thread); @@ -443,7 +462,7 @@ void z_thread_perms_clear(struct _k_object *ko, struct k_thread *thread) } } -static void clear_perms_cb(struct _k_object *ko, void *ctx_ptr) +static void clear_perms_cb(struct z_object *ko, void *ctx_ptr) { uintptr_t id = (uintptr_t)ctx_ptr; @@ -459,7 +478,7 @@ void z_thread_perms_all_clear(struct k_thread *thread) } } -static int thread_perms_test(struct _k_object *ko) +static int thread_perms_test(struct z_object *ko) { int index; @@ -474,7 +493,7 @@ static int thread_perms_test(struct _k_object *ko) return 0; } -static void dump_permission_error(struct _k_object *ko) +static void dump_permission_error(struct z_object *ko) { int index = thread_index_get(_current); LOG_ERR("thread %p (%d) does not have permission on %s %p", @@ -483,7 +502,7 @@ static void dump_permission_error(struct _k_object *ko) LOG_HEXDUMP_ERR(ko->perms, sizeof(ko->perms), "permission bitmap"); } -void z_dump_object_error(int retval, void *obj, struct _k_object *ko, +void z_dump_object_error(int retval, void *obj, struct z_object *ko, enum k_objects otype) { switch (retval) { @@ -507,7 +526,7 @@ void z_dump_object_error(int retval, void *obj, struct _k_object *ko, void z_impl_k_object_access_grant(void *object, struct k_thread *thread) { - struct _k_object *ko = z_object_find(object); + struct z_object *ko = z_object_find(object); if (ko != NULL) { z_thread_perms_set(ko, thread); @@ -516,7 +535,7 @@ void z_impl_k_object_access_grant(void *object, struct k_thread *thread) void k_object_access_revoke(void *object, struct k_thread *thread) { - struct _k_object *ko = z_object_find(object); + struct z_object *ko = z_object_find(object); if (ko != NULL) { z_thread_perms_clear(ko, thread); @@ -530,14 +549,14 @@ void z_impl_k_object_release(void *object) void k_object_access_all_grant(void *object) { - struct _k_object *ko = z_object_find(object); + struct z_object *ko = z_object_find(object); if (ko != NULL) { ko->flags |= K_OBJ_FLAG_PUBLIC; } } -int z_object_validate(struct _k_object *ko, enum k_objects otype, +int z_object_validate(struct z_object *ko, enum k_objects otype, enum _obj_init_check init) { if (unlikely((ko == NULL) || @@ -572,7 +591,7 @@ int z_object_validate(struct _k_object *ko, enum k_objects otype, void z_object_init(void *obj) { - struct _k_object *ko; + struct z_object *ko; /* By the time we get here, if the caller was from userspace, all the * necessary checks have been done in z_object_validate(), which takes @@ -597,7 +616,7 @@ void z_object_init(void *obj) void z_object_recycle(void *obj) { - struct _k_object *ko = z_object_find(obj); + struct z_object *ko = z_object_find(obj); if (ko != NULL) { (void)memset(ko->perms, 0, sizeof(ko->perms)); @@ -608,7 +627,7 @@ void z_object_recycle(void *obj) void z_object_uninit(void *obj) { - struct _k_object *ko; + struct z_object *ko; /* See comments in z_object_init() */ ko = z_object_find(obj); diff --git a/kernel/userspace_handler.c b/kernel/userspace_handler.c index 054fa1724d838..dc4602ca9110d 100644 --- a/kernel/userspace_handler.c +++ b/kernel/userspace_handler.c @@ -8,9 +8,9 @@ #include #include -static struct _k_object *validate_any_object(void *obj) +static struct z_object *validate_any_object(void *obj) { - struct _k_object *ko; + struct z_object *ko; int ret; ko = z_object_find(obj); @@ -39,7 +39,7 @@ static struct _k_object *validate_any_object(void *obj) static inline void z_vrfy_k_object_access_grant(void *object, struct k_thread *thread) { - struct _k_object *ko; + struct z_object *ko; Z_OOPS(Z_SYSCALL_OBJ_INIT(thread, K_OBJ_THREAD)); ko = validate_any_object(object); @@ -51,7 +51,7 @@ static inline void z_vrfy_k_object_access_grant(void *object, static inline void z_vrfy_k_object_release(void *object) { - struct _k_object *ko; + struct z_object *ko; ko = validate_any_object((void *)object); Z_OOPS(Z_SYSCALL_VERIFY_MSG(ko != NULL, "object %p access denied", @@ -63,7 +63,7 @@ static inline void z_vrfy_k_object_release(void *object) static inline void *z_vrfy_k_object_alloc(enum k_objects otype) { Z_OOPS(Z_SYSCALL_VERIFY_MSG(otype > K_OBJ_ANY && otype < K_OBJ_LAST && - otype != K_OBJ__THREAD_STACK_ELEMENT, + otype != K_OBJ_THREAD_STACK_ELEMENT, "bad object type %d requested", otype)); return z_impl_k_object_alloc(otype); diff --git a/lib/os/mutex.c b/lib/os/mutex.c index 84f207074ffa6..43fc254a7acf9 100644 --- a/lib/os/mutex.c +++ b/lib/os/mutex.c @@ -11,14 +11,14 @@ static struct k_mutex *get_k_mutex(struct sys_mutex *mutex) { - struct _k_object *obj; + struct z_object *obj; obj = z_object_find(mutex); if (obj == NULL || obj->type != K_OBJ_SYS_MUTEX) { return NULL; } - return (struct k_mutex *)obj->data; + return obj->data.mutex; } static bool check_sys_mutex_addr(struct sys_mutex *addr) diff --git a/scripts/elf_helper.py b/scripts/elf_helper.py deleted file mode 100644 index 44b53162e2ea3..0000000000000 --- a/scripts/elf_helper.py +++ /dev/null @@ -1,606 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2017-2018 Linaro -# -# SPDX-License-Identifier: Apache-2.0 - -import sys -import os -import struct -from distutils.version import LooseVersion - -from collections import OrderedDict - -import elftools -from elftools.elf.elffile import ELFFile -from elftools.elf.sections import SymbolTableSection - -if LooseVersion(elftools.__version__) < LooseVersion('0.24'): - sys.exit("pyelftools is out of date, need version 0.24 or later") - - -def subsystem_to_enum(subsys): - return "K_OBJ_DRIVER_" + subsys[:-11].upper() - - -def kobject_to_enum(kobj): - if kobj.startswith("k_") or kobj.startswith("_k_"): - name = kobj[2:] - else: - name = kobj - - return "K_OBJ_%s" % name.upper() - - -DW_OP_addr = 0x3 -DW_OP_fbreg = 0x91 -STACK_TYPE = "_k_thread_stack_element" -thread_counter = 0 -sys_mutex_counter = 0 -futex_counter = 0 - -# Global type environment. Populated by pass 1. -type_env = {} -extern_env = {} -kobjects = {} -subsystems = {} - -# --- debug stuff --- - -scr = os.path.basename(sys.argv[0]) - -# --- type classes ---- - - -class KobjectInstance: - def __init__(self, type_obj, addr): - global thread_counter - global sys_mutex_counter - global futex_counter - - self.addr = addr - self.type_obj = type_obj - - # Type name determined later since drivers needs to look at the - # API struct address - self.type_name = None - - if self.type_obj.name == "k_thread": - # Assign an ID for this thread object, used to track its - # permissions to other kernel objects - self.data = thread_counter - thread_counter = thread_counter + 1 - elif self.type_obj.name == "sys_mutex": - self.data = "(uintptr_t)(&kernel_mutexes[%d])" % sys_mutex_counter - sys_mutex_counter += 1 - elif self.type_obj.name == "k_futex": - self.data = "(uintptr_t)(&futex_data[%d])" % futex_counter - futex_counter += 1 - else: - self.data = 0 - - -class KobjectType: - def __init__(self, offset, name, size, api=False): - self.name = name - self.size = size - self.offset = offset - self.api = api - - def __repr__(self): - return "" % self.name - - @staticmethod - def has_kobject(): - return True - - def get_kobjects(self, addr): - return {addr: KobjectInstance(self, addr)} - - -class ArrayType: - def __init__(self, offset, elements, member_type): - self.elements = elements - self.member_type = member_type - self.offset = offset - - def __repr__(self): - return "" % self.member_type - - def has_kobject(self): - if self.member_type not in type_env: - return False - - return type_env[self.member_type].has_kobject() - - def get_kobjects(self, addr): - mt = type_env[self.member_type] - - # Stacks are arrays of _k_stack_element_t but we want to treat - # the whole array as one kernel object (a thread stack) - # Data value gets set to size of entire region - if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: - # An array of stacks appears as a multi-dimensional array. - # The last size is the size of each stack. We need to track - # each stack within the array, not as one huge stack object. - *dimensions, stacksize = self.elements - num_members = 1 - for e in dimensions: - num_members = num_members * e - - ret = {} - for i in range(num_members): - a = addr + (i * stacksize) - o = mt.get_kobjects(a) - o[a].data = stacksize - ret.update(o) - return ret - - objs = {} - - # Multidimensional array flattened out - num_members = 1 - for e in self.elements: - num_members = num_members * e - - for i in range(num_members): - objs.update(mt.get_kobjects(addr + (i * mt.size))) - return objs - - -class AggregateTypeMember: - def __init__(self, offset, member_name, member_type, member_offset): - self.member_name = member_name - self.member_type = member_type - if isinstance(member_offset, list): - # DWARF v2, location encoded as set of operations - # only "DW_OP_plus_uconst" with ULEB128 argument supported - if member_offset[0] == 0x23: - self.member_offset = member_offset[1] & 0x7f - for i in range(1, len(member_offset)-1): - if member_offset[i] & 0x80: - self.member_offset += ( - member_offset[i+1] & 0x7f) << i*7 - else: - raise Exception("not yet supported location operation (%s:%d:%d)" % - (self.member_name, self.member_type, member_offset[0])) - else: - self.member_offset = member_offset - - def __repr__(self): - return "" % ( - self.member_name, self.member_type, self.member_offset) - - def has_kobject(self): - if self.member_type not in type_env: - return False - - return type_env[self.member_type].has_kobject() - - def get_kobjects(self, addr): - mt = type_env[self.member_type] - return mt.get_kobjects(addr + self.member_offset) - - -class ConstType: - def __init__(self, child_type): - self.child_type = child_type - - def __repr__(self): - return "" % self.child_type - - def has_kobject(self): - if self.child_type not in type_env: - return False - - return type_env[self.child_type].has_kobject() - - def get_kobjects(self, addr): - return type_env[self.child_type].get_kobjects(addr) - - -class AggregateType: - def __init__(self, offset, name, size): - self.name = name - self.size = size - self.offset = offset - self.members = [] - - def add_member(self, member): - self.members.append(member) - - def __repr__(self): - return "" % (self.name, self.members) - - def has_kobject(self): - result = False - - bad_members = [] - - for member in self.members: - if member.has_kobject(): - result = True - else: - bad_members.append(member) - # Don't need to consider this again, just remove it - - for bad_member in bad_members: - self.members.remove(bad_member) - - return result - - def get_kobjects(self, addr): - objs = {} - for member in self.members: - objs.update(member.get_kobjects(addr)) - return objs - - -# --- helper functions for getting data from DIEs --- - -def die_get_spec(die): - if 'DW_AT_specification' not in die.attributes: - return None - - spec_val = die.attributes["DW_AT_specification"].value - - # offset of the DW_TAG_variable for the extern declaration - offset = spec_val + die.cu.cu_offset - - return extern_env.get(offset) - - -def die_get_name(die): - if 'DW_AT_name' not in die.attributes: - die = die_get_spec(die) - if not die: - return None - - return die.attributes["DW_AT_name"].value.decode("utf-8") - - -def die_get_type_offset(die): - if 'DW_AT_type' not in die.attributes: - die = die_get_spec(die) - if not die: - return None - - return die.attributes["DW_AT_type"].value + die.cu.cu_offset - - -def die_get_byte_size(die): - if 'DW_AT_byte_size' not in die.attributes: - return 0 - - return die.attributes["DW_AT_byte_size"].value - - -def analyze_die_struct(die): - name = die_get_name(die) or "" - offset = die.offset - size = die_get_byte_size(die) - - # Incomplete type - if not size: - return - - if name in kobjects: - type_env[offset] = KobjectType(offset, name, size) - elif name in subsystems: - type_env[offset] = KobjectType(offset, name, size, api=True) - else: - at = AggregateType(offset, name, size) - type_env[offset] = at - - for child in die.iter_children(): - if child.tag != "DW_TAG_member": - continue - data_member_location = child.attributes.get("DW_AT_data_member_location") - if not data_member_location: - continue - - child_type = die_get_type_offset(child) - member_offset = data_member_location.value - cname = die_get_name(child) or "" - m = AggregateTypeMember(child.offset, cname, child_type, - member_offset) - at.add_member(m) - - return - - -def analyze_die_const(die): - type_offset = die_get_type_offset(die) - if not type_offset: - return - - type_env[die.offset] = ConstType(type_offset) - - -def analyze_die_array(die): - type_offset = die_get_type_offset(die) - elements = [] - - for child in die.iter_children(): - if child.tag != "DW_TAG_subrange_type": - continue - if "DW_AT_upper_bound" not in child.attributes: - continue - - ub = child.attributes["DW_AT_upper_bound"] - if not ub.form.startswith("DW_FORM_data"): - continue - - elements.append(ub.value + 1) - - if not elements: - if type_offset in type_env.keys(): - mt = type_env[type_offset] - if mt.has_kobject(): - if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: - elements.append(1) - type_env[die.offset] = ArrayType(die.offset, elements, type_offset) - else: - type_env[die.offset] = ArrayType(die.offset, elements, type_offset) - - -def analyze_typedef(die): - type_offset = die_get_type_offset(die) - - if type_offset not in type_env.keys(): - return - - type_env[die.offset] = type_env[type_offset] - - -def unpack_pointer(elf, data, offset): - endian_code = "<" if elf.little_endian else ">" - if elf.elfclass == 32: - size_code = "I" - size = 4 - else: - size_code = "Q" - size = 8 - - return struct.unpack(endian_code + size_code, - data[offset:offset + size])[0] - - -def addr_deref(elf, addr): - for section in elf.iter_sections(): - start = section['sh_addr'] - end = start + section['sh_size'] - - if start <= addr < end: - data = section.data() - offset = addr - start - return unpack_pointer(elf, data, offset) - - return 0 - - -def device_get_api_addr(elf, addr): - # Read device->driver API - offset = 4 if elf.elfclass == 32 else 8 - return addr_deref(elf, addr + offset) - - -def get_filename_lineno(die): - lp_header = die.dwarfinfo.line_program_for_CU(die.cu).header - files = lp_header["file_entry"] - includes = lp_header["include_directory"] - - fileinfo = files[die.attributes["DW_AT_decl_file"].value - 1] - filename = fileinfo.name.decode("utf-8") - filedir = includes[fileinfo.dir_index - 1].decode("utf-8") - - path = os.path.join(filedir, filename) - lineno = die.attributes["DW_AT_decl_line"].value - return (path, lineno) - - -class ElfHelper: - - def __init__(self, filename, verbose, kobjs, subs): - self.verbose = verbose - self.fp = open(filename, "rb") - self.elf = ELFFile(self.fp) - self.little_endian = self.elf.little_endian - global kobjects - global subsystems - kobjects = kobjs - subsystems = subs - - def find_kobjects(self, syms): - if not self.elf.has_dwarf_info(): - sys.exit("ELF file has no DWARF information") - - app_smem_start = syms["_app_smem_start"] - app_smem_end = syms["_app_smem_end"] - - di = self.elf.get_dwarf_info() - - variables = [] - - # Step 1: collect all type information. - for CU in di.iter_CUs(): - for die in CU.iter_DIEs(): - # Unions are disregarded, kernel objects should never be union - # members since the memory is not dedicated to that object and - # could be something else - if die.tag == "DW_TAG_structure_type": - analyze_die_struct(die) - elif die.tag == "DW_TAG_const_type": - analyze_die_const(die) - elif die.tag == "DW_TAG_array_type": - analyze_die_array(die) - elif die.tag == "DW_TAG_typedef": - analyze_typedef(die) - elif die.tag == "DW_TAG_variable": - variables.append(die) - - # Step 2: filter type_env to only contain kernel objects, or structs - # and arrays of kernel objects - bad_offsets = [] - for offset, type_object in type_env.items(): - if not type_object.has_kobject(): - bad_offsets.append(offset) - - for offset in bad_offsets: - del type_env[offset] - - # Step 3: Now that we know all the types we are looking for, examine - # all variables - all_objs = {} - - for die in variables: - name = die_get_name(die) - if not name: - continue - - if name.startswith("__device_sys_init"): - # Boot-time initialization function; not an actual device - continue - - type_offset = die_get_type_offset(die) - - # Is this a kernel object, or a structure containing kernel - # objects? - if type_offset not in type_env: - continue - - if "DW_AT_declaration" in die.attributes: - # Extern declaration, only used indirectly - extern_env[die.offset] = die - continue - - if "DW_AT_location" not in die.attributes: - self.debug_die( - die, - "No location information for object '%s'; possibly" - " stack allocated" % name) - continue - - loc = die.attributes["DW_AT_location"] - if loc.form != "DW_FORM_exprloc" and \ - loc.form != "DW_FORM_block1": - self.debug_die( - die, - "kernel object '%s' unexpected location format" % - name) - continue - - opcode = loc.value[0] - if opcode != DW_OP_addr: - - # Check if frame pointer offset DW_OP_fbreg - if opcode == DW_OP_fbreg: - self.debug_die(die, "kernel object '%s' found on stack" % - name) - else: - self.debug_die( - die, - "kernel object '%s' unexpected exprloc opcode %s" % - (name, hex(opcode))) - continue - - addr = (loc.value[1] | (loc.value[2] << 8) | - (loc.value[3] << 16) | (loc.value[4] << 24)) - - if addr == 0: - # Never linked; gc-sections deleted it - continue - - type_obj = type_env[type_offset] - objs = type_obj.get_kobjects(addr) - all_objs.update(objs) - - self.debug("symbol '%s' at %s contains %d object(s)" - % (name, hex(addr), len(objs))) - - # Step 4: objs is a dictionary mapping variable memory addresses to - # their associated type objects. Now that we have seen all variables - # and can properly look up API structs, convert this into a dictionary - # mapping variables to the C enumeration of what kernel object type it - # is. - ret = {} - for addr, ko in all_objs.items(): - # API structs don't get into the gperf table - if ko.type_obj.api: - continue - - _, user_ram_allowed = kobjects[ko.type_obj.name] - if not user_ram_allowed and app_smem_start <= addr < app_smem_end: - self.debug_die(die, - "object '%s' found in invalid location %s" - % (name, hex(addr))) - continue - - if ko.type_obj.name != "device": - # Not a device struct so we immediately know its type - ko.type_name = kobject_to_enum(ko.type_obj.name) - ret[addr] = ko - continue - - # Device struct. Need to get the address of its API struct, - # if it has one. - apiaddr = device_get_api_addr(self.elf, addr) - if apiaddr not in all_objs: - if apiaddr == 0: - self.debug("device instance at 0x%x has no associated subsystem" - % addr) - else: - self.debug("device instance at 0x%x has unknown API 0x%x" - % (addr, apiaddr)) - # API struct does not correspond to a known subsystem, skip it - continue - - apiobj = all_objs[apiaddr] - ko.type_name = subsystem_to_enum(apiobj.type_obj.name) - ret[addr] = ko - - self.debug("found %d kernel object instances total" % len(ret)) - - # 1. Before python 3.7 dict order is not guaranteed. With Python - # 3.5 it doesn't seem random with *integer* keys but can't - # rely on that. - # 2. OrderedDict means _insertion_ order, so not enough because - # built from other (random!) dicts: need to _sort_ first. - # 3. Sorting memory address looks good. - return OrderedDict(sorted(ret.items())) - - def get_symbols(self): - for section in self.elf.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 debug(self, text): - if not self.verbose: - return - sys.stdout.write(scr + ": " + text + "\n") - - @staticmethod - def error(text): - sys.exit("%s ERROR: %s" % (scr, text)) - - def debug_die(self, die, text): - fn, ln = get_filename_lineno(die) - - self.debug(str(die)) - self.debug("File '%s', line %d:" % (fn, ln)) - self.debug(" %s" % text) - - @staticmethod - def get_thread_counter(): - return thread_counter - - @staticmethod - def get_sys_mutex_counter(): - return sys_mutex_counter - - @staticmethod - def get_futex_counter(): - return futex_counter diff --git a/scripts/gen_kobject_list.py b/scripts/gen_kobject_list.py index e565835844a8b..94fe665282d84 100755 --- a/scripts/gen_kobject_list.py +++ b/scripts/gen_kobject_list.py @@ -57,7 +57,14 @@ import os import struct import json -from elf_helper import ElfHelper, kobject_to_enum +from distutils.version import LooseVersion + +import elftools +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection + +if LooseVersion(elftools.__version__) < LooseVersion('0.24'): + sys.exit("pyelftools is out of date, need version 0.24 or later") from collections import OrderedDict @@ -85,12 +92,20 @@ ("k_stack", (None, False)), ("k_thread", (None, False)), ("k_timer", (None, False)), - ("_k_thread_stack_element", (None, False)), + ("z_thread_stack_element", (None, False)), ("device", (None, False)), ("sys_mutex", (None, True)), ("k_futex", (None, True)) ]) +def kobject_to_enum(kobj): + if kobj.startswith("k_") or kobj.startswith("z_"): + name = kobj[2:] + else: + name = kobj + + return "K_OBJ_%s" % name.upper() + subsystems = [ # Editing the list is deprecated, add the __subsystem sentinal to your driver # api declaration instead. e.x. @@ -100,6 +115,550 @@ #}; ] +def subsystem_to_enum(subsys): + return "K_OBJ_DRIVER_" + subsys[:-11].upper() + +# --- debug stuff --- + +scr = os.path.basename(sys.argv[0]) + +def debug(text): + if not args.verbose: + return + sys.stdout.write(scr + ": " + text + "\n") + +def error(text): + sys.exit("%s ERROR: %s" % (scr, text)) + +def debug_die(die, text): + lp_header = die.dwarfinfo.line_program_for_CU(die.cu).header + files = lp_header["file_entry"] + includes = lp_header["include_directory"] + + fileinfo = files[die.attributes["DW_AT_decl_file"].value - 1] + filename = fileinfo.name.decode("utf-8") + filedir = includes[fileinfo.dir_index - 1].decode("utf-8") + + path = os.path.join(filedir, filename) + lineno = die.attributes["DW_AT_decl_line"].value + + debug(str(die)) + debug("File '%s', line %d:" % (path, lineno)) + debug(" %s" % text) + +# -- ELF processing + +DW_OP_addr = 0x3 +DW_OP_fbreg = 0x91 +STACK_TYPE = "z_thread_stack_element" +thread_counter = 0 +sys_mutex_counter = 0 +futex_counter = 0 +stack_counter = 0 + +# Global type environment. Populated by pass 1. +type_env = {} +extern_env = {} + +class KobjectInstance: + def __init__(self, type_obj, addr): + global thread_counter + global sys_mutex_counter + global futex_counter + global stack_counter + + self.addr = addr + self.type_obj = type_obj + + # Type name determined later since drivers needs to look at the + # API struct address + self.type_name = None + + if self.type_obj.name == "k_thread": + # Assign an ID for this thread object, used to track its + # permissions to other kernel objects + self.data = thread_counter + thread_counter = thread_counter + 1 + elif self.type_obj.name == "sys_mutex": + self.data = "&kernel_mutexes[%d]" % sys_mutex_counter + sys_mutex_counter += 1 + elif self.type_obj.name == "k_futex": + self.data = "&futex_data[%d]" % futex_counter + futex_counter += 1 + elif self.type_obj.name == STACK_TYPE: + stack_counter += 1 + else: + self.data = 0 + + +class KobjectType: + def __init__(self, offset, name, size, api=False): + self.name = name + self.size = size + self.offset = offset + self.api = api + + def __repr__(self): + return "" % self.name + + @staticmethod + def has_kobject(): + return True + + def get_kobjects(self, addr): + return {addr: KobjectInstance(self, addr)} + + +class ArrayType: + def __init__(self, offset, elements, member_type): + self.elements = elements + self.member_type = member_type + self.offset = offset + + def __repr__(self): + return "" % self.member_type + + def has_kobject(self): + if self.member_type not in type_env: + return False + + return type_env[self.member_type].has_kobject() + + def get_kobjects(self, addr): + mt = type_env[self.member_type] + + # Stacks are arrays of _k_stack_element_t but we want to treat + # the whole array as one kernel object (a thread stack) + # Data value gets set to size of entire region + if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: + # An array of stacks appears as a multi-dimensional array. + # The last size is the size of each stack. We need to track + # each stack within the array, not as one huge stack object. + *dimensions, stacksize = self.elements + num_members = 1 + for e in dimensions: + num_members = num_members * e + + ret = {} + for i in range(num_members): + a = addr + (i * stacksize) + o = mt.get_kobjects(a) + o[a].data = stacksize + ret.update(o) + return ret + + objs = {} + + # Multidimensional array flattened out + num_members = 1 + for e in self.elements: + num_members = num_members * e + + for i in range(num_members): + objs.update(mt.get_kobjects(addr + (i * mt.size))) + return objs + + +class AggregateTypeMember: + def __init__(self, offset, member_name, member_type, member_offset): + self.member_name = member_name + self.member_type = member_type + if isinstance(member_offset, list): + # DWARF v2, location encoded as set of operations + # only "DW_OP_plus_uconst" with ULEB128 argument supported + if member_offset[0] == 0x23: + self.member_offset = member_offset[1] & 0x7f + for i in range(1, len(member_offset)-1): + if member_offset[i] & 0x80: + self.member_offset += ( + member_offset[i+1] & 0x7f) << i*7 + else: + raise Exception("not yet supported location operation (%s:%d:%d)" % + (self.member_name, self.member_type, member_offset[0])) + else: + self.member_offset = member_offset + + def __repr__(self): + return "" % ( + self.member_name, self.member_type, self.member_offset) + + def has_kobject(self): + if self.member_type not in type_env: + return False + + return type_env[self.member_type].has_kobject() + + def get_kobjects(self, addr): + mt = type_env[self.member_type] + return mt.get_kobjects(addr + self.member_offset) + + +class ConstType: + def __init__(self, child_type): + self.child_type = child_type + + def __repr__(self): + return "" % self.child_type + + def has_kobject(self): + if self.child_type not in type_env: + return False + + return type_env[self.child_type].has_kobject() + + def get_kobjects(self, addr): + return type_env[self.child_type].get_kobjects(addr) + + +class AggregateType: + def __init__(self, offset, name, size): + self.name = name + self.size = size + self.offset = offset + self.members = [] + + def add_member(self, member): + self.members.append(member) + + def __repr__(self): + return "" % (self.name, self.members) + + def has_kobject(self): + result = False + + bad_members = [] + + for member in self.members: + if member.has_kobject(): + result = True + else: + bad_members.append(member) + # Don't need to consider this again, just remove it + + for bad_member in bad_members: + self.members.remove(bad_member) + + return result + + def get_kobjects(self, addr): + objs = {} + for member in self.members: + objs.update(member.get_kobjects(addr)) + return objs + + +# --- helper functions for getting data from DIEs --- + +def die_get_spec(die): + if 'DW_AT_specification' not in die.attributes: + return None + + spec_val = die.attributes["DW_AT_specification"].value + + # offset of the DW_TAG_variable for the extern declaration + offset = spec_val + die.cu.cu_offset + + return extern_env.get(offset) + + +def die_get_name(die): + if 'DW_AT_name' not in die.attributes: + die = die_get_spec(die) + if not die: + return None + + return die.attributes["DW_AT_name"].value.decode("utf-8") + + +def die_get_type_offset(die): + if 'DW_AT_type' not in die.attributes: + die = die_get_spec(die) + if not die: + return None + + return die.attributes["DW_AT_type"].value + die.cu.cu_offset + + +def die_get_byte_size(die): + if 'DW_AT_byte_size' not in die.attributes: + return 0 + + return die.attributes["DW_AT_byte_size"].value + + +def analyze_die_struct(die): + name = die_get_name(die) or "" + offset = die.offset + size = die_get_byte_size(die) + + # Incomplete type + if not size: + return + + if name in kobjects: + type_env[offset] = KobjectType(offset, name, size) + elif name in subsystems: + type_env[offset] = KobjectType(offset, name, size, api=True) + else: + at = AggregateType(offset, name, size) + type_env[offset] = at + + for child in die.iter_children(): + if child.tag != "DW_TAG_member": + continue + data_member_location = child.attributes.get("DW_AT_data_member_location") + if not data_member_location: + continue + + child_type = die_get_type_offset(child) + member_offset = data_member_location.value + cname = die_get_name(child) or "" + m = AggregateTypeMember(child.offset, cname, child_type, + member_offset) + at.add_member(m) + + return + + +def analyze_die_const(die): + type_offset = die_get_type_offset(die) + if not type_offset: + return + + type_env[die.offset] = ConstType(type_offset) + + +def analyze_die_array(die): + type_offset = die_get_type_offset(die) + elements = [] + + for child in die.iter_children(): + if child.tag != "DW_TAG_subrange_type": + continue + if "DW_AT_upper_bound" not in child.attributes: + continue + + ub = child.attributes["DW_AT_upper_bound"] + if not ub.form.startswith("DW_FORM_data"): + continue + + elements.append(ub.value + 1) + + if not elements: + if type_offset in type_env.keys(): + mt = type_env[type_offset] + if mt.has_kobject(): + if isinstance(mt, KobjectType) and mt.name == STACK_TYPE: + elements.append(1) + type_env[die.offset] = ArrayType(die.offset, elements, type_offset) + else: + type_env[die.offset] = ArrayType(die.offset, elements, type_offset) + + +def analyze_typedef(die): + type_offset = die_get_type_offset(die) + + if type_offset not in type_env.keys(): + return + + type_env[die.offset] = type_env[type_offset] + + +def unpack_pointer(elf, data, offset): + endian_code = "<" if elf.little_endian else ">" + if elf.elfclass == 32: + size_code = "I" + size = 4 + else: + size_code = "Q" + size = 8 + + return struct.unpack(endian_code + size_code, + data[offset:offset + size])[0] + + +def addr_deref(elf, addr): + for section in elf.iter_sections(): + start = section['sh_addr'] + end = start + section['sh_size'] + + if start <= addr < end: + data = section.data() + offset = addr - start + return unpack_pointer(elf, data, offset) + + return 0 + + +def device_get_api_addr(elf, addr): + # Read device->driver API + offset = 4 if elf.elfclass == 32 else 8 + return addr_deref(elf, addr + offset) + + +def find_kobjects(elf, syms): + if not elf.has_dwarf_info(): + sys.exit("ELF file has no DWARF information") + + app_smem_start = syms["_app_smem_start"] + app_smem_end = syms["_app_smem_end"] + + di = elf.get_dwarf_info() + + variables = [] + + # Step 1: collect all type information. + for CU in di.iter_CUs(): + for die in CU.iter_DIEs(): + # Unions are disregarded, kernel objects should never be union + # members since the memory is not dedicated to that object and + # could be something else + if die.tag == "DW_TAG_structure_type": + analyze_die_struct(die) + elif die.tag == "DW_TAG_const_type": + analyze_die_const(die) + elif die.tag == "DW_TAG_array_type": + analyze_die_array(die) + elif die.tag == "DW_TAG_typedef": + analyze_typedef(die) + elif die.tag == "DW_TAG_variable": + variables.append(die) + + # Step 2: filter type_env to only contain kernel objects, or structs + # and arrays of kernel objects + bad_offsets = [] + for offset, type_object in type_env.items(): + if not type_object.has_kobject(): + bad_offsets.append(offset) + + for offset in bad_offsets: + del type_env[offset] + + # Step 3: Now that we know all the types we are looking for, examine + # all variables + all_objs = {} + + for die in variables: + name = die_get_name(die) + if not name: + continue + + if name.startswith("__device_sys_init"): + # Boot-time initialization function; not an actual device + continue + + type_offset = die_get_type_offset(die) + + # Is this a kernel object, or a structure containing kernel + # objects? + if type_offset not in type_env: + continue + + if "DW_AT_declaration" in die.attributes: + # Extern declaration, only used indirectly + extern_env[die.offset] = die + continue + + if "DW_AT_location" not in die.attributes: + debug_die(die, + "No location information for object '%s'; possibly stack allocated" + % name) + continue + + loc = die.attributes["DW_AT_location"] + if loc.form != "DW_FORM_exprloc" and \ + loc.form != "DW_FORM_block1": + debug_die(die, "kernel object '%s' unexpected location format" % + name) + continue + + opcode = loc.value[0] + if opcode != DW_OP_addr: + + # Check if frame pointer offset DW_OP_fbreg + if opcode == DW_OP_fbreg: + debug_die(die, "kernel object '%s' found on stack" % name) + else: + debug_die(die, + "kernel object '%s' unexpected exprloc opcode %s" % + (name, hex(opcode))) + continue + + addr = (loc.value[1] | (loc.value[2] << 8) | + (loc.value[3] << 16) | (loc.value[4] << 24)) + + if addr == 0: + # Never linked; gc-sections deleted it + continue + + type_obj = type_env[type_offset] + objs = type_obj.get_kobjects(addr) + all_objs.update(objs) + + debug("symbol '%s' at %s contains %d object(s)" + % (name, hex(addr), len(objs))) + + # Step 4: objs is a dictionary mapping variable memory addresses to + # their associated type objects. Now that we have seen all variables + # and can properly look up API structs, convert this into a dictionary + # mapping variables to the C enumeration of what kernel object type it + # is. + ret = {} + for addr, ko in all_objs.items(): + # API structs don't get into the gperf table + if ko.type_obj.api: + continue + + _, user_ram_allowed = kobjects[ko.type_obj.name] + if not user_ram_allowed and app_smem_start <= addr < app_smem_end: + debug_die(die, "object '%s' found in invalid location %s" + % (name, hex(addr))) + continue + + if ko.type_obj.name != "device": + # Not a device struct so we immediately know its type + ko.type_name = kobject_to_enum(ko.type_obj.name) + ret[addr] = ko + continue + + # Device struct. Need to get the address of its API struct, + # if it has one. + apiaddr = device_get_api_addr(elf, addr) + if apiaddr not in all_objs: + if apiaddr == 0: + debug("device instance at 0x%x has no associated subsystem" + % addr) + else: + debug("device instance at 0x%x has unknown API 0x%x" + % (addr, apiaddr)) + # API struct does not correspond to a known subsystem, skip it + continue + + apiobj = all_objs[apiaddr] + ko.type_name = subsystem_to_enum(apiobj.type_obj.name) + ret[addr] = ko + + debug("found %d kernel object instances total" % len(ret)) + + # 1. Before python 3.7 dict order is not guaranteed. With Python + # 3.5 it doesn't seem random with *integer* keys but can't + # rely on that. + # 2. OrderedDict means _insertion_ order, so not enough because + # built from other (random!) dicts: need to _sort_ first. + # 3. Sorting memory address looks good. + return OrderedDict(sorted(ret.items())) + +def get_symbols(elf): + for section in elf.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") + + +# -- GPERF generation logic + header = """%compare-lengths %define lookup-function-name z_object_lookup %language=ANSI-C @@ -111,7 +670,7 @@ #include #include %} -struct _k_object; +struct z_object; """ # Different versions of gperf have different prototypes for the lookup @@ -119,7 +678,7 @@ # turned into a string, we told gperf to expect binary strings that are not # NULL-terminated. footer = """%% -struct _k_object *z_object_gperf_find(void *obj) +struct z_object *z_object_gperf_find(void *obj) { return z_object_lookup((const char *)obj, sizeof(void *)); } @@ -136,7 +695,7 @@ } #ifndef CONFIG_DYNAMIC_OBJECTS -struct _k_object *z_object_find(void *obj) +struct z_object *z_object_find(void *obj) ALIAS_OF(z_object_gperf_find); void z_object_wordlist_foreach(_wordlist_cb_func_t func, void *context) @@ -145,29 +704,65 @@ """ -def write_gperf_table(fp, eh, objs, static_begin, static_end): +def write_gperf_table(fp, syms, objs, little_endian, static_begin, static_end): fp.write(header) - num_mutexes = eh.get_sys_mutex_counter() - if num_mutexes != 0: - fp.write("static struct k_mutex kernel_mutexes[%d] = {\n" % num_mutexes) - for i in range(num_mutexes): + if sys_mutex_counter != 0: + fp.write("static struct k_mutex kernel_mutexes[%d] = {\n" + % sys_mutex_counter) + for i in range(sys_mutex_counter): fp.write("_K_MUTEX_INITIALIZER(kernel_mutexes[%d])" % i) - if i != num_mutexes - 1: + if i != sys_mutex_counter - 1: fp.write(", ") fp.write("};\n") - num_futex = eh.get_futex_counter() - if num_futex != 0: - fp.write("static struct z_futex_data futex_data[%d] = {\n" % num_futex) - for i in range(num_futex): + if futex_counter != 0: + fp.write("static struct z_futex_data futex_data[%d] = {\n" + % futex_counter) + for i in range(futex_counter): fp.write("Z_FUTEX_DATA_INITIALIZER(futex_data[%d])" % i) - if i != num_futex - 1: + if i != futex_counter - 1: fp.write(", ") fp.write("};\n") + metadata_names = { + "K_OBJ_THREAD" : "thread_id", + "K_OBJ_SYS_MUTEX" : "mutex", + "K_OBJ_FUTEX" : "futex_data" + } + + if "CONFIG_GEN_PRIV_STACKS" in syms: + metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_data" + if stack_counter != 0: + fp.write("static u8_t Z_GENERIC_SECTION(.priv_stacks.noinit) " + " __aligned(Z_PRIVILEGE_STACK_ALIGN)" + " priv_stacks[%d][CONFIG_PRIVILEGED_STACK_SIZE];\n" + % stack_counter) + + fp.write("static struct z_stack_data stack_data[%d] = {\n" + % stack_counter) + counter = 0 + for _, ko in objs.items(): + if ko.type_name != "K_OBJ_THREAD_STACK_ELEMENT": + continue + + # ko.data currently has the stack size. fetch the value to + # populate the appropriate entry in stack_data, and put + # a reference to the entry in stack_data into the data value + # instead + size = ko.data + ko.data = "&stack_data[%d]" % counter + fp.write("\t{ %d, (u8_t *)(&priv_stacks[%d]) }" + % (size, counter)) + if counter != (stack_counter - 1): + fp.write(",") + fp.write("\n") + counter += 1 + fp.write("};\n") + else: + metadata_names["K_OBJ_THREAD_STACK_ELEMENT"] = "stack_size" + fp.write("%%\n") # Setup variables for mapping thread indexes - syms = eh.get_symbols() thread_max_bytes = syms["CONFIG_MAX_THREAD_BYTES"] thread_idx_map = {} @@ -187,7 +782,7 @@ def write_gperf_table(fp, eh, objs, static_begin, static_end): else: format_code = "I" - if eh.little_endian: + if little_endian: endian = "<" else: endian = ">" @@ -204,7 +799,13 @@ def write_gperf_table(fp, eh, objs, static_begin, static_end): if is_driver: flags += " | K_OBJ_FLAG_DRIVER" - fp.write("\", {}, %s, %s, %s\n" % (obj_type, flags, str(ko.data))) + if ko.type_name in metadata_names: + tname = metadata_names[ko.type_name] + else: + tname = "unused" + + fp.write("\", {}, %s, %s, { .%s = %s }\n" % (obj_type, flags, + tname, str(ko.data))) if obj_type == "K_OBJ_THREAD": idx = math.floor(ko.data / 8) @@ -300,7 +901,7 @@ def write_kobj_size_output(fp): dep, _ = obj_info # device handled by default case. Stacks are not currently handled, # if they eventually are it will be a special case. - if kobj in {"device", "_k_thread_stack_element"}: + if kobj in {"device", STACK_TYPE}: continue if dep: @@ -362,22 +963,21 @@ def main(): if args.gperf_output: assert args.kernel, "--kernel ELF required for --gperf-output" - eh = ElfHelper(args.kernel, args.verbose, kobjects, subsystems) - syms = eh.get_symbols() + elf = ELFFile(open(args.kernel, "rb")) + syms = get_symbols(elf) max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8 - objs = eh.find_kobjects(syms) + objs = find_kobjects(elf, syms) if not objs: sys.stderr.write("WARNING: zero kobject found in %s\n" % args.kernel) - thread_counter = eh.get_thread_counter() if thread_counter > max_threads: sys.exit("Too many thread objects ({})\n" "Increase CONFIG_MAX_THREAD_BYTES to {}" .format(thread_counter, -(-thread_counter // 8))) with open(args.gperf_output, "w") as fp: - write_gperf_table(fp, eh, objs, + write_gperf_table(fp, syms, objs, elf.little_endian, syms["_static_kernel_objects_begin"], syms["_static_kernel_objects_end"]) diff --git a/scripts/gen_priv_stacks.py b/scripts/gen_priv_stacks.py deleted file mode 100755 index 9db974f709df3..0000000000000 --- a/scripts/gen_priv_stacks.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2017 Linaro Limited -# -# SPDX-License-Identifier: Apache-2.0 - -""" -Script to generate gperf tables mapping threads to their privileged mode stacks - -Some MPU devices require that memory region definitions be aligned to their -own size, which must be a power of two. This introduces difficulties in -reserving memory for the thread's supervisor mode stack inline with the -K_THREAD_STACK_DEFINE() macro. - -Instead, the stack used when a user thread elevates privileges is allocated -elsewhere in memory, and a gperf table is created to be able to quickly -determine where the supervisor mode stack is in memory. This is accomplished -by scanning the DWARF debug information in zephyr_prebuilt.elf, identifying -instances of 'struct k_thread', and emitting a gperf configuration file which -allocates memory for each thread's privileged stack and creates the table -mapping thread addresses to these stacks. -""" - -import sys -import argparse -import struct -from elf_helper import ElfHelper - -kobjects = { - "_k_thread_stack_element": (None, False) -} - - -header = """%compare-lengths -%define lookup-function-name z_priv_stack_map_lookup -%language=ANSI-C -%global-table -%struct-type -""" - - -# Each privilege stack buffer needs to respect the alignment -# constraints as specified in arm/arch.h. -priv_stack_decl_temp = ("static u8_t __used" - " __aligned(Z_PRIVILEGE_STACK_ALIGN)" - " priv_stack_%x[CONFIG_PRIVILEGED_STACK_SIZE];\n") - - -includes = """#include -#include -""" - - -structure = """struct _k_priv_stack_map { - char *name; - u8_t *priv_stack_addr; -}; -%% -""" - - -# Different versions of gperf have different prototypes for the lookup -# function, best to implement the wrapper here. The pointer value itself is -# turned into a string, we told gperf to expect binary strings that are not -# NULL-terminated. -footer = """%% -u8_t *z_priv_stack_find(void *obj) -{ - const struct _k_priv_stack_map *map = - z_priv_stack_map_lookup((const char *)obj, sizeof(void *)); - return map->priv_stack_addr; -} -""" - - -def write_gperf_table(fp, eh, objs): - fp.write(header) - - # priv stack declarations - fp.write("%{\n") - fp.write(includes) - for obj_addr in objs: - fp.write(priv_stack_decl_temp % (obj_addr)) - fp.write("%}\n") - - # structure declaration - fp.write(structure) - - for obj_addr in objs: - byte_str = struct.pack("I", obj_addr) - fp.write("\"") - for byte in byte_str: - val = "\\x%02x" % byte - fp.write(val) - - fp.write("\",priv_stack_%x\n" % obj_addr) - - fp.write(footer) - - -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 list of kernel object addresses for gperf use") - parser.add_argument("-v", "--verbose", action="store_true", - help="Print extra debugging information") - args = parser.parse_args() - - -def main(): - parse_args() - - eh = ElfHelper(args.kernel, args.verbose, kobjects, []) - syms = eh.get_symbols() - max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8 - objs = eh.find_kobjects(syms) - if not objs: - sys.stderr.write("WARNING: zero kobject found in %s\n" - % args.kernel) - - thread_counter = eh.get_thread_counter() - if thread_counter > max_threads: - sys.exit("Too many thread objects ({})\n" - "Increase CONFIG_MAX_THREAD_BYTES to {}" - .format(thread_counter, -(-thread_counter // 8))) - - with open(args.output, "w") as fp: - write_gperf_table(fp, eh, objs) - - -if __name__ == "__main__": - main() diff --git a/scripts/process_gperf.py b/scripts/process_gperf.py index ac46fc7726c6e..e56a1071a9324 100755 --- a/scripts/process_gperf.py +++ b/scripts/process_gperf.py @@ -83,7 +83,7 @@ def process_line(line, fp): return # Set the lookup function to static inline so it gets rolled into - # _k_object_find(), nothing else will use it + # z_object_find(), nothing else will use it if re.search(args.pattern + " [*]$", line): fp.write("static inline " + line) return diff --git a/tests/kernel/mem_protect/userspace/src/main.c b/tests/kernel/mem_protect/userspace/src/main.c index d4b84ac7d193a..863602a5d5440 100644 --- a/tests/kernel/mem_protect/userspace/src/main.c +++ b/tests/kernel/mem_protect/userspace/src/main.c @@ -1123,7 +1123,7 @@ static struct k_sem recycle_sem; void test_object_recycle(void) { - struct _k_object *ko; + struct z_object *ko; int perms_count = 0; ko = z_object_find(&recycle_sem);