From 22aafaa23aece0f1324bee20de730213815c8654 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 31 Oct 2024 16:11:41 +0100 Subject: [PATCH 1/9] math: add a missing symbol export norm_int32() is called by LLEXT modules when built with gcc, export it. Signed-off-by: Guennadi Liakhovetski --- src/math/numbers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/math/numbers.c b/src/math/numbers.c index 76179b954533..e16be49e37e1 100644 --- a/src/math/numbers.c +++ b/src/math/numbers.c @@ -149,6 +149,7 @@ int norm_int32(int32_t val) return 31 - c; } +EXPORT_SYMBOL(norm_int32); #endif /* CONFIG_NORM */ From 62a97d84bab94a1c628a7ccb88b3956f41ee6b3e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 31 Oct 2024 16:13:52 +0100 Subject: [PATCH 2/9] rimage: (cosmetic) remove dangling spaces Remove multiple end-of-line spaces in comments. Signed-off-by: Guennadi Liakhovetski --- tools/rimage/src/module.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/rimage/src/module.c b/tools/rimage/src/module.c index 333d2d147e25..4c585b5b53b6 100644 --- a/tools/rimage/src/module.c +++ b/tools/rimage/src/module.c @@ -121,7 +121,7 @@ void module_print_zones(const struct module *module) /** * Print a list of valid program headers - * + * * @param module pointer to a module structure */ static void module_print_programs(const struct module *module) @@ -143,7 +143,7 @@ static void module_print_programs(const struct module *module) /** * Goes through program headers array to find the physical address based on the virtual address. - * + * * @param elf elf file structure * @param vaddr virtual address * @return physical address when success, virtual address on error @@ -173,7 +173,7 @@ unsigned long uncache_to_cache(const struct memory_alias *alias, unsigned long a /** * Checks if the section is placed in the rom memory address space - * + * * @param config Memory configuration structure * @param section section to be checked * @return true if section is placed in rom memory address space @@ -201,7 +201,7 @@ static bool section_is_rom(const struct memory_config *config, /** * Initialize module_sections_info structure - * + * * @param info Pointer to a module_sections_info structure */ static void sections_info_init(struct module_sections_info *info) @@ -213,7 +213,7 @@ static void sections_info_init(struct module_sections_info *info) /** * Adds section to module_sections_info structure - * + * * @param info Pointer to a module_sections_info structure * @param section module_section structure */ @@ -244,7 +244,7 @@ static void sections_info_add(struct module_sections_info *info, struct module_s /** * Calculates file size after adding all sections - * + * * @param info Pointer to a module_sections_info structure */ static void sections_info_finalize(struct module_sections_info *info) @@ -257,7 +257,7 @@ static void sections_info_finalize(struct module_sections_info *info) /** * Checks the section header (type and flags) to determine the section type. - * + * * @param section section header * @return enum module_section_type */ @@ -430,7 +430,7 @@ void module_close(struct module *module) /** * Checks if the contents of the section overlaps - * + * * @param a first section to check * @param b second section to check * @return true if space of a sections overlap @@ -451,7 +451,7 @@ static bool section_check_overlap(const struct module_section *a, const struct m /** * Checks if the contents of the modules overlaps - * + * * @param mod first module to check * @param mod2 second module to check * @return error code From 994340663ebdb512ee16fbc1de18b63b9f005ac3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 4 Nov 2024 12:42:18 +0100 Subject: [PATCH 3/9] LLEXT: add support for detached executable sections With this commit functions can be marked to be placed in custom sections, e.g. by adding __attribute__((section(".text.dram"))) to them. Those sections will then become "detached," i.e. they won't be linked into the main address space of the module. Instead they will be linked at a low address, after which Zephyr LLEXT linked will prepare them to be used at their original location, e.g. in DRAM. This commit modifies the SOF LLEXT link helper to use low addresses for linking of such sections. Signed-off-by: Guennadi Liakhovetski --- scripts/llext_link_helper.py | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/scripts/llext_link_helper.py b/scripts/llext_link_helper.py index 98514c35a6cd..325d65a4e3de 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -73,9 +73,12 @@ def main(): command = [args.command] + executable = [] writable = [] readonly = [] + text_found = False + elf = ELFFile(open(args.file, 'rb')) # Create an object file with sections grouped by their properties, @@ -102,13 +105,13 @@ def main(): # In general additional executable sections are possible, e.g. # .init. In the future support for arbitrary such sections can be # added, similar to writable and read-only data below. - if s_name != '.text': - print(f"Warning! Non-standard executable section {s_name}") - - text_addr = max_alignment(text_addr, 0x1000, s_alignment) - text_size = s_size - - command.append(f'-Wl,-Ttext=0x{text_addr:x}') + if s_name == '.text': + text_found = True + text_addr = max_alignment(text_addr, 0x1000, s_alignment) + text_size = s_size + command.append(f'-Wl,-Ttext=0x{text_addr:x}') + else: + executable.append(section) continue @@ -122,6 +125,36 @@ def main(): # .rodata or other read-only sections readonly.append(section) + if not text_found: + raise RuntimeError('No .text section found in the object file') + + # The original LLEXT support in SOF linked all LLEXT modules with pre- + # calculated addresses. Such modules can only be used at those exact + # addresses, so we map memory buffers for such modules to those + # addresses and copy them there. + # Now we also need to be able to re-link parts of modules at run-time to + # run at arbitrary memory locations. One of the use-cases is running + # parts of the module directly in DRAM - sacrificing performance but + # saving scarce SRAM. We achieve this by placing non-performance + # critical functions in a .text.dram ELF section. When compiling and + # linking such functions, an additional .literal.dram section is + # automatically created. Note, that for some reason the compiler also + # marks that section as executable. + # This script links those sections at address 0. We could hard-code + # section names, but so far we choose to only link .text the "original" + # way and all other executable sections we link at 0. + exe_addr = 0 + + for section in executable: + s_alignment = section.header['sh_addralign'] + s_name = section.name + + exe_addr = align_up(exe_addr, s_alignment) + + command.append(f'-Wl,--section-start={s_name}=0x{exe_addr:x}') + + exe_addr += section.header['sh_size'] + start_addr = align_up(text_addr + text_size, 0x1000) for section in readonly: From 418a510f53a930539d856cef1afaad2369ca59ba Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 13:01:45 +0200 Subject: [PATCH 4/9] LLEXT: (cosmetic) simplify calling llext_manager_link() Re-use values from the caller instead of re-calculating them in the function. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 144634339b8d..958320f6b76d 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -236,19 +236,18 @@ static int llext_manager_unload_module(uint32_t module_id, const struct sof_man_ return err; } -static int llext_manager_link(struct llext_buf_loader *ebl, struct sof_man_fw_desc *desc, - struct sof_man_module *mod, uint32_t module_id, struct module_data *md, +static int llext_manager_link(struct llext_buf_loader *ebl, uintptr_t dram_base, + const char *name, uint32_t module_id, struct module_data *md, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) { - uintptr_t dram_base = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET; 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 = { .relocate_local = !ctx->segment[LIB_MANAGER_TEXT].size, .pre_located = true, }; - int ret = llext_load(&ebl->loader, mod->name, &md->llext, &ldr_parm); + int ret = llext_load(&ebl->loader, name, &md->llext, &ldr_parm); if (ret) return ret; @@ -336,7 +335,7 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, mod_array = (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(0)); /* LLEXT linking is only needed once for all the modules in the library */ - ret = llext_manager_link(&ebl, desc, mod_array, module_id, md, + ret = llext_manager_link(&ebl, dram_base, mod_array[0].name, module_id, md, (const void **)&buildinfo, &mod_manifest); if (ret < 0) { tr_err(&lib_manager_tr, "linking failed: %d", ret); From 8a0bf9bec3b4a1f434092622726560c02fcbb0a1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 13:44:27 +0200 Subject: [PATCH 5/9] LLEXT: simplify manifest array address calculation The LLEXT manifest array can be accessed in DRAM, no need to recalculate its address in SRAM. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 958320f6b76d..daa1b020c4b9 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -356,15 +356,8 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, if (ret < 0) return 0; - /* Manifest is in read-only data */ - uintptr_t dram_rodata = (uintptr_t)ctx->base_addr + - ctx->segment[LIB_MANAGER_RODATA].file_offset; - uintptr_t va_rodata_base = ctx->segment[LIB_MANAGER_RODATA].addr; - size_t offset = (uintptr_t)mod_manifest - dram_rodata; - - /* ctx->mod_manifest points to an array of module manifests */ - ctx->mod_manifest = sys_cache_uncached_ptr_get((__sparse_force void __sparse_cache *) - (va_rodata_base + offset)); + /* ctx->mod_manifest points to a const array of module manifests */ + ctx->mod_manifest = mod_manifest; } return ctx->mod_manifest[entry_index].module.entry_point; From a97175ec4191b042e2e4b51e21b6f1aabd1805cb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 13:56:28 +0200 Subject: [PATCH 6/9] LLEXT: rimage: add support for detached sections Rimage shouldn't include detached sections in image size calculations to avoid creating meaninglessly large values. Signed-off-by: Guennadi Liakhovetski --- tools/rimage/src/include/rimage/module.h | 2 ++ .../src/include/rimage/sof/user/manifest.h | 9 +++++ tools/rimage/src/module.c | 36 +++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tools/rimage/src/include/rimage/module.h b/tools/rimage/src/include/rimage/module.h index 872b559ddbd2..5df4f469b57f 100644 --- a/tools/rimage/src/include/rimage/module.h +++ b/tools/rimage/src/include/rimage/module.h @@ -24,6 +24,8 @@ struct module_section { /* The contents of the section lie in the rom memory space */ bool rom; + /* The section is detached from the main part of the module */ + bool detached; /* ADSP devices have their RAM regions mapped twice. The first mapping is set in the CPU * to bypass the L1 cache, and so access through pointers in that region is coherent between diff --git a/tools/rimage/src/include/rimage/sof/user/manifest.h b/tools/rimage/src/include/rimage/sof/user/manifest.h index 6e1dc09df89e..829b1b134810 100644 --- a/tools/rimage/src/include/rimage/sof/user/manifest.h +++ b/tools/rimage/src/include/rimage/sof/user/manifest.h @@ -250,4 +250,13 @@ struct sof_man_module_manifest { (sizeof(struct sof_man_fw_header) + \ (index) * sizeof(struct sof_man_module)) +/* + * LLEXT module link area for detached sections. When an LLEXT module contains + * detached sections, they will be linked with addresses in this range. The + * upper limit has no special meaning, simply assuming that 128MiB should be + * enough and that SRAM will not use these addresses. + */ +#define SOF_MODULE_DRAM_LINK_START 0 +#define SOF_MODULE_DRAM_LINK_END 0x08000000 + #endif /* __RIMAGE_USER_MANIFEST_H__ */ diff --git a/tools/rimage/src/module.c b/tools/rimage/src/module.c index 4c585b5b53b6..965b9feecc19 100644 --- a/tools/rimage/src/module.c +++ b/tools/rimage/src/module.c @@ -15,6 +15,7 @@ #include #include #include +#include int module_read_section(const struct module *module, const struct module_section *section, void *buffer, const size_t size) @@ -199,6 +200,33 @@ static bool section_is_rom(const struct memory_config *config, return false; } +/** + * Checks if the section is detached from the main module + * + * Some sections can be detached from the main module, e.g. for running in DRAM + * + * @param config Memory configuration structure + * @param section section to be checked + * @return true if section is detached + */ +static bool section_is_detached(const struct memory_config *config, + const struct elf_section_header *section) +{ + uint32_t sect_start, sect_end; + const uint32_t start = SOF_MODULE_DRAM_LINK_START, end = SOF_MODULE_DRAM_LINK_END; + + sect_start = section->data.vaddr; + sect_end = sect_start + section->data.size; + + if (sect_end <= start || sect_start >= end) + return false; + if (sect_start >= start && sect_end <= end) + return true; + + fprintf(stderr, "Warning! Section %s partially overlaps with dram memory.\n", section->name); + return false; +} + /** * Initialize module_sections_info structure * @@ -315,11 +343,12 @@ void module_parse_sections(struct module *module, const struct memory_config *me out_section->size = sect->data.size; out_section->type = get_section_type(sect); out_section->rom = section_is_rom(mem_cfg, sect); + out_section->detached = section_is_detached(mem_cfg, sect); out_section->address = sect->data.vaddr; out_section->load_address = find_physical_address(&module->elf, sect->data.vaddr); /* Don't convert ROM addresses, ROM sections aren't included in the output image */ - if (!out_section->rom) { + if (!out_section->rom && !out_section->detached) { /* Walk the sections in the ELF file, changing the VMA/LMA of each uncached section * to the equivalent address in the cached area of memory. */ out_section->address = uncache_to_cache(&mem_cfg->alias, @@ -357,7 +386,10 @@ void module_parse_sections(struct module *module, const struct memory_config *me break; } - if (out_section->rom) { + if (out_section->detached) { + /* Detached sections are copied as is and don't contribute to metadata */ + fprintf(stdout, " detached"); + } else if (out_section->rom) { /* ROM sections aren't included in the output image */ fprintf(stdout, " ROM"); } else { From 4cd64dd0b80fb6d700f4d3b549bf00c4577ef426 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 13:50:43 +0200 Subject: [PATCH 7/9] LLEXT: add support for detached sections We link detached sections, to be used in DRAM, at low addresses, beginning at 0. Add a callback for Zephyr llext to recognise our detached sections. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index daa1b020c4b9..a3bc9b09b2a9 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -236,6 +236,11 @@ static int llext_manager_unload_module(uint32_t module_id, const struct sof_man_ return err; } +static bool llext_manager_section_detached(const elf_shdr_t *shdr) +{ + return shdr->sh_addr < SOF_MODULE_DRAM_LINK_END; +} + static int llext_manager_link(struct llext_buf_loader *ebl, uintptr_t dram_base, const char *name, uint32_t module_id, struct module_data *md, const void **buildinfo, @@ -246,6 +251,7 @@ static int llext_manager_link(struct llext_buf_loader *ebl, uintptr_t dram_base, struct llext_load_param ldr_parm = { .relocate_local = !ctx->segment[LIB_MANAGER_TEXT].size, .pre_located = true, + .section_detached = llext_manager_section_detached, }; int ret = llext_load(&ebl->loader, name, &md->llext, &ldr_parm); @@ -262,7 +268,7 @@ static int llext_manager_link(struct llext_buf_loader *ebl, uintptr_t dram_base, ctx->segment[LIB_MANAGER_TEXT].size, ctx->segment[LIB_MANAGER_TEXT].file_offset); - /* This contains all other sections, except .text, it might contain .bss too */ + /* All read-only data sections */ ctx->segment[LIB_MANAGER_RODATA].addr = ebl->loader.sects[LLEXT_MEM_RODATA].sh_addr; ctx->segment[LIB_MANAGER_RODATA].file_offset = @@ -274,6 +280,7 @@ static int llext_manager_link(struct llext_buf_loader *ebl, uintptr_t dram_base, ctx->segment[LIB_MANAGER_RODATA].size, ctx->segment[LIB_MANAGER_RODATA].file_offset); + /* All writable data sections */ ctx->segment[LIB_MANAGER_DATA].addr = ebl->loader.sects[LLEXT_MEM_DATA].sh_addr; ctx->segment[LIB_MANAGER_DATA].file_offset = From 0a9b72bdb1217033ef2c2cdebb7f99a0167c4cf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 16:48:03 +0200 Subject: [PATCH 8/9] LLEXT: add a macro to place selected functions in .text.dram With this macro modules can use "__cold" in front of their function definitions to place them in the .text.dram section, which will force their execution in DRAM without being copied to SRAM. Signed-off-by: Guennadi Liakhovetski --- src/include/module/module/llext.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/include/module/module/llext.h b/src/include/module/module/llext.h index 05b8f0ffb5f5..f8477fe7d9b3 100644 --- a/src/include/module/module/llext.h +++ b/src/include/module/module/llext.h @@ -34,4 +34,10 @@ static const struct sof_module_api_build_info buildinfo __section(".mod_buildinf .api_version_number.full = SOF_MODULE_API_CURRENT_VERSION, \ } +#if CONFIG_LLEXT_TYPE_ELF_RELOCATABLE && defined(LL_EXTENSION_BUILD) +#define __cold __section(".text.dram") +#else +#define __cold +#endif + #endif From 7062a86dbf82eff55a3a838bfe94536569d430e5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 Nov 2024 17:02:21 +0200 Subject: [PATCH 9/9] drc: move .init() and .free() to DRAM DRC's .init() and .free() functions aren't performance-critical, move them to DRAM for modular builds. Signed-off-by: Guennadi Liakhovetski --- src/audio/drc/drc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio/drc/drc.c b/src/audio/drc/drc.c index af29dfaefa78..000e804cbd00 100644 --- a/src/audio/drc/drc.c +++ b/src/audio/drc/drc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -138,7 +139,7 @@ static int drc_setup(struct drc_comp_data *cd, uint16_t channels, uint32_t rate) * End of DRC setup code. Next the standard component methods. */ -static int drc_init(struct processing_module *mod) +__cold static int drc_init(struct processing_module *mod) { struct module_data *md = &mod->priv; struct comp_dev *dev = mod->dev; @@ -195,7 +196,7 @@ static int drc_init(struct processing_module *mod) return ret; } -static int drc_free(struct processing_module *mod) +__cold static int drc_free(struct processing_module *mod) { struct drc_comp_data *cd = module_get_private_data(mod); @@ -407,7 +408,6 @@ SOF_MODULE_INIT(drc, sys_comp_module_drc_interface_init); /* modular: llext dynamic link */ #include -#include #include #define UUID_DRC 0xda, 0xe4, 0x6e, 0xb3, 0x6f, 0x00, 0xf9, 0x47, \