Skip to content

Commit 5c8ed84

Browse files
committed
library_manager: llext: implement page-level VMA allocator for relocatable modules
Implement page-level virtual memory mapping using Zephyr's sys_bitarray utility over the library region. Compile section layout at load-time to allocate virtual addresses and rewrite section sh_addr headers in-place. This enables Zephyr LLEXT to naturally relocate references. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1 parent 1622c1e commit 5c8ed84

3 files changed

Lines changed: 130 additions & 1 deletion

File tree

src/include/sof/lib_manager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ struct lib_manager_module {
116116
unsigned int n_dependent; /* For auxiliary modules: number of dependents */
117117
bool mapped;
118118
struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS];
119+
uintptr_t vma_base;
120+
size_t vma_size;
119121
};
120122

121123
struct lib_manager_mod_ctx {

src/library_manager/llext_manager.c

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,101 @@ extern struct tr_ctx lib_manager_tr;
5050

5151
#define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE
5252

53+
#include <zephyr/sys/bitarray.h>
54+
#include <zephyr/llext/elf.h>
55+
56+
#define LLEXT_LIB_PAGES (CONFIG_LIBRARY_REGION_SIZE / PAGE_SZ)
57+
SYS_BITARRAY_DEFINE_STATIC(lib_vma_bitarray, LLEXT_LIB_PAGES);
58+
59+
static uintptr_t llext_manager_alloc_vma(size_t size)
60+
{
61+
size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ;
62+
size_t offset;
63+
int ret;
64+
65+
ret = sys_bitarray_alloc(&lib_vma_bitarray, num_pages, &offset);
66+
if (ret < 0) {
67+
tr_err(&lib_manager_tr, "llext_manager_alloc_vma: failed to allocate %zu pages", num_pages);
68+
return 0;
69+
}
70+
71+
return CONFIG_LIBRARY_BASE_ADDRESS + offset * PAGE_SZ;
72+
}
73+
74+
void llext_manager_free_vma(uintptr_t vma, size_t size)
75+
{
76+
if (!vma || !size)
77+
return;
78+
79+
size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ;
80+
size_t offset = (vma - CONFIG_LIBRARY_BASE_ADDRESS) / PAGE_SZ;
81+
82+
sys_bitarray_free(&lib_vma_bitarray, num_pages, offset);
83+
}
84+
85+
static enum llext_mem llext_manager_get_sec_mem_idx(const char *name, const elf_shdr_t *shdr)
86+
{
87+
if (strcmp(name, ".exported_sym") == 0)
88+
return LLEXT_MEM_EXPORT;
89+
90+
switch (shdr->sh_type) {
91+
case SHT_NOBITS:
92+
return LLEXT_MEM_BSS;
93+
case SHT_PROGBITS:
94+
if (shdr->sh_flags & SHF_EXECINSTR)
95+
return LLEXT_MEM_TEXT;
96+
else if (shdr->sh_flags & SHF_WRITE)
97+
return LLEXT_MEM_DATA;
98+
else
99+
return LLEXT_MEM_RODATA;
100+
case SHT_PREINIT_ARRAY:
101+
return LLEXT_MEM_PREINIT;
102+
case SHT_INIT_ARRAY:
103+
return LLEXT_MEM_INIT;
104+
case SHT_FINI_ARRAY:
105+
return LLEXT_MEM_FINI;
106+
default:
107+
return LLEXT_MEM_COUNT;
108+
}
109+
}
110+
111+
static size_t llext_manager_layout_sections(uint8_t *elf_buf, uintptr_t vma_base)
112+
{
113+
elf_ehdr_t *hdr = (elf_ehdr_t *)elf_buf;
114+
elf_shdr_t *shdrs = (elf_shdr_t *)(elf_buf + hdr->e_shoff);
115+
elf_shdr_t *shstr_shdr = shdrs + hdr->e_shstrndx;
116+
const char *shstrtab = (const char *)(elf_buf + shstr_shdr->sh_offset);
117+
118+
uintptr_t current_vma = vma_base;
119+
enum llext_mem last_region = LLEXT_MEM_COUNT;
120+
121+
for (int i = 0; i < hdr->e_shnum; i++) {
122+
elf_shdr_t *shdr = shdrs + i;
123+
124+
if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_size == 0)
125+
continue;
126+
127+
const char *name = shstrtab + shdr->sh_name;
128+
enum llext_mem s_region = llext_manager_get_sec_mem_idx(name, shdr);
129+
130+
if (s_region == LLEXT_MEM_COUNT)
131+
continue;
132+
133+
if (last_region != LLEXT_MEM_COUNT && last_region != s_region) {
134+
current_vma = ALIGN_UP(current_vma, PAGE_SZ);
135+
}
136+
last_region = s_region;
137+
138+
current_vma = ALIGN_UP(current_vma, shdr->sh_addralign);
139+
if (vma_base) {
140+
shdr->sh_addr = current_vma;
141+
}
142+
current_vma += shdr->sh_size;
143+
}
144+
145+
return current_vma - vma_base;
146+
}
147+
53148
static int llext_manager_update_flags(void __sparse_cache *vma, size_t size, uint32_t flags)
54149
{
55150
size_t pre_pad_size = (uintptr_t)vma & (PAGE_SZ - 1);
@@ -383,6 +478,26 @@ static int llext_manager_link(const char *name,
383478
}
384479

385480
if (!*llext || mctx->mapped) {
481+
if (!*llext) {
482+
uint8_t *elf_buf = (uint8_t *)mctx->ebl->buf;
483+
size_t total_size = llext_manager_layout_sections(elf_buf, 0);
484+
if (total_size == 0) {
485+
tr_err(&lib_manager_tr, "llext_manager_link: layout sections failed");
486+
return -EINVAL;
487+
}
488+
489+
uintptr_t vma_base = llext_manager_alloc_vma(total_size);
490+
if (!vma_base) {
491+
tr_err(&lib_manager_tr, "llext_manager_link: VMA allocation failed");
492+
return -ENOMEM;
493+
}
494+
495+
mctx->vma_base = vma_base;
496+
mctx->vma_size = total_size;
497+
498+
llext_manager_layout_sections(elf_buf, vma_base);
499+
}
500+
386501
/*
387502
* Either the very first time loading this module, or the module
388503
* is already mapped, we just call llext_load() to refcount it
@@ -395,8 +510,15 @@ static int llext_manager_link(const char *name,
395510
};
396511

397512
ret = llext_load(ldr, name, llext, &ldr_parm);
398-
if (ret)
513+
if (ret) {
514+
tr_err(&lib_manager_tr, "llext_load failed: ret=%d", ret);
515+
if (mctx->vma_base) {
516+
llext_manager_free_vma(mctx->vma_base, mctx->vma_size);
517+
mctx->vma_base = 0;
518+
mctx->vma_size = 0;
519+
}
399520
return ret;
521+
}
400522
}
401523

402524
/* All code sections */

src/library_manager/llext_manager_dram.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
LOG_MODULE_DECLARE(lib_manager, CONFIG_SOF_LOG_LEVEL);
1616

17+
void llext_manager_free_vma(uintptr_t vma, size_t size);
18+
1719
struct lib_manager_dram_storage {
1820
struct ext_library ext_lib;
1921
struct lib_manager_mod_ctx *ctx;
@@ -332,6 +334,9 @@ int llext_manager_restore_from_dram(void)
332334
if (mod[k].llext)
333335
llext_unload(&mod[k].llext);
334336

337+
if (mod[k].vma_base)
338+
llext_manager_free_vma(mod[k].vma_base, mod[k].vma_size);
339+
335340
if (mod[k].ebl)
336341
rfree(mod[k].ebl);
337342
}

0 commit comments

Comments
 (0)