subsys/llext: Generate syscalls stubs for EDK exclusively for userspace
A new Kconfig option which generates syscall stubs assuming that extensions will always run on userspace, thus simplifying linking them, as there's no need for z_impl_ stubs (used for direct syscalls), CONFIG_LLEXT_EDK_USERSPACE_ONLY. While defining __ZEPHYR_USER__ could have the same effect for optmised builds, people building extensions on debug environments - thus non-optimised - would suffer, as they'd need to somehow make the stubs available (by either exporting the symbol or implementing dummy stubs). Signed-off-by: Ederson de Souza <ederson.desouza@intel.com>
This commit is contained in:
parent
d156a03074
commit
967168a536
4 changed files with 60 additions and 19 deletions
|
@ -623,6 +623,7 @@ get_property(LIBC_LINK_LIBRARIES TARGET zephyr_interface PROPERTY LIBC_LINK_LIBR
|
|||
zephyr_link_libraries(${LIBC_LINK_LIBRARIES})
|
||||
|
||||
set(syscall_list_h ${CMAKE_CURRENT_BINARY_DIR}/include/generated/syscall_list.h)
|
||||
set(edk_syscall_list_h ${CMAKE_CURRENT_BINARY_DIR}/edk/include/generated/syscall_list.h)
|
||||
set(syscalls_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/syscalls.json)
|
||||
set(struct_tags_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/struct_tags.json)
|
||||
|
||||
|
@ -2065,6 +2066,19 @@ endif()
|
|||
set(llext_edk_file ${PROJECT_BINARY_DIR}/${CONFIG_LLEXT_EDK_NAME}.tar.xz)
|
||||
add_custom_command(
|
||||
OUTPUT ${llext_edk_file}
|
||||
# Regenerate syscalls in case CONFIG_LLEXT_EDK_USERSPACE_ONLY
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-E make_directory edk/include/generated
|
||||
COMMAND
|
||||
${PYTHON_EXECUTABLE}
|
||||
${ZEPHYR_BASE}/scripts/build/gen_syscalls.py
|
||||
--json-file ${syscalls_json} # Read this file
|
||||
--base-output edk/include/generated/syscalls # Write to this dir
|
||||
--syscall-dispatch edk/include/generated/syscall_dispatch.c # Write this file
|
||||
--syscall-list ${edk_syscall_list_h}
|
||||
$<$<BOOL:${CONFIG_LLEXT_EDK_USERSPACE_ONLY}>:--userspace-only>
|
||||
${SYSCALL_LONG_REGISTERS_ARG}
|
||||
${SYSCALL_SPLIT_TIMEOUT_ARG}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
|
||||
-DAPPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}
|
||||
|
@ -2075,6 +2089,7 @@ add_custom_command(
|
|||
-Dllext_edk_name=${CONFIG_LLEXT_EDK_NAME}
|
||||
-DWEST_TOPDIR=${WEST_TOPDIR}
|
||||
-DZEPHYR_BASE=${ZEPHYR_BASE}
|
||||
-DCONFIG_LLEXT_EDK_USERSPACE_ONLY=${CONFIG_LLEXT_EDK_USERSPACE_ONLY}
|
||||
-P ${ZEPHYR_BASE}/cmake/llext-edk.cmake
|
||||
DEPENDS ${logical_target_for_zephyr_elf}
|
||||
COMMAND_EXPAND_LISTS
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
# - WEST_TOPDIR: Path to the west top directory.
|
||||
# - APPLICATION_SOURCE_DIR: Path to the application source directory.
|
||||
# - PROJECT_BINARY_DIR: Path to the project binary build directory.
|
||||
# - CONFIG_LLEXT_EDK_USERSPACE_ONLY: Whether to copy syscall headers from the
|
||||
# edk directory. This is necessary when building an extension that only
|
||||
# supports userspace, as the syscall headers are regenerated in the edk
|
||||
# directory.
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
|
@ -94,6 +98,11 @@ foreach(dir ${include_dirs})
|
|||
list(APPEND all_flags_cmake "-I\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}")
|
||||
endforeach()
|
||||
|
||||
if(CONFIG_LLEXT_EDK_USERSPACE_ONLY)
|
||||
# Copy syscall headers from edk directory, as they were regenerated there.
|
||||
file(COPY ${PROJECT_BINARY_DIR}/edk/include/generated/ DESTINATION ${LLEXT_EDK_INC}/zephyr/include/generated)
|
||||
endif()
|
||||
|
||||
list(JOIN all_flags_make " " all_flags_str)
|
||||
file(WRITE ${llext_edk}/Makefile.cflags "LLEXT_CFLAGS = ${all_flags_str}")
|
||||
|
||||
|
|
|
@ -205,21 +205,27 @@ def union_decl(type, split):
|
|||
middle = "struct { uintptr_t lo, hi; } split" if split else "uintptr_t x"
|
||||
return "union { %s; %s val; }" % (middle, type)
|
||||
|
||||
def wrapper_defs(func_name, func_type, args, fn):
|
||||
def wrapper_defs(func_name, func_type, args, fn, userspace_only):
|
||||
ret64 = need_split(func_type)
|
||||
mrsh_args = [] # List of rvalue expressions for the marshalled invocation
|
||||
|
||||
decl_arglist = ", ".join([" ".join(argrec) for argrec in args]) or "void"
|
||||
syscall_id = "K_SYSCALL_" + func_name.upper()
|
||||
|
||||
wrap = "extern %s z_impl_%s(%s);\n" % (func_type, func_name, decl_arglist)
|
||||
wrap += "\n"
|
||||
wrap = ''
|
||||
if not userspace_only:
|
||||
wrap += "extern %s z_impl_%s(%s);\n" % (func_type, func_name, decl_arglist)
|
||||
wrap += "\n"
|
||||
|
||||
wrap += "__pinned_func\n"
|
||||
wrap += "static inline %s %s(%s)\n" % (func_type, func_name, decl_arglist)
|
||||
wrap += "{\n"
|
||||
wrap += "#ifdef CONFIG_USERSPACE\n"
|
||||
if not userspace_only:
|
||||
wrap += "#ifdef CONFIG_USERSPACE\n"
|
||||
|
||||
wrap += ("\t" + "uint64_t ret64;\n") if ret64 else ""
|
||||
wrap += "\t" + "if (z_syscall_trap()) {\n"
|
||||
if not userspace_only:
|
||||
wrap += "\t" + "if (z_syscall_trap()) {\n"
|
||||
|
||||
valist_args = []
|
||||
for argnum, (argtype, argname) in enumerate(args):
|
||||
|
@ -267,18 +273,19 @@ def wrapper_defs(func_name, func_type, args, fn):
|
|||
for argname in valist_args:
|
||||
wrap += "\t\t" + "va_end(%s);\n" % argname
|
||||
wrap += retcode
|
||||
wrap += "\t" + "}\n"
|
||||
wrap += "#endif\n"
|
||||
if not userspace_only:
|
||||
wrap += "\t" + "}\n"
|
||||
wrap += "#endif\n"
|
||||
|
||||
# Otherwise fall through to direct invocation of the impl func.
|
||||
# Note the compiler barrier: that is required to prevent code from
|
||||
# the impl call from being hoisted above the check for user
|
||||
# context.
|
||||
impl_arglist = ", ".join([argrec[1] for argrec in args])
|
||||
impl_call = "z_impl_%s(%s)" % (func_name, impl_arglist)
|
||||
wrap += "\t" + "compiler_barrier();\n"
|
||||
wrap += "\t" + "%s%s;\n" % ("return " if func_type != "void" else "",
|
||||
impl_call)
|
||||
# Otherwise fall through to direct invocation of the impl func.
|
||||
# Note the compiler barrier: that is required to prevent code from
|
||||
# the impl call from being hoisted above the check for user
|
||||
# context.
|
||||
impl_arglist = ", ".join([argrec[1] for argrec in args])
|
||||
impl_call = "z_impl_%s(%s)" % (func_name, impl_arglist)
|
||||
wrap += "\t" + "compiler_barrier();\n"
|
||||
wrap += "\t" + "%s%s;\n" % ("return " if func_type != "void" else "",
|
||||
impl_call)
|
||||
|
||||
wrap += "}\n"
|
||||
|
||||
|
@ -377,7 +384,7 @@ def marshall_defs(func_name, func_type, args):
|
|||
|
||||
return mrsh, mrsh_name
|
||||
|
||||
def analyze_fn(match_group, fn):
|
||||
def analyze_fn(match_group, fn, userspace_only):
|
||||
func, args = match_group
|
||||
|
||||
try:
|
||||
|
@ -395,7 +402,7 @@ def analyze_fn(match_group, fn):
|
|||
|
||||
marshaller = None
|
||||
marshaller, handler = marshall_defs(func_name, func_type, args)
|
||||
invocation = wrapper_defs(func_name, func_type, args, fn)
|
||||
invocation = wrapper_defs(func_name, func_type, args, fn, userspace_only)
|
||||
|
||||
# Entry in _k_syscall_table
|
||||
table_entry = "[%s] = %s" % (sys_id, handler)
|
||||
|
@ -424,6 +431,8 @@ def parse_args():
|
|||
help="Generate marshalling files (*_mrsh.c)")
|
||||
parser.add_argument("-e", "--syscall-export-llext",
|
||||
help="output C system call export for extensions")
|
||||
parser.add_argument("-u", "--userspace-only", action="store_true",
|
||||
help="Only generate the userpace path of wrappers")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
|
@ -448,7 +457,7 @@ def main():
|
|||
exported = []
|
||||
|
||||
for match_group, fn, to_emit in syscalls:
|
||||
handler, inv, mrsh, sys_id, entry = analyze_fn(match_group, fn)
|
||||
handler, inv, mrsh, sys_id, entry = analyze_fn(match_group, fn, args.userspace_only)
|
||||
|
||||
if fn not in invocations:
|
||||
invocations[fn] = []
|
||||
|
|
|
@ -83,4 +83,12 @@ config LLEXT_EDK_NAME
|
|||
stating EDK location, used on generated Makefile.cflags. For
|
||||
instance, the default name, "llext-edk", becomes LLEXT_EDK_INSTALL_DIR.
|
||||
|
||||
config LLEXT_EDK_USERSPACE_ONLY
|
||||
bool "Only generate the Userpace codepath on syscall stubs for the EDK"
|
||||
help
|
||||
Syscall stubs can contain code that verifies if running code is at user
|
||||
or kernel space and route the call accordingly. If the EDK is expected
|
||||
to be used by userspace only extensions, this option will make EDK stubs
|
||||
not contain the routing code, and only generate the userspace one.
|
||||
|
||||
endmenu
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue