Skip to content
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

Merged
merged 7 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/audio/mux/mux.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ static const struct module_interface demux_interface = {
DECLARE_MODULE_ADAPTER(demux_interface, demux_uuid, demux_tr);
SOF_MODULE_INIT(demux, sys_comp_module_demux_interface_init);

#if CONFIG_COMP_VOLUME_MODULE
#if CONFIG_COMP_MUX_MODULE
/* modular: llext dynamic link */

#include <module/module/api_ver.h>
Expand All @@ -486,13 +486,21 @@ SOF_MODULE_INIT(demux, sys_comp_module_demux_interface_init);
0xE2, 0xA2, 0xF4, 0x2E, 0x30, 0x69
SOF_LLEXT_MOD_ENTRY(mux, &mux_interface);

#define UUID_DEMUX 0x68, 0x68, 0xB2, 0xC4, 0x30, 0x14, 0x0E, 0x47, 0x89, 0xA0, \
0x15, 0xD1, 0xC7, 0x7F, 0x85, 0x1A
SOF_LLEXT_MOD_ENTRY(demux, &demux_interface);
/*
* The demux entry is removed because mtl.toml doesn't have an entry
* for it. Once that is fixed, the manifest line below can be
* re-activated:
* #define UUID_DEMUX 0x68, 0x68, 0xB2, 0xC4, 0x30, 0x14, 0x0E, 0x47, 0x89, 0xA0, \
* 0x15, 0xD1, 0xC7, 0x7F, 0x85, 0x1A
* SOF_LLEXT_MOD_ENTRY(demux, &demux_interface);
*/
Comment on lines +489 to +496
Copy link
Member

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 ?

Copy link
Collaborator Author

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

Copy link
Member

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.

Copy link
Collaborator Author

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.


static const struct sof_man_module_manifest mod_manifest[] __section(".module") __used = {
SOF_LLEXT_MODULE_MANIFEST("MUX", mux_llext_entry, 1, UUID_MUX, 15),
SOF_LLEXT_MODULE_MANIFEST("DEMUX", demux_llext_entry, 1, UUID_DEMUX, 15),
/*
* See comment above for a demux deactivation reason
* SOF_LLEXT_MODULE_MANIFEST("DEMUX", demux_llext_entry, 1, UUID_DEMUX, 15),
*/
};

SOF_LLEXT_BUILDINFO;
Expand Down
10 changes: 8 additions & 2 deletions src/include/sof/lib_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,18 @@ struct lib_manager_segment_desc {
size_t size;
};

struct lib_manager_mod_ctx {
void *base_addr;
struct lib_manager_module {
unsigned int start_idx;
const struct sof_man_module_manifest *mod_manifest;
struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS];
};

struct lib_manager_mod_ctx {
void *base_addr;
unsigned int n_mod;
struct lib_manager_module *mod;
};

struct ext_library {
struct k_spinlock lock; /* last locking CPU record */
struct lib_manager_mod_ctx *desc[LIB_MANAGER_MAX_LIBS];
Expand Down
223 changes: 162 additions & 61 deletions src/library_manager/llext_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 *)
Expand Down Expand Up @@ -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);
Expand All @@ -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,
};
Expand All @@ -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");

Expand All @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The 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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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);
Expand All @@ -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)
Expand Down
Loading
Loading