-
Notifications
You must be signed in to change notification settings - Fork 321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LLEXT: libraries with multiple modules #9688
Changes from all commits
95c4be7
2d2c5bf
d3cd367
3bd6ba4
50f82a9
3853ab1
1e3f9ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,14 +37,6 @@ | |
#include <stddef.h> | ||
#include <stdint.h> | ||
|
||
/* | ||
* 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; | ||
|
@@ -125,24 +117,22 @@ static int llext_manager_load_data_from_storage(const struct llext *ext, | |
} | ||
|
||
static int llext_manager_load_module(const struct llext *ext, const struct llext_buf_loader *ebl, | ||
uint32_t module_id) | ||
const struct lib_manager_module *mctx) | ||
{ | ||
struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); | ||
|
||
/* Executable code (.text) */ | ||
void __sparse_cache *va_base_text = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_TEXT].addr; | ||
size_t text_size = ctx->segment[LIB_MANAGER_TEXT].size; | ||
mctx->segment[LIB_MANAGER_TEXT].addr; | ||
size_t text_size = mctx->segment[LIB_MANAGER_TEXT].size; | ||
|
||
/* Read-only data (.rodata and others) */ | ||
void __sparse_cache *va_base_rodata = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_RODATA].addr; | ||
size_t rodata_size = ctx->segment[LIB_MANAGER_RODATA].size; | ||
mctx->segment[LIB_MANAGER_RODATA].addr; | ||
size_t rodata_size = mctx->segment[LIB_MANAGER_RODATA].size; | ||
|
||
/* Writable data (.data, .bss and others) */ | ||
void __sparse_cache *va_base_data = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_DATA].addr; | ||
size_t data_size = ctx->segment[LIB_MANAGER_DATA].size; | ||
mctx->segment[LIB_MANAGER_DATA].addr; | ||
size_t data_size = mctx->segment[LIB_MANAGER_DATA].size; | ||
|
||
/* .bss, should be within writable data above */ | ||
void __sparse_cache *bss_addr = (void __sparse_cache *) | ||
|
@@ -201,23 +191,22 @@ static int llext_manager_load_module(const struct llext *ext, const struct llext | |
return ret; | ||
} | ||
|
||
static int llext_manager_unload_module(uint32_t module_id) | ||
static int llext_manager_unload_module(const struct lib_manager_module *mctx) | ||
{ | ||
struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); | ||
/* Executable code (.text) */ | ||
void __sparse_cache *va_base_text = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_TEXT].addr; | ||
size_t text_size = ctx->segment[LIB_MANAGER_TEXT].size; | ||
mctx->segment[LIB_MANAGER_TEXT].addr; | ||
size_t text_size = mctx->segment[LIB_MANAGER_TEXT].size; | ||
|
||
/* Read-only data (.rodata, etc.) */ | ||
void __sparse_cache *va_base_rodata = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_RODATA].addr; | ||
size_t rodata_size = ctx->segment[LIB_MANAGER_RODATA].size; | ||
mctx->segment[LIB_MANAGER_RODATA].addr; | ||
size_t rodata_size = mctx->segment[LIB_MANAGER_RODATA].size; | ||
|
||
/* Writable data (.data, .bss, etc.) */ | ||
void __sparse_cache *va_base_data = (void __sparse_cache *) | ||
ctx->segment[LIB_MANAGER_DATA].addr; | ||
size_t data_size = ctx->segment[LIB_MANAGER_DATA].size; | ||
mctx->segment[LIB_MANAGER_DATA].addr; | ||
size_t data_size = mctx->segment[LIB_MANAGER_DATA].size; | ||
int err = 0, ret; | ||
|
||
ret = llext_manager_align_unmap(va_base_text, text_size); | ||
|
@@ -240,15 +229,14 @@ 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, | ||
const char *name, uint32_t module_id, struct module_data *md, | ||
static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, | ||
struct lib_manager_module *mctx, struct module_data *md, | ||
const void **buildinfo, | ||
const struct sof_man_module_manifest **mod_manifest) | ||
{ | ||
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, | ||
.relocate_local = !mctx->segment[LIB_MANAGER_TEXT].size, | ||
.pre_located = true, | ||
.section_detached = llext_manager_section_detached, | ||
}; | ||
|
@@ -257,30 +245,30 @@ static int llext_manager_link(struct llext_buf_loader *ebl, | |
if (ret) | ||
return ret; | ||
|
||
ctx->segment[LIB_MANAGER_TEXT].addr = ebl->loader.sects[LLEXT_MEM_TEXT].sh_addr; | ||
ctx->segment[LIB_MANAGER_TEXT].size = ebl->loader.sects[LLEXT_MEM_TEXT].sh_size; | ||
mctx->segment[LIB_MANAGER_TEXT].addr = ebl->loader.sects[LLEXT_MEM_TEXT].sh_addr; | ||
mctx->segment[LIB_MANAGER_TEXT].size = ebl->loader.sects[LLEXT_MEM_TEXT].sh_size; | ||
|
||
tr_dbg(&lib_manager_tr, ".text: start: %#lx size %#x", | ||
ctx->segment[LIB_MANAGER_TEXT].addr, | ||
ctx->segment[LIB_MANAGER_TEXT].size); | ||
mctx->segment[LIB_MANAGER_TEXT].addr, | ||
mctx->segment[LIB_MANAGER_TEXT].size); | ||
|
||
/* All read-only data sections */ | ||
ctx->segment[LIB_MANAGER_RODATA].addr = | ||
mctx->segment[LIB_MANAGER_RODATA].addr = | ||
ebl->loader.sects[LLEXT_MEM_RODATA].sh_addr; | ||
ctx->segment[LIB_MANAGER_RODATA].size = ebl->loader.sects[LLEXT_MEM_RODATA].sh_size; | ||
mctx->segment[LIB_MANAGER_RODATA].size = ebl->loader.sects[LLEXT_MEM_RODATA].sh_size; | ||
|
||
tr_dbg(&lib_manager_tr, ".rodata: start: %#lx size %#x", | ||
ctx->segment[LIB_MANAGER_RODATA].addr, | ||
ctx->segment[LIB_MANAGER_RODATA].size); | ||
mctx->segment[LIB_MANAGER_RODATA].addr, | ||
mctx->segment[LIB_MANAGER_RODATA].size); | ||
|
||
/* All writable data sections */ | ||
ctx->segment[LIB_MANAGER_DATA].addr = | ||
mctx->segment[LIB_MANAGER_DATA].addr = | ||
ebl->loader.sects[LLEXT_MEM_DATA].sh_addr; | ||
ctx->segment[LIB_MANAGER_DATA].size = ebl->loader.sects[LLEXT_MEM_DATA].sh_size; | ||
mctx->segment[LIB_MANAGER_DATA].size = ebl->loader.sects[LLEXT_MEM_DATA].sh_size; | ||
|
||
tr_dbg(&lib_manager_tr, ".data: start: %#lx size %#x", | ||
ctx->segment[LIB_MANAGER_DATA].addr, | ||
ctx->segment[LIB_MANAGER_DATA].size); | ||
mctx->segment[LIB_MANAGER_DATA].addr, | ||
mctx->segment[LIB_MANAGER_DATA].size); | ||
|
||
ssize_t binfo_o = llext_find_section(&ebl->loader, ".mod_buildinfo"); | ||
|
||
|
@@ -295,35 +283,131 @@ static int llext_manager_link(struct llext_buf_loader *ebl, | |
return binfo_o >= 0 && mod_o >= 0 ? 0 : -EPROTO; | ||
} | ||
|
||
static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, | ||
const struct sof_man_fw_desc *desc, | ||
const struct sof_man_module *mod_array) | ||
{ | ||
unsigned int i, n_mod; | ||
size_t offs; | ||
|
||
/* count modules */ | ||
for (i = 0, n_mod = 0, offs = ~0; i < desc->header.num_module_entries; i++) | ||
if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { | ||
offs = mod_array[i].segment[LIB_MANAGER_TEXT].file_offset; | ||
n_mod++; | ||
} | ||
|
||
/* | ||
* Loadable modules are loaded to DRAM once and never unloaded from it. | ||
* Context, related to them, is never freed | ||
*/ | ||
ctx->mod = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED, SOF_MEM_FLAG_COHERENT, | ||
SOF_MEM_CAPS_RAM, n_mod * sizeof(ctx->mod[0])); | ||
if (!ctx->mod) | ||
return -ENOMEM; | ||
|
||
ctx->n_mod = n_mod; | ||
|
||
for (i = 0, n_mod = 0, offs = ~0; i < desc->header.num_module_entries; i++) | ||
if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { | ||
offs = mod_array[i].segment[LIB_MANAGER_TEXT].file_offset; | ||
ctx->mod[n_mod].segment[LIB_MANAGER_TEXT].size = 0; | ||
ctx->mod[n_mod++].start_idx = i; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigned int idx) | ||
{ | ||
unsigned int i; | ||
|
||
for (i = 0; i < ctx->n_mod; i++) | ||
if (ctx->mod[i].start_idx > idx) | ||
break; | ||
|
||
return i - 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if "ctx->mod[0].startidx > idx" is true? this would return (unsigned)-1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kv2019i it cannot be true by construction - the first module always starts from 0. I can add a comment in an incremental PR |
||
} | ||
|
||
uintptr_t llext_manager_allocate_module(struct processing_module *proc, | ||
const struct comp_ipc_config *ipc_config, | ||
const void *ipc_specific_config) | ||
{ | ||
uint32_t module_id = IPC4_MOD_ID(ipc_config->id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "varoable definition" in git commit message |
||
struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(module_id); | ||
struct sof_man_module *mod_array; | ||
int ret; | ||
uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); | ||
struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); | ||
const struct sof_module_api_build_info *buildinfo; | ||
|
||
if (!ctx || !desc) { | ||
tr_err(&lib_manager_tr, "failed to get module descriptor"); | ||
return 0; | ||
} | ||
|
||
struct sof_man_module *mod_array = (struct sof_man_module *)((char *)desc + | ||
SOF_MAN_MODULE_OFFSET(0)); | ||
uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); | ||
size_t mod_offset = mod_array[entry_index].segment[LIB_MANAGER_TEXT].file_offset; | ||
const struct sof_man_module_manifest *mod_manifest; | ||
size_t mod_size = desc->header.preload_page_count * PAGE_SZ - FILE_TEXT_OFFSET_V1_8; | ||
uintptr_t dram_base = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET; | ||
struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)dram_base + FILE_TEXT_OFFSET_V1_8, | ||
mod_size); | ||
const struct sof_module_api_build_info *buildinfo; | ||
struct module_data *md = &proc->priv; | ||
size_t mod_size; | ||
int i, inst_idx; | ||
int ret; | ||
|
||
tr_dbg(&lib_manager_tr, "mod_id: %#x", ipc_config->id); | ||
|
||
if (!ctx || !desc) { | ||
tr_err(&lib_manager_tr, "failed to get module descriptor"); | ||
if (!ctx->mod) | ||
llext_manager_mod_init(ctx, desc, mod_array); | ||
|
||
if (entry_index >= desc->header.num_module_entries) { | ||
tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d", | ||
entry_index, desc->header.num_module_entries - 1); | ||
return 0; | ||
} | ||
|
||
mod_array = (struct sof_man_module *)((char *)desc + SOF_MAN_MODULE_OFFSET(0)); | ||
unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); | ||
struct lib_manager_module *mctx = ctx->mod + mod_idx; | ||
|
||
/* | ||
* We don't know the number of ELF files that this library is built of. | ||
* We know the number of module drivers, but each of those ELF files can | ||
* also contain multiple such drivers. Each driver brings two copies of | ||
* its manifest with it: one in the ".module" ELF section and one in an | ||
* array of manifests at the beginning of the library. This latter array | ||
* is created from a TOML configuration file. The order is preserved - | ||
* this is guaranteed by rimage. | ||
* All module drivers within a single ELF file have equal .file_offset, | ||
* this makes it possible to find borders between them. | ||
* We know the global index of the requested driver in that array, but | ||
* we need to find the matching manifest in ".module" because only it | ||
* contains the entry point. For safety we calculate the ELF driver | ||
* index and then also check the driver name. | ||
* We also need the driver size. For this we search the manifest array | ||
* for the next ELF file, then the difference between offsets gives us | ||
* the driver size. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good comment! |
||
*/ | ||
for (i = entry_index - 1; i >= 0; i--) | ||
if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != mod_offset) | ||
break; | ||
|
||
/* Driver index within a single module */ | ||
inst_idx = entry_index - i - 1; | ||
|
||
/* Find the next module or stop at the end */ | ||
for (i = entry_index + 1; i < desc->header.num_module_entries; i++) | ||
if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != mod_offset) | ||
break; | ||
|
||
/* LLEXT linking is only needed once for all the modules in the library */ | ||
ret = llext_manager_link(&ebl, mod_array[0].name, module_id, md, | ||
if (i == desc->header.num_module_entries) | ||
mod_size = desc->header.preload_page_count * PAGE_SZ - mod_offset; | ||
else | ||
mod_size = ALIGN_UP(mod_array[i].segment[LIB_MANAGER_TEXT].file_offset - mod_offset, | ||
PAGE_SZ); | ||
|
||
uintptr_t dram_base = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET; | ||
struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, mod_size); | ||
|
||
/* LLEXT linking is only needed once for all the drivers in each module */ | ||
ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, md, | ||
(const void **)&buildinfo, &mod_manifest); | ||
if (ret < 0) { | ||
tr_err(&lib_manager_tr, "linking failed: %d", ret); | ||
|
@@ -339,26 +423,43 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, | |
} | ||
|
||
/* Map executable code and data */ | ||
ret = llext_manager_load_module(md->llext, &ebl, module_id); | ||
ret = llext_manager_load_module(md->llext, &ebl, mctx); | ||
if (ret < 0) | ||
return 0; | ||
|
||
/* ctx->mod_manifest points to a const array of module manifests */ | ||
ctx->mod_manifest = mod_manifest; | ||
/* mctx->mod_manifest points to a const array of module manifests */ | ||
mctx->mod_manifest = mod_manifest; | ||
} | ||
|
||
return ctx->mod_manifest[entry_index].module.entry_point; | ||
if (strncmp(mod_array[entry_index].name, mctx->mod_manifest[inst_idx].module.name, | ||
sizeof(mod_array[0].name))) { | ||
tr_err(&lib_manager_tr, "Name mismatch %s vs. %s", | ||
mod_array[entry_index].name, mctx->mod_manifest[inst_idx].module.name); | ||
return 0; | ||
} | ||
|
||
return mctx->mod_manifest[inst_idx].module.entry_point; | ||
} | ||
|
||
int llext_manager_free_module(const uint32_t component_id) | ||
{ | ||
const uint32_t module_id = IPC4_MOD_ID(component_id); | ||
const unsigned int base_module_id = LIB_MANAGER_GET_LIB_ID(module_id) << | ||
LIB_MANAGER_LIB_ID_SHIFT; | ||
struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(module_id); | ||
struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); | ||
uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); | ||
|
||
if (entry_index >= desc->header.num_module_entries) { | ||
tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d", | ||
entry_index, desc->header.num_module_entries - 1); | ||
return -ENOENT; | ||
} | ||
|
||
unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); | ||
struct lib_manager_module *mctx = ctx->mod + mod_idx; | ||
|
||
tr_dbg(&lib_manager_tr, "mod_id: %#x", component_id); | ||
|
||
return llext_manager_unload_module(base_module_id); | ||
return llext_manager_unload_module(mctx); | ||
} | ||
|
||
bool comp_is_llext(struct comp_dev *comp) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need an entry for demux for legacy reasons ? if so, can we followup with a PR that adds the demux UUID ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lgirdwood sure we can restore it if needed, but we need a full TOML entry for it for that, we don't have one ATM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, pls just copy the mux CPC data for demux and add inline comment. The toml CPC data is in need of tuning in general anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lgirdwood my current demux change is a pure compile-time one. Demux is currently disabled in both monolithic and modular IPC4 Intel builds. Also, even for compilation, this change only affects modular mux/demux builds, which we don't do either in any default configurations anyway. Whereas if I add a demux TOML entry, it will affect the generated binary, it will add a demux driver, where we haven't got one until now. So I would prefer to do that in a separate PR.