From b04e779b8936b30cffc5af1081044f9c55be589c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 29 Apr 2024 10:51:56 +0200 Subject: [PATCH 1/8] llext: (cosmetic) use a macro instead of a constant 0x8000 is the manifest .text offset, use an existing macro instead of open-coding it. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 6f41222348b6..9d32023b2aa5 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -37,6 +37,14 @@ #include #include +/* + * FIXME: this definition is copied from tools/rimage/src/include/rimage/manifest.h + * which we cannot easily include here, because it also pulls in + * tools/rimage/src/include/rimage/elf.h which then conflicts with + * zephyr/include/zephyr/llext/elf.h + */ +#define FILE_TEXT_OFFSET_V1_8 0x8000 + LOG_MODULE_DECLARE(lib_manager, CONFIG_SOF_LOG_LEVEL); extern struct tr_ctx lib_manager_tr; @@ -172,11 +180,10 @@ static int llext_manager_link(struct sof_man_fw_desc *desc, struct sof_man_modul uint32_t module_id, struct module_data *md, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) { - size_t mod_size = desc->header.preload_page_count * PAGE_SZ - 0x8000; - /* FIXME: where does the module begin?? */ + size_t mod_size = desc->header.preload_page_count * PAGE_SZ - FILE_TEXT_OFFSET_V1_8; struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)desc - - SOF_MAN_ELF_TEXT_OFFSET + 0x8000, - mod_size); + SOF_MAN_ELF_TEXT_OFFSET + FILE_TEXT_OFFSET_V1_8, + mod_size); struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); /* Identify if this is the first time loading this module */ struct llext_load_param ldr_parm = {!ctx->segment_size[SOF_MAN_SEGMENT_TEXT]}; From 279387c1124bf4ef687b704f3a2c9cc63325279b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 29 Apr 2024 11:58:40 +0200 Subject: [PATCH 2/8] llext: catch only the expected exception When an ELF section isn't found .get_section_by_name() raises an AttributeError exception, catch it specitically instead of catching any exception. Signed-off-by: Guennadi Liakhovetski --- scripts/llext_link_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/llext_link_helper.py b/scripts/llext_link_helper.py index cfd3d5205fec..a739fc25ab96 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -58,7 +58,8 @@ def main(): try: offset = elf.get_section_by_name(sections[i]).header.sh_offset size = elf.get_section_by_name(sections[i]).header.sh_size - except: + except AttributeError: + print("section " + sections[i] + " not found in " + args.file) continue if last_increment == 0: From f69cabeb6cf93f9d2337d225de6cb9f9e0e11a08 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 3 Apr 2024 09:23:43 +0200 Subject: [PATCH 3/8] smart-amp: add a UUID file creation When installing modular components we need to create UUID-based soft links. Create a separate file with a list of UUIDs to assist in that. Signed-off-by: Guennadi Liakhovetski --- src/samples/audio/smart_amp_llext/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/samples/audio/smart_amp_llext/CMakeLists.txt b/src/samples/audio/smart_amp_llext/CMakeLists.txt index 2e57380bf65b..0d64174ec83d 100644 --- a/src/samples/audio/smart_amp_llext/CMakeLists.txt +++ b/src/samples/audio/smart_amp_llext/CMakeLists.txt @@ -10,6 +10,16 @@ SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE) set(MODULE "smart_amp_test") cmake_path(SET SOF_BASE NORMALIZE ${PROJECT_SOURCE_DIR}/../../../..) +file(STRINGS ${CMAKE_CURRENT_LIST_DIR}/../${MODULE}.toml uuids REGEX "^[ \t]*uuid *=") + +file(WRITE ${PROJECT_BINARY_DIR}/llext.uuid "") + +foreach(line IN LISTS uuids) + # extract UUID value - drop the 'uuid = ' part of the assignment line + string(REGEX REPLACE "^[ \t]*uuid *= \"([0-9A-F\-]*)\"" "\\1" uuid ${line}) + file(APPEND ${PROJECT_BINARY_DIR}/llext.uuid "${uuid}\n") +endforeach() + add_library(${MODULE} SHARED) target_sources(${MODULE} PRIVATE From 71f9603026ed67718b4a36c3aa86336c65346c12 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Apr 2024 09:04:45 +0200 Subject: [PATCH 4/8] module: support multi-module builds To be able to build and sign multiple modules within one build we need to unify name generation. For signing we invoke something like west sign -i smart_amp_test which means, that the west sign utility must be able to locate the module binary directory and recognise files in it using only that input file name. It will then create signed llext images inside those binary directories too, so that deployment scripts can find and recognise them there. We unify the naming as ${MODULE}_llext - for the binary directory, ${MODULE}.so - ELF file for signing in that directory ${MODULE}.llext - final signed extension llext.uuid - a file with a list of UUIDs, provided by this extension Signed-off-by: Guennadi Liakhovetski --- .../CMakeLists.txt | 12 ++++++------ .../llext.toml.h | 0 zephyr/CMakeLists.txt | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/samples/audio/{smart_amp_llext => smart_amp_test_llext}/CMakeLists.txt (89%) rename src/samples/audio/{smart_amp_llext => smart_amp_test_llext}/llext.toml.h (100%) diff --git a/src/samples/audio/smart_amp_llext/CMakeLists.txt b/src/samples/audio/smart_amp_test_llext/CMakeLists.txt similarity index 89% rename from src/samples/audio/smart_amp_llext/CMakeLists.txt rename to src/samples/audio/smart_amp_test_llext/CMakeLists.txt index 0d64174ec83d..24aa7b013cb8 100644 --- a/src/samples/audio/smart_amp_llext/CMakeLists.txt +++ b/src/samples/audio/smart_amp_test_llext/CMakeLists.txt @@ -65,12 +65,12 @@ target_compile_options(${MODULE} PRIVATE if("${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "zephyr") set(MODULE_LINKER_PARAMS -nostdlib -nodefaultlibs) -set(EXTRA_LINKED_PARAMS -shared) -set(COPY_CMD ${CMAKE_STRIP} -R .xt.* -o ${MODULE}_out.so ${MODULE}_llext.so) +set(EXTRA_LINKER_PARAMS -shared) +set(COPY_CMD ${CMAKE_STRIP} -R .xt.* -o ${MODULE}.so ${MODULE}_pre.so) else() set(MODULE_LINKER_PARAMS -nostdlib -nodefaultlibs -r) -set(EXTRA_LINKED_PARAMS) -set(COPY_CMD ${CMAKE_OBJCOPY} -R .xt.* ${MODULE}_llext.so ${MODULE}_out.so) +set(EXTRA_LINKER_PARAMS) +set(COPY_CMD ${CMAKE_OBJCOPY} -R .xt.* ${MODULE}_pre.so ${MODULE}.so) endif() target_link_options(${MODULE} PRIVATE @@ -81,8 +81,8 @@ add_custom_command(OUTPUT lib${MODULE}_out.so DEPENDS ${MODULE} COMMAND ${SOF_BASE}scripts/llext_link_helper.py -f lib${MODULE}.so -t "0xa06ca000" ${CMAKE_C_COMPILER} -- - ${MODULE_LINKER_PARAMS} ${EXTRA_LINKED_PARAMS} -fPIC - -o ${MODULE}_llext.so $ + ${MODULE_LINKER_PARAMS} ${EXTRA_LINKER_PARAMS} -fPIC + -o ${MODULE}_pre.so $ COMMAND ${COPY_CMD} COMMAND_EXPAND_LISTS ) diff --git a/src/samples/audio/smart_amp_llext/llext.toml.h b/src/samples/audio/smart_amp_test_llext/llext.toml.h similarity index 100% rename from src/samples/audio/smart_amp_llext/llext.toml.h rename to src/samples/audio/smart_amp_test_llext/llext.toml.h diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index f58176df6695..e6889be1187b 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -794,8 +794,8 @@ if(CONFIG_IPC_MAJOR_3) ) elseif(CONFIG_IPC_MAJOR_4) if(CONFIG_SAMPLE_SMART_AMP STREQUAL "m") - add_subdirectory(${SOF_SAMPLES_PATH}/audio/smart_amp_llext - ${PROJECT_BINARY_DIR}/smart_amp_llext) + add_subdirectory(${SOF_SAMPLES_PATH}/audio/smart_amp_test_llext + ${PROJECT_BINARY_DIR}/smart_amp_test_llext) add_dependencies(app smart_amp_test_llext) elseif(CONFIG_SAMPLE_SMART_AMP) zephyr_library_sources( From f79b23b9210d92b634c49e2abf48165e27bb29c9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 9 Apr 2024 16:24:12 +0300 Subject: [PATCH 5/8] smart-amp-test: add toml preprocessing when modular When building smart-amp-test as a module, we also need to preprocess its TOML configuration file. Add it to cmake. Signed-off-by: Guennadi Liakhovetski --- src/samples/audio/smart_amp_test_llext/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/samples/audio/smart_amp_test_llext/CMakeLists.txt b/src/samples/audio/smart_amp_test_llext/CMakeLists.txt index 24aa7b013cb8..265290e38a75 100644 --- a/src/samples/audio/smart_amp_test_llext/CMakeLists.txt +++ b/src/samples/audio/smart_amp_test_llext/CMakeLists.txt @@ -1,6 +1,10 @@ # Copyright (c) 2023 Intel Corporation. # SPDX-License-Identifier: Apache-2.0 +# FIXME: This *WILL* be converted to add_llext_target() as long as that's +# reasonably possible, and if it isn't, a *significant* effort will be made to +# make that possible + cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(smart_amp_test) @@ -79,6 +83,10 @@ target_link_options(${MODULE} PRIVATE add_custom_command(OUTPUT lib${MODULE}_out.so DEPENDS ${MODULE} + COMMAND ${CMAKE_C_COMPILER} -E ${CMAKE_CURRENT_LIST_DIR}/llext.toml.h -P -DREM= + -I${SOF_BASE} -I${SOF_BASE}src + -imacros ../include/generated/autoconf.h + -o rimage_config.toml COMMAND ${SOF_BASE}scripts/llext_link_helper.py -f lib${MODULE}.so -t "0xa06ca000" ${CMAKE_C_COMPILER} -- ${MODULE_LINKER_PARAMS} ${EXTRA_LINKER_PARAMS} -fPIC From ff8768751e430abcce2d9f9f8f9931e5787d20dd Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Apr 2024 14:46:54 +0200 Subject: [PATCH 6/8] smart_amp_test: use the correct loading type Set "load_type" to 2 in TOML configuration, when building for LLEXT. Signed-off-by: Guennadi Liakhovetski --- src/samples/audio/smart_amp_test.toml | 6 +++++- src/samples/audio/smart_amp_test_llext/llext.toml.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/samples/audio/smart_amp_test.toml b/src/samples/audio/smart_amp_test.toml index 3e02feac280d..5f6b53b45554 100644 --- a/src/samples/audio/smart_amp_test.toml +++ b/src/samples/audio/smart_amp_test.toml @@ -1,3 +1,7 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + REM # smart amp test module config [[module.entry]] name = "SMATEST" @@ -5,7 +9,7 @@ affinity_mask = "0x1" instance_count = "1" domain_types = "0" - load_type = "0" + load_type = LOAD_TYPE init_config = "1" module_type = "0xD" auto_start = "0" diff --git a/src/samples/audio/smart_amp_test_llext/llext.toml.h b/src/samples/audio/smart_amp_test_llext/llext.toml.h index 350cdc076e06..eb6fcfac7d24 100644 --- a/src/samples/audio/smart_amp_test_llext/llext.toml.h +++ b/src/samples/audio/smart_amp_test_llext/llext.toml.h @@ -1,4 +1,5 @@ #include +#define LOAD_TYPE "2" #include "../smart_amp_test.toml" [module] From 173e14435bd0b45f7706043dc4cb1809e7053173 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 9 Apr 2024 16:26:28 +0300 Subject: [PATCH 7/8] deployable build: install LLEXT modules into the tree Install loadable LLEXT modules into the deployment tree and create symbolic links for them. Signed-off-by: Guennadi Liakhovetski --- scripts/xtensa-build-zephyr.py | 84 ++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index aab811fabc79..2b4ab65b4e6c 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -63,6 +63,8 @@ sof_fw_version = None +signing_key = None + if py_platform.system() == "Windows": xtensa_tools_version_postfix = "-win32" elif py_platform.system() == "Linux": @@ -601,7 +603,7 @@ def clean_staging(platform): def rimage_west_configuration(platform_dict, dest_dir): """Configure rimage in a new file `dest_dir/westconfig.ini`, starting from the workspace .west/config. - Returns the pathlib.Path to the new file. + Returns a tuple (west ConfigFile, pathlib.Path to that new file). """ saved_local_var = os.environ.get('WEST_CONFIG_LOCAL') @@ -641,7 +643,7 @@ def rimage_west_configuration(platform_dict, dest_dir): platform_wconfig.set("rimage.extra-args", shlex.join(extra_args)) - return platform_west_config_path + return platform_wconfig, platform_west_config_path def build_rimage(): @@ -671,12 +673,12 @@ def rimage_options(platform_dict): example: [ (-f, 2.5.0), (-b, 1), (-k, key.pem),... ] """ + global signing_key opts = [] if args.verbose > 0: opts.append(("-v",) * args.verbose) - signing_key = None if args.key: key_path = pathlib.Path(args.key) assert key_path.exists(), f"{key_path} not found" @@ -831,10 +833,11 @@ def build_platforms(): see https://docs.zephyrproject.org/latest/guides/west/build-flash-debug.html#one-time-cmake-arguments Try "west config build.cmake-args -- ..." instead.""") - platf_build_environ['WEST_CONFIG_LOCAL'] = str(rimage_west_configuration( + platform_wcfg, wcfg_path = rimage_west_configuration( platform_dict, STAGING_DIR / "sof-info" / platform - )) + ) + platf_build_environ['WEST_CONFIG_LOCAL'] = str(wcfg_path) # Make sure the build logs don't leave anything hidden execute_command(['west', 'config', '-l'], cwd=west_top, @@ -869,7 +872,7 @@ def build_platforms(): if platform not in RI_INFO_UNSUPPORTED: reproducible_checksum(platform, west_top / platform_build_dir_name / "zephyr" / "zephyr.ri") - install_platform(platform, sof_platform_output_dir, platf_build_environ) + install_platform(platform, sof_platform_output_dir, platf_build_environ, platform_wcfg) src_dest_list = [] tools_output_dir = pathlib.Path(STAGING_DIR, "tools") @@ -902,7 +905,69 @@ def build_platforms(): symlinks=True, ignore_dangling_symlinks=True, dirs_exist_ok=True) -def install_platform(platform, sof_output_dir, platf_build_environ): +def install_lib(sof_lib_dir, abs_build_dir, platform_wconfig): + """[summary] Sign loadable llext modules, if any, copy them to the + deployment tree and create UUID links for the kernel to find and load + them.""" + + global signing_key + + with os.scandir(str(abs_build_dir)) as iter: + if args.key_type_subdir != "none": + sof_lib_dir = sof_lib_dir / args.key_type_subdir + + sof_lib_dir.mkdir(parents=True, exist_ok=True) + + for entry in iter: + if (not entry.is_dir or + not entry.name.endswith('_llext')): + continue + + entry_path = pathlib.Path(entry.path) + + uuids = entry_path / 'llext.uuid' + if not os.path.exists(uuids): + print(f"Directory {entry.name} has no llext.uuid file. Skipping.") + continue + + # replace '_llext' with '.llext', e.g. + # eq_iir_llext/eq_iir.llext + llext_base = entry.name[:-6] + llext_file = llext_base + '.llext' + + dst = sof_lib_dir / llext_file + + rimage_cfg = entry_path / 'rimage_config.toml' + llext_input = entry_path / (llext_base + '.so') + llext_output = entry_path / llext_file + + sign_cmd = [str(platform_wconfig.get("rimage.path")), "-o", str(llext_output), + "-e", "-c", str(rimage_cfg), + "-k", str(signing_key), "-l", "-r", + str(llext_input)] + execute_command(sign_cmd, cwd=west_top) + + # An intuitive way to make this multiline would be + # with (open(dst, 'wb') as fdst, open(llext_output, 'rb') as fllext, + # open(llext_output.with_suffix('.llext.xman'), 'rb') as fman): + # but a Python version, used on Windows errored out on this. + # Thus we're left with a choice between a 150-character + # long line and an illogical split like this + with open(dst, 'wb') as fdst, open(llext_output, 'rb') as fllext, open( + llext_output.with_suffix('.llext.xman'), 'rb') as fman: + # Concatenate the manifest and the llext + shutil.copyfileobj(fman, fdst) + shutil.copyfileobj(fllext, fdst) + + # Create symbolic links for all UUIDs + with open(uuids, 'r') as uuids_f: + for uuid in uuids_f: + linkname = uuid.strip() + '.bin' + symlink_or_copy(sof_lib_dir, llext_file, + sof_lib_dir, linkname) + + +def install_platform(platform, sof_output_dir, platf_build_environ, platform_wconfig): # Keep in sync with caller platform_build_dir_name = f"build-{platform}" @@ -948,6 +1013,10 @@ def install_platform(platform, sof_output_dir, platf_build_environ): for p_alias in platform_configs[platform].aliases: symlink_or_copy(install_key_dir, output_fwname, install_key_dir, f"sof-{p_alias}.ri") + if args.deployable_build and platform_configs[platform].ipc4: + install_lib(sof_output_dir / '..' / 'sof-ipc4-lib' / platform, abs_build_dir, + platform_wconfig) + # sof-info/ directory @@ -1033,7 +1102,6 @@ class InstFile: gzip_res.result() # throws exception if gzip unexpectedly failed gzip_threads.shutdown() - # Zephyr's CONFIG_KERNEL_BIN_NAME default value BIN_NAME = 'zephyr' From a71dc09d4a944579874e3f88c46943ea1bf62ecf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 3 May 2024 10:41:40 +0200 Subject: [PATCH 8/8] llext: use xtensa-build-zephyr.py in GitHub CI Switch the GitHub CI workflow to use xtensa-build-zephyr.py Signed-off-by: Guennadi Liakhovetski --- .github/workflows/llext.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/llext.yml b/.github/workflows/llext.yml index 24b48312501b..adc732a7417b 100644 --- a/.github/workflows/llext.yml +++ b/.github/workflows/llext.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [mtl, lnl] + platform: [mtl] steps: - name: git clone sof @@ -34,8 +34,10 @@ jobs: - name: llext build run: | cd workspace && ./sof/zephyr/docker-run.sh /bin/sh -c \ - 'ln -s /opt/toolchains/zephyr-sdk-* ~/; - west build --board intel_adsp_ace15_mtpm sof/app \ - -- -DEXTRA_CFLAGS=-Werror -DEXTRA_CXXFLAGS=-Werror \ - -DEXTRA_AFLAGS=-Werror \ - -DOVERLAY_CONFIG=overlays/mtl/module_overlay.conf' + "ln -s /opt/toolchains/zephyr-sdk-* ~/; + python sof/scripts/xtensa-build-zephyr.py \ + --cmake-args=-DEXTRA_CFLAGS=-Werror \ + --cmake-args=-DEXTRA_CXXFLAGS=-Werror \ + --cmake-args=-DEXTRA_AFLAGS='-Werror -Wa,--fatal-warnings' \ + --cmake-args=--warn-uninitialized \ + --overlay=sof/app/overlays/mtl/module_overlay.conf ${{ matrix.platform }}"