Browse Source

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>
pull/72896/head
Ederson de Souza 1 year ago committed by Fabio Baltieri
parent
commit
967168a536
  1. 15
      CMakeLists.txt
  2. 9
      cmake/llext-edk.cmake
  3. 49
      scripts/build/gen_syscalls.py
  4. 8
      subsys/llext/Kconfig

15
CMakeLists.txt

@ -623,6 +623,7 @@ get_property(LIBC_LINK_LIBRARIES TARGET zephyr_interface PROPERTY LIBC_LINK_LIBR @@ -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() @@ -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( @@ -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

9
cmake/llext-edk.cmake

@ -24,6 +24,10 @@ @@ -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}) @@ -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}")

49
scripts/build/gen_syscalls.py

@ -205,21 +205,27 @@ def union_decl(type, split): @@ -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): @@ -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"
# 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)
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)
wrap += "}\n"
@ -377,7 +384,7 @@ def marshall_defs(func_name, func_type, args): @@ -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): @@ -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(): @@ -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(): @@ -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] = []

8
subsys/llext/Kconfig

@ -83,4 +83,12 @@ config LLEXT_EDK_NAME @@ -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…
Cancel
Save