From a4618d807a49aed816cbe75335b7b66c060ba249 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 29 Sep 2026 15:10:53 +0300 Subject: [PATCH 1/3] userspace: doc: add user/kernel split reference and syscall map SOF audio application logic now runs in Zephyr user-space on userspace configurations (e.g. Intel PTL with CONFIG_SOF_USERSPACE_LL), but the source layout gives no indication of which code runs in kernel vs user context or where the privilege boundary is crossed. Add a canonical reference under src/include/sof/userspace/: - README.md documents the execution model, a four-tier directory taxonomy (kernel-only / boundary / application / shared-library), the syscall boundary, the memory-partition markers and their usage rule, the SOF_USERSPACE* Kconfig hierarchy, and rules for placing new code. The taxonomy is grep-verifiable. - syscalls.h is a documentation-only map of the complete syscall surface, grouping every SOF __syscall by subsystem and pointing to its z_vrfy_ validator and z_impl_ logic. It notes that zephyr/syscall/ already centralizes the lib-level validators and that the remaining scattered validators are the migration target. No functional change; documentation only. Signed-off-by: Kai Vehmanen --- src/include/sof/userspace/README.md | 221 +++++++++++++++++++++++++++ src/include/sof/userspace/syscalls.h | 159 +++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 src/include/sof/userspace/README.md create mode 100644 src/include/sof/userspace/syscalls.h diff --git a/src/include/sof/userspace/README.md b/src/include/sof/userspace/README.md new file mode 100644 index 000000000000..4feb2076668a --- /dev/null +++ b/src/include/sof/userspace/README.md @@ -0,0 +1,221 @@ +# SOF User/Kernel Split — Architecture and Conventions + +This is the canonical reference for how Sound Open Firmware (SOF) is divided between +**kernel (supervisor) context** and **user (unprivileged) context** on top of Zephyr +userspace. It defines the conventions that make the split evident in the source layout and +easy to maintain. + +> **Status:** convention proposal. The execution model and the syscall/marker/Kconfig facts +> below reflect the code as it is today. The *directory taxonomy* and the per-directory +> "Runs in:" banners are the proposed mechanism for making the split visible; they are being +> rolled out incrementally. + +## Why this exists + +On PTL (`intel_adsp_ace30_ptl`) builds, audio application logic runs in Zephyr **user-space** +by default. The board config enables `CONFIG_USERSPACE`, and +`app/overlays/ptl/ll_userspace_overlay.conf` enables `CONFIG_SOF_USERSPACE_LL`, so the +low-latency (LL) pipeline, the data-processing (DP) modules, and the module-facing IPC all +execute in MMU-protected user threads. The kernel keeps the OS/HAL, boot, drivers, and the +syscall validators. + +The privilege boundary is real, but nothing in the source *layout* signals which side a given +file belongs to. This document makes that explicit. + +## Execution model + +```mermaid +graph TD + subgraph Kernel [Kernel / supervisor context - ring 0] + Boot[Boot & init] + Drv[Drivers / HAL] + Dispatch[IPC dispatch] + LLk[LL scheduler core + domain setup] + Vrfy["Syscall validators (z_vrfy_*)"] + end + + subgraph User [User context - ring 3, MMU-isolated] + LLu[LL pipeline thread] + DP[DP module threads] + Mods[Audio modules / copiers] + IPCu[Module-facing IPC handler] + end + + Dispatch -->|forwards cmd| IPCu + LLk -->|starts| LLu + LLu -->|"syscalls"| Vrfy + DP -->|"syscalls"| Vrfy + IPCu -->|"syscalls"| Vrfy + Vrfy -->|"z_impl_* (validated)"| Drv +``` + +- **Kernel context** owns hardware, boot, interrupt handling, memory-domain construction, and + the *validating* half of every syscall (`z_vrfy_*`). +- **User context** runs the audio application: LL pipeline processing, DP modules, copiers, + and the module-facing IPC path. It touches kernel resources **only** through syscalls. +- The **only sanctioned crossings** between the two are Zephyr syscalls (see + [The boundary is the syscall set](#the-boundary-is-the-syscall-set)). + +## Directory taxonomy — where code runs + +Every `src/` subdirectory falls into one of four tiers. This is the primary mechanism for +making the split evident: the tier tells you where a directory's code runs before you read a +line of it. Each top-level directory's own `README.md` repeats its tier in a **"Runs in:"** +banner. + +| Tier | Meaning | Directories | +|------|---------|-------------| +| **Kernel-only** | OS integration, HAL, boot, inter-core. Never executes in a user thread. Must not be reached directly from user code — only via a syscall. | `arch/`, `drivers/`, `idc/`, `init/`, `platform/`, `probe/`, `logging/`, `trace/` | +| **Boundary / bridge** | Owns the syscalls and the thread / memory-domain setup. Split internally: some translation units run kernel-side, some user-side (e.g. `handler-kernel.c` vs `handler-user.c`, `zephyr_ll.c` vs `zephyr_ll_user.c`). | `schedule/`, `ipc/`, `library_manager/`, `debug/debug_stream/`, and (in the Zephyr integration tree) `zephyr/syscall/` | +| **Application** | Audio pipeline / component logic that runs in user threads under userspace configs. | `audio/` | +| **Shared library** | Context-agnostic code linked into whichever context calls it. Must stay "user-safe": no privileged operations (no direct MMIO, no spinlocks held across syscalls, no raw kernel object access) so it is safe when pulled into a user build. | `lib/`, `math/`, `module/` | + +Classification is grep-verifiable — see [Verifying the taxonomy](#verifying-the-taxonomy). + +> **`zephyr/syscall/` is the model to grow toward.** The Zephyr integration tree already +> collects the validating half of several syscalls in one place — `zephyr/syscall/alloc.c`, +> `cpu.c`, `vregion.c`, `dai.c`, `sof_dma.c` each hold the `z_vrfy_*` wrappers for their +> subsystem, physically separated from the `z_impl_*` logic (in `zephyr/lib/*.c` and +> `src/lib/*.c`). This is exactly the boundary-tier separation this document promotes; the +> migration opportunity is to move the *still-scattered* validators (IPC, module management, +> library manager, scheduling, debug) toward the same convention. + +> Borderline cases (e.g. parts of `lib/` that host syscall *implementations* such as `dma.c` +> and `dai.c`) behave as boundary code even though the directory is mostly shared-library. When +> a single directory genuinely mixes tiers, its README states the exception explicitly. + +## The boundary is the syscall set + +Zephyr crosses the user/kernel boundary with system calls declared `__syscall` in a header and +implemented as two functions: + +- `z_vrfy_()` — **kernel-side validator**. Runs in supervisor mode, validates every + argument coming from user space (`K_SYSCALL_MEMORY_READ/WRITE`, `K_SYSCALL_OBJ`, + `k_usermode_from/to_copy`), then calls the implementation. This is the trust boundary. +- `z_impl_()` — the actual logic. Callable directly from kernel code (fast path) or from + the validator when the call originates in user space. + +**These pairs are the entire user/kernel API surface.** The full, authoritative map lives in +[`syscalls.h`](syscalls.h) in this directory. Summary by subsystem: + +| Subsystem | Syscalls | Declared in | Validator (`z_vrfy_`) / logic (`z_impl_`) | +|-----------|----------|-------------|-------------------------------------------| +| IPC | `ipc_msg_send`, `ipc_msg_list_remove`, `ipc_msg_reply`, `ipc_compound_pre_start`, `ipc_compound_post_start`, `ipc_wait_for_compound_msg`, `send_resource_notif` | `sof/ipc/msg.h`, `sof/ipc/ipc_reply.h`, `ipc4/handler.h`, `ipc4/notification.h` | scattered: `ipc/ipc-common.c`, `ipc/ipc3/helper.c`, `ipc/ipc4/handler-kernel.c`, `ipc/ipc4/notification.c` | +| Heap alloc | `sof_heap_alloc`, `sof_heap_free` | `rtos/alloc.h` | `zephyr/syscall/alloc.c` / `zephyr/lib/alloc.c` | +| Virtual regions | `vregion_create_map`, `vregion_set_interim`, `vregion_get`, `vregion_put`, `vregion_alloc_align`, `vregion_alloc_coherent_align`, `vregion_free` | `sof/lib/vregion.h` | `zephyr/syscall/vregion.c` / `zephyr/lib/vregion.c` | +| DMA | `sof_dma_get`, `sof_dma_put`, `sof_dma_get_attribute`, `sof_dma_request_channel`, `sof_dma_release_channel`, `sof_dma_config`, `sof_dma_start`, `sof_dma_stop`, `sof_dma_get_status`, `sof_dma_reload`, `sof_dma_suspend`, `sof_dma_resume` | `sof/lib/sof_dma.h` | `zephyr/syscall/sof_dma.c` / `lib/dma.c` | +| DAI | `dai_get_device` | `sof/lib/dai-zephyr.h` | `zephyr/syscall/dai.c` / `lib/dai.c` | +| CPU | `cpu_get_id` | `sof/lib/cpu.h` | `zephyr/syscall/cpu.c` / `zephyr/lib/cpu.c` | +| Module management | `mod_balloc_align`, `mod_alloc_ext`, `mod_free`, `mod_free_all`, `mod_data_blob_handler_new`, `mod_fast_get` | `sof/audio/module_adapter/module/generic.h` | scattered: `audio/module_adapter/module/generic.c` | +| Library manager | `lib_manager_free_module` | `sof/lib_manager.h` | scattered: `library_manager/lib_manager.c` | +| Scheduling | `zephyr_ll_task_sem_alloc`, `zephyr_ll_task_sem_free`, `scheduler_dp_internal_free`, `scheduler_dp_ll_tick` | `sof/schedule/ll_schedule_domain.h`, `sof/schedule/dp_schedule.h` | scattered: `schedule/zephyr_ll.c`, `schedule/zephyr_dp_schedule*.c` | +| Debug | `debug_stream_slot_send_record` | `user/debug_stream_slot.h` | scattered: `debug/debug_stream/debug_stream_slot.c` | + +The `zephyr/syscall/*.c` files hold the validators in one place per subsystem; the rows marked +**scattered** keep their `z_vrfy_` next to the kernel logic and are the migration targets. + +Most syscall declarations are gated by `#if defined(__ZEPHYR__) && defined(CONFIG_SOF_FULL_ZEPHYR_APPLICATION)`; outside that, the same name is a plain inline that calls `z_impl_*` directly (the non-userspace / host-test build). + +## Memory-partition markers + +User threads can only touch memory that is mapped into their memory domain. SOF exposes shared +globals to user threads through Zephyr application memory partitions. The markers live in +[`rtos/userspace_helper.h`](../../../../zephyr/include/rtos/userspace_helper.h): + +| Marker | Partition | Availability | Use for | +|--------|-----------|--------------|---------| +| `APP_TASK_DATA` / `APP_TASK_BSS` | `common_partition` | `CONFIG_USERSPACE` | Data shared with all user-space modules/tasks | +| `APP_SYSUSER_DATA` / `APP_SYSUSER_BSS` | `sysuser_partition` | `CONFIG_SOF_USERSPACE_LL` | Data shared with the system LL user thread | +| `K_APP_DMEM(part)` / `K_APP_BMEM(part)` | caller-defined | `CONFIG_USERSPACE` | A component's own partition (e.g. `ipc_context_part`) | + +When userspace is disabled, all four SOF markers expand to nothing, so annotated globals are +ordinary variables in non-userspace builds. + +**Rule:** *any global that a user thread reads or writes must carry a partition marker at its +declaration site.* Conversely, marked data is **user-writable**, so kernel code must never +trust it — re-validate on every use. See the warning in +[`schedule/zephyr_ll_user.c`](../../../schedule/zephyr_ll_user.c) around `zephyr_ll_heap`, and +`zephyr_ll_user_heap_verify()` which exists precisely because the user-visible heap pointer +cannot be trusted. + +## Kconfig hierarchy + +The userspace behaviour is assembled from a small option tree (source of truth: +[`zephyr/Kconfig`](../../../../zephyr/Kconfig)). + +```mermaid +graph TD + US["USERSPACE (Zephyr MMU)"] + US --> ALLOC[SOF_USERSPACE_INTERFACE_ALLOC] + US --> DMA[SOF_USERSPACE_INTERFACE_DMA] + US --> VREG["SOF_USERSPACE_INTERFACE_VREGION (needs SOF_VREGIONS)"] + US --> LL[SOF_USERSPACE_LL] + LL -.selects.-> ALLOC + LL -.selects.-> DMA + LL -.selects if SOF_VREGIONS.-> VREG + US --> SHEAP[SOF_USERSPACE_USE_SHARED_HEAP] + US --> DHEAP[SOF_USERSPACE_USE_DRIVER_HEAP] + PROXY[SOF_USERSPACE_PROXY] + PROXY -.selects.-> SHEAP + PROXY -.selects.-> DHEAP + PROXY --> MODIPC[SOF_USERSPACE_MOD_IPC_BY_DP_THREAD] + APP["SOF_USERSPACE_APPLICATION (auto = USERSPACE && !PROXY, needs SOF_VREGIONS)"] +``` + +Key options: + +- **`SOF_USERSPACE_LL`** — run LL pipelines in user threads (`depends on USERSPACE`; selects + the ALLOC/DMA/VREGION interfaces). This is what the PTL overlay turns on. +- **`SOF_USERSPACE_PROXY`** — run modules inside a proxy container that forwards kernel-side + calls to user-privileged module code (selects the driver + shared heaps). +- **`SOF_USERSPACE_APPLICATION`** — not user-settable; a shortcut for + `USERSPACE && !SOF_USERSPACE_PROXY` (`depends on SOF_VREGIONS`), used to switch the DP + scheduler between the `_application` and `_thread` implementations in + [`schedule/CMakeLists.txt`](../../../schedule/CMakeLists.txt). +- **`SOF_USERSPACE`** — a separate WIP flag for userspace *modules*; do not confuse with + `USERSPACE` (the Zephyr option) or `SOF_USERSPACE_LL`. + +## Rules for placing new code + +1. **Pick the tier first.** OS/HAL/boot → a kernel-only directory. Audio application logic → + `audio/`. Reusable pure code → a shared-library directory (keep it user-safe). +2. **Cross the boundary only via a syscall.** Add a `__syscall` declaration, put the + `z_vrfy_` validator in the boundary tier — preferably in `zephyr/syscall/.c` + alongside the existing ones — validate every argument, and list the new call in + [`syscalls.h`](syscalls.h). +3. **Mark shared globals.** Any global a user thread reads/writes gets `APP_TASK_*`, + `APP_SYSUSER_*`, or a `K_APP_*` partition marker — and the kernel re-validates it. +4. **Grant kernel objects explicitly.** User threads need `k_object_access_grant()` / + `k_thread_access_grant()` for each kernel object they use. +5. **Split by suffix inside boundary directories.** Where a subsystem has both sides, keep the + existing `-kernel` / `-user` file split so the two contexts stay physically separate. + +## Verifying the taxonomy + +The tier of any directory can be confirmed from its source signals: + +```sh +# Boundary code: declares or implements syscalls +grep -rl 'z_vrfy_\|__syscall' src/ + +# User-visible data: partition markers +grep -rl 'APP_SYSUSER\|APP_TASK\|K_APP_BMEM\|K_APP_DMEM\|K_APPMEM_PARTITION' src/ + +# User threads / object grants / domains +grep -rl 'K_USER\|_access_grant\|mem_domain' src/ +``` + +A kernel-only directory returns nothing for all three. A boundary directory hits the first (and +usually the third). The application/shared tiers hit markers but should not host `z_vrfy_` +validators. + +## Related files + +- [`syscalls.h`](syscalls.h) — the authoritative syscall map. +- [`zephyr/include/rtos/userspace_helper.h`](../../../../zephyr/include/rtos/userspace_helper.h) + — partition markers and user-space helper API. +- [`schedule/README.md`](../../../schedule/README.md), + [`ipc/README.md`](../../../ipc/README.md), + [`audio/module_adapter/README.md`](../../../audio/module_adapter/README.md) — boundary + subsystems. +- [`AGENTS.md`](../../../../AGENTS.md) — contributor rules that reference this document. diff --git a/src/include/sof/userspace/syscalls.h b/src/include/sof/userspace/syscalls.h new file mode 100644 index 000000000000..fd925e28d382 --- /dev/null +++ b/src/include/sof/userspace/syscalls.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. + */ + +/** + * \file + * \brief Map of the SOF user/kernel system-call boundary. + * + * This header is a documentation-only index of every SOF system call — the + * complete user/kernel API surface. It does not declare or include anything; + * it exists so that the boundary between unprivileged (user) audio code and + * privileged (kernel) OS/HAL code can be read in one place. + * + * For the architecture, directory taxonomy, memory-partition markers and + * Kconfig hierarchy that this boundary sits in, see the companion + * \c README.md in this directory. + * + * ## What a SOF syscall looks like + * + * Each entry below is declared \c __syscall in the header named in its group + * and implemented as a pair: + * + * - \c z_vrfy_() — kernel-side validator. Runs in supervisor mode, + * validates every user-supplied argument (K_SYSCALL_MEMORY_READ/WRITE, + * K_SYSCALL_OBJ, k_usermode_from/to_copy), then calls the implementation. + * This is the trust boundary. + * - \c z_impl_() — the actual logic; callable directly from kernel + * code (fast path) or from the validator when the call comes from user + * space. + * + * The validators for the lib-level calls are collected under + * \c zephyr/syscall/ (alloc.c, cpu.c, vregion.c, dai.c, sof_dma.c); the + * remaining validators still live next to their kernel logic and are the + * migration targets called out in the README. + * + * NOTE: keep this list in sync with the actual declarations. It can be + * regenerated / checked with: + * \code + * grep -rn '__syscall' src/include zephyr/include + * \endcode + * + * ------------------------------------------------------------------------ + * + * ## IPC — inter-processor communication + * Declared: sof/ipc/msg.h, sof/ipc/ipc_reply.h, ipc4/handler.h, + * ipc4/notification.h + * Kernel side: src/ipc/ipc-common.c, src/ipc/ipc3/helper.c, + * src/ipc/ipc4/handler-kernel.c, src/ipc/ipc4/notification.c + * + * void ipc_msg_send(struct ipc_msg *msg, void *data, bool high_priority); + * void ipc_msg_list_remove(struct ipc_msg *msg); + * void ipc_msg_reply(struct sof_ipc_reply *reply); + * void ipc_compound_pre_start(int msg_id); + * void ipc_compound_post_start(uint32_t msg_id, int ret, bool delayed); + * int ipc_wait_for_compound_msg(void); + * bool send_resource_notif(uint32_t resource_id, uint32_t event_type, + * uint32_t data); + * + * ## Heap allocation + * Declared: rtos/alloc.h + * Kernel side: zephyr/syscall/alloc.c (z_vrfy_) / zephyr/lib/alloc.c (z_impl_) + * + * void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + * size_t align); + * void sof_heap_free(struct k_heap *heap, void *addr); + * + * ## Virtual regions (vregion allocator) + * Declared: sof/lib/vregion.h + * Kernel side: zephyr/syscall/vregion.c (z_vrfy_) / zephyr/lib/vregion.c + * + * struct vregion *vregion_create_map(uintptr_t *vreg_start, size_t *vreg_size); + * void vregion_set_interim(struct vregion *vr); + * struct vregion *vregion_get(struct vregion *vr); + * struct vregion *vregion_put(struct vregion *vr); + * void *vregion_alloc_align(struct vregion *vr, size_t size, size_t alignment); + * void *vregion_alloc_coherent_align(struct vregion *vr, size_t size, + * size_t alignment); + * void vregion_free(struct vregion *vr, void *ptr); + * + * ## DMA + * Declared: sof/lib/sof_dma.h + * Kernel side: zephyr/syscall/sof_dma.c (z_vrfy_) / src/lib/dma.c (z_impl_) + * + * struct sof_dma *sof_dma_get(uint32_t dir, uint32_t caps, uint32_t dev, + * uint32_t flags); + * void sof_dma_put(struct sof_dma *dma); + * int sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value); + * int sof_dma_request_channel(struct sof_dma *dma, uint32_t stream_tag); + * void sof_dma_release_channel(struct sof_dma *dma, uint32_t channel); + * int sof_dma_config(struct sof_dma *dma, uint32_t channel, ...); + * int sof_dma_start(struct sof_dma *dma, uint32_t channel); + * int sof_dma_stop(struct sof_dma *dma, uint32_t channel); + * int sof_dma_get_status(struct sof_dma *dma, uint32_t channel, + * struct dma_status *stat); + * int sof_dma_reload(struct sof_dma *dma, uint32_t channel, size_t size); + * int sof_dma_suspend(struct sof_dma *dma, uint32_t channel); + * int sof_dma_resume(struct sof_dma *dma, uint32_t channel); + * + * ## DAI + * Declared: sof/lib/dai-zephyr.h + * Kernel side: zephyr/syscall/dai.c (z_vrfy_) / src/lib/dai.c (z_impl_) + * + * const struct device *dai_get_device(enum sof_ipc_dai_type type, uint32_t index); + * + * ## CPU + * Declared: sof/lib/cpu.h + * Kernel side: zephyr/syscall/cpu.c (z_vrfy_) / zephyr/lib/cpu.c (z_impl_) + * + * int cpu_get_id(void); + * + * ## Module management (module adapter) + * Declared: sof/audio/module_adapter/module/generic.h + * Kernel side: src/audio/module_adapter/module/generic.c + * + * void *mod_balloc_align(struct processing_module *mod, size_t size, + * size_t alignment); + * void *mod_alloc_ext(struct processing_module *mod, uint32_t flags, + * size_t size, size_t align); + * int mod_free(struct processing_module *mod, const void *ptr); + * void mod_free_all(struct processing_module *mod); + * struct comp_data_blob_handler *mod_data_blob_handler_new(struct processing_module *mod); + * const void *mod_fast_get(struct processing_module *mod, + * const void * const dram_ptr, size_t size); + * + * ## Library manager + * Declared: sof/lib_manager.h + * Kernel side: src/library_manager/lib_manager.c + * + * int lib_manager_free_module(const uint32_t component_id); + * + * ## Scheduling (LL and DP) + * Declared: sof/schedule/ll_schedule_domain.h, sof/schedule/dp_schedule.h + * Kernel side: src/schedule/zephyr_ll.c, + * src/schedule/zephyr_dp_schedule.c, + * src/schedule/zephyr_dp_schedule_application.c + * + * int zephyr_ll_task_sem_alloc(struct task *task); + * int zephyr_ll_task_sem_free(struct task *task); + * void scheduler_dp_internal_free(struct task *task); + * void scheduler_dp_ll_tick(void); + * + * ## Debug + * Declared: user/debug_stream_slot.h + * Kernel side: src/debug/debug_stream/debug_stream_slot.c + * + * int debug_stream_slot_send_record(struct debug_stream_record *rec); + */ + +#ifndef __SOF_USERSPACE_SYSCALLS_H__ +#define __SOF_USERSPACE_SYSCALLS_H__ + +/* + * Intentionally empty. This file documents the syscall boundary; the actual + * declarations live in the per-subsystem headers listed above so that each + * translation unit pulls in only what it needs. + */ + +#endif /* __SOF_USERSPACE_SYSCALLS_H__ */ From 1b7d7c6d60c9df023782959c97b9d89ffc74a080 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 29 Sep 2026 15:11:14 +0300 Subject: [PATCH 2/3] readme: add user/kernel "Runs in" tier banners to src directories Make the user/kernel split evident at the point of use: each top-level src/ directory README now opens with a "Runs in:" banner declaring its tier (kernel-only / boundary / application / shared-library) and linking to the canonical reference in src/include/sof/userspace/README.md. Add banners to existing READMEs (schedule, ipc, ipc4, module_adapter, init, module) and add short new READMEs for directories that lacked one (audio, drivers, platform, arch, idc, lib, math). lib/ is flagged as shared-library with a boundary exception, since dma.c and dai.c host syscall implementations. No functional change; documentation only. Signed-off-by: Kai Vehmanen --- src/arch/README.md | 8 ++++++++ src/audio/README.md | 18 ++++++++++++++++++ src/audio/module_adapter/README.md | 6 ++++++ src/drivers/README.md | 11 +++++++++++ src/idc/README.md | 9 +++++++++ src/init/README.md | 4 ++++ src/ipc/README.md | 6 ++++++ src/ipc/ipc4/README.md | 5 +++++ src/lib/README.md | 14 ++++++++++++++ src/math/README.md | 10 ++++++++++ src/module/README.md | 5 +++++ src/platform/README.md | 10 ++++++++++ src/schedule/README.md | 7 +++++++ 13 files changed, 113 insertions(+) create mode 100644 src/arch/README.md create mode 100644 src/audio/README.md create mode 100644 src/drivers/README.md create mode 100644 src/idc/README.md create mode 100644 src/lib/README.md create mode 100644 src/math/README.md create mode 100644 src/platform/README.md diff --git a/src/arch/README.md b/src/arch/README.md new file mode 100644 index 000000000000..df7999635033 --- /dev/null +++ b/src/arch/README.md @@ -0,0 +1,8 @@ +# Architecture Support (`src/arch`) + +> **Runs in:** Kernel-only — CPU-architecture primitives (Xtensa) execute in supervisor context +> and are never linked into a user thread. See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory contains architecture-specific code. It sits below the RTOS and audio +application and has no user-space entry points. diff --git a/src/audio/README.md b/src/audio/README.md new file mode 100644 index 000000000000..5cad0d540b2e --- /dev/null +++ b/src/audio/README.md @@ -0,0 +1,18 @@ +# Audio Components (`src/audio`) + +> **Runs in:** Application — under userspace configs (e.g. PTL with `CONFIG_SOF_USERSPACE_LL`), +> the pipeline, buffers, copiers and processing components in this tree run in MMU-isolated +> **user** threads. They reach kernel resources only through the SOF syscalls. See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory holds the SOF audio application: the pipeline graph and scheduling glue, host/DAI +copiers, and the ~36 processing components/modules (volume, EQ, SRC, mixers, codecs, etc.). This +is the code the user/kernel split is designed to protect the rest of the firmware from. + +Notable subdirectories carry their own README with a matching "Runs in:" banner, e.g. +[`module_adapter/`](module_adapter/README.md), which is the boundary container that launches +modules in user context. + +When adding a component here, keep it inside the application tier: allocate through the module +allocation syscalls (`mod_*`), mark any globals shared with the kernel using the partition +markers, and never call kernel-only subsystems (`drivers/`, `platform/`, `idc/`) directly. diff --git a/src/audio/module_adapter/README.md b/src/audio/module_adapter/README.md index c642c46096bd..e87eda3f83cf 100644 --- a/src/audio/module_adapter/README.md +++ b/src/audio/module_adapter/README.md @@ -1,5 +1,11 @@ # Module Adapter Architecture +> **Runs in:** Boundary / bridge — the module adapter is the container that runs audio modules +> in **user** threads (DP threads, or the userspace proxy in `library/userspace_proxy.c`) while +> being driven from the **kernel** pipeline. It hosts the `mod_*` allocation syscalls and sets +> up per-module memory domains and object grants. See +> [User/kernel split](../../include/sof/userspace/README.md). + This directory contains the Module Adapter. ## Overview diff --git a/src/drivers/README.md b/src/drivers/README.md new file mode 100644 index 000000000000..546a4a41994d --- /dev/null +++ b/src/drivers/README.md @@ -0,0 +1,11 @@ +# Device Drivers (`src/drivers`) + +> **Runs in:** Kernel-only — hardware/HAL access runs in supervisor context and is never +> executed from a user thread. User-space audio code reaches driver functionality only through +> SOF syscalls (e.g. the `sof_dma_*` and `dai_get_device` calls). See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory contains low-level device drivers and hardware abstraction. Because it touches +MMIO, interrupts and other privileged resources, nothing here is mapped into a user memory +domain. If user-space code needs a driver operation, expose it through a validated syscall in +the boundary tier rather than linking this code into a user thread. diff --git a/src/idc/README.md b/src/idc/README.md new file mode 100644 index 000000000000..401f1daed747 --- /dev/null +++ b/src/idc/README.md @@ -0,0 +1,9 @@ +# Inter-Domain / Inter-Core Communication (`src/idc`) + +> **Runs in:** Kernel-only — cross-core messaging (`idc.c`, `zephyr_idc.c`) runs in supervisor +> context. User-space audio code triggers cross-core work indirectly through higher-level +> subsystems, not by calling IDC directly. See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory implements the mechanism cores use to signal and hand work to each other. It is +part of the OS-integration layer, not the audio application. diff --git a/src/init/README.md b/src/init/README.md index 1df29a7b6223..f577c472c0cc 100644 --- a/src/init/README.md +++ b/src/init/README.md @@ -1,5 +1,9 @@ # DSP Initialization (`src/init`) +> **Runs in:** Kernel-only — boot and early bring-up run in supervisor context before any user +> thread exists; this code never executes in a user thread. See +> [User/kernel split](../include/sof/userspace/README.md). + The `src/init` directory contains the generic digital signal processor (DSP) initialization code and firmware metadata structures for Sound Open Firmware (SOF). It acts as the bridge between the underlying RTOS (Zephyr) boot phase and the SOF-specific task scheduling and processing pipelines. ## Architecture and Boot Flow diff --git a/src/ipc/README.md b/src/ipc/README.md index 7389e6ae4a54..f2a13ff1ce57 100644 --- a/src/ipc/README.md +++ b/src/ipc/README.md @@ -1,5 +1,11 @@ # Inter-Processor Communication (IPC) Core Architecture +> **Runs in:** Boundary / bridge — the kernel receives and dispatches IPC, then forwards +> module-facing commands to a user thread. The split is visible in the file names +> (`ipc4/handler-kernel.c` vs `ipc4/handler-user.c`, `ipc4/notification-user.c`), and this +> subsystem hosts the `ipc_msg_*`, `ipc_compound_*` and `send_resource_notif` syscalls. See +> [User/kernel split](../include/sof/userspace/README.md). + This directory contains the common foundation for all Inter-Processor Communication (IPC) within the Sound Open Firmware (SOF) project. It bridges the gap between hardware mailbox interrupts and the version-specific (IPC3/IPC4) message handlers. ## Overview diff --git a/src/ipc/ipc4/README.md b/src/ipc/ipc4/README.md index 1b40195085b9..955042576c8a 100644 --- a/src/ipc/ipc4/README.md +++ b/src/ipc/ipc4/README.md @@ -1,5 +1,10 @@ # IPC4 Architecture +> **Runs in:** Boundary / bridge — `handler-kernel.c` runs in the kernel dispatcher; +> `handler-user.c` and `notification-user.c` run in the user thread that services modules. The +> `z_vrfy_`/`z_impl_` pairs for the compound-message and reply syscalls live here. See +> [User/kernel split](../../include/sof/userspace/README.md). + This directory holds the handlers and topology parsing logic for Inter-Processor Communication Version 4. IPC4 introduces a significantly denser, compound-command structure heavily based around the concept of "pipelines" and dynamic "modules" rather than static DSP stream roles. ## Overview diff --git a/src/lib/README.md b/src/lib/README.md new file mode 100644 index 000000000000..5b2c71237cbd --- /dev/null +++ b/src/lib/README.md @@ -0,0 +1,14 @@ +# Common Library (`src/lib`) + +> **Runs in:** Shared library — **with a boundary exception.** Most of this directory +> (`lib.c`, `notifier.c`, `objpool.c`, `clk.c`, `agent.c`, `ams.c`, `cpu-clk-manager.c`) is +> context-agnostic helper code linked into whichever thread calls it and must stay user-safe. +> However, `dma.c` and `dai.c` host the **kernel-side implementations** (`z_impl_sof_dma_*`, +> `z_impl_dai_get_device`) of syscalls whose validators live in `zephyr/syscall/`. Treat those +> two files as boundary/kernel code. See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory collects cross-cutting helpers that don't belong to a single subsystem. When +adding code here, decide first whether it is shared-library (user-safe, no privileged ops) or a +syscall implementation (kernel-only, reached from user space through a `z_vrfy_` validator), and +keep the two kinds in separate files as `dma.c`/`dai.c` already do. diff --git a/src/math/README.md b/src/math/README.md new file mode 100644 index 000000000000..2b9adc030de8 --- /dev/null +++ b/src/math/README.md @@ -0,0 +1,10 @@ +# Math Library (`src/math`) + +> **Runs in:** Shared library — DSP math (FIR/IIR filters, FFT, trig, decibels, etc.) is pure, +> context-agnostic code linked into whichever thread calls it, including user-space audio +> components. It must stay "user-safe": no privileged operations, no direct hardware or kernel +> object access. See [User/kernel split](../include/sof/userspace/README.md). + +This directory provides the numerical routines used by audio components. Because it is pulled +into user-space builds, keep additions free of anything that would require supervisor +privileges; if a routine needs a kernel service, the caller must obtain it through a syscall. diff --git a/src/module/README.md b/src/module/README.md index 3d79c948bdd8..d2823f156754 100644 --- a/src/module/README.md +++ b/src/module/README.md @@ -1,5 +1,10 @@ # Audio Processing Modules (`src/module`) +> **Runs in:** Shared library — this directory defines the processing-module API/ABI. It is +> context-agnostic: the same interface is used whether a module is a statically linked core +> component or a user-space LLEXT module, so it must stay free of privileged operations. See +> [User/kernel split](../include/sof/userspace/README.md). + The `src/module` directory and the `src/include/module` headers define the Sound Open Firmware (SOF) modern Audio Processing Module API. This architecture abstracts the underlying OS and pipeline scheduler implementations from the actual audio signal processing logic, allowing modules to be written once and deployed either as statically linked core components or as dynamically loadable Zephyr EXT (LLEXT) modules. ## Architecture Overview diff --git a/src/platform/README.md b/src/platform/README.md new file mode 100644 index 000000000000..da20eb6569d2 --- /dev/null +++ b/src/platform/README.md @@ -0,0 +1,10 @@ +# Platform Support (`src/platform`) + +> **Runs in:** Kernel-only — platform bring-up, memory maps, interrupt wiring and IPC mailbox +> windows are supervisor-context concerns and never run in a user thread. See +> [User/kernel split](../include/sof/userspace/README.md). + +This directory contains SoC/platform-specific integration (e.g. Intel ACE, MediaTek, i.MX), +including the Kconfig that selects the build target such as `PANTHERLAKE` (PTL). It configures +the hardware the audio application later runs on top of, but is not itself part of the +user-space application. diff --git a/src/schedule/README.md b/src/schedule/README.md index 6c919ae96a19..40028caeacb9 100644 --- a/src/schedule/README.md +++ b/src/schedule/README.md @@ -1,5 +1,12 @@ # SOF Scheduling Architecture +> **Runs in:** Boundary / bridge — this directory owns the LL/DP schedulers and their thread + +> memory-domain setup. It is split internally between kernel-side files (`zephyr_ll.c`, +> `zephyr_domain.c`, `zephyr_dp_schedule_thread.c`) and user-side files (`zephyr_ll_user.c`, +> `zephyr_ll_app.c`, `zephyr_dp_schedule_application.c`), and it hosts the +> `zephyr_ll_task_sem_*` and `scheduler_dp_*` syscalls. See +> [User/kernel split](../include/sof/userspace/README.md). + This directory (`src/schedule`) contains the Sound Open Firmware (SOF) scheduling infrastructure, deeply integrated with the underlying Zephyr RTOS. SOF utilizes a multi-tiered scheduling approach to cater to different real-time constraints, ranging from hard real-time, low-latency requirements to more relaxed, compute-intensive data processing tasks. ## Overview of Schedulers From f6f9047ff32869f73d6573978ba7a569fa3471a8 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 29 Sep 2026 15:11:26 +0300 Subject: [PATCH 3/3] docs: AGENTS.md: document the user/kernel boundary rules Add a "User/kernel boundary" subsection under Development Standards that points contributors to src/include/sof/userspace/README.md and states the requirements for new code: place it in the correct directory tier, cross the boundary only via a validated syscall, and mark any globals shared with user threads with a partition marker. Signed-off-by: Kai Vehmanen --- AGENTS.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 462c39f0d343..2625f00fbfae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,3 +20,23 @@ This document outlines the rules AI agents must follow when checking out branche ### Codestyle and Linting * **Standard:** Use `clangd` instead of `checkpatch` for codestyle verification. * **Rationale:** `checkpatch` is prone to confusion with assembly and non-standard C; `clangd` provides better integration with IDEs and AI tools and is easier to maintain. + +### User/kernel boundary +On PTL and other userspace configurations, audio application logic runs in Zephyr user-space +while the OS/HAL stays in the kernel. The authoritative model, directory taxonomy, syscall map, +memory-partition markers and Kconfig hierarchy are documented in +[`src/include/sof/userspace/README.md`](src/include/sof/userspace/README.md). When adding or +moving code, follow it: + +* **Place code in the right tier.** OS/HAL/boot/inter-core → a kernel-only directory + (`arch/`, `drivers/`, `idc/`, `init/`, `platform/`, `probe/`, `logging/`, `trace/`); audio + application logic → `audio/`; reusable pure code → a shared-library directory (`lib/`, + `math/`, `module/`) kept free of privileged operations. Each top-level directory's + `README.md` states its tier in a "Runs in:" banner — keep it accurate. +* **Cross the boundary only via a syscall.** Add a `__syscall` declaration, put the `z_vrfy_` + validator in the boundary tier (preferably `zephyr/syscall/.c`), validate every + user-supplied argument, and add the call to + [`src/include/sof/userspace/syscalls.h`](src/include/sof/userspace/syscalls.h). +* **Mark shared globals.** Any global a user thread reads or writes must carry a partition + marker (`APP_TASK_*`, `APP_SYSUSER_*`, or a `K_APP_*` partition) at its declaration site, and + kernel code must re-validate such data because it is user-writable.