Conversation
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 <kai.vehmanen@linux.intel.com>
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 <kai.vehmanen@linux.intel.com>
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 <kai.vehmanen@linux.intel.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Several central architecture claims, file classifications, and syscall signatures do not match the current source and configuration.
Review effort: Balanced
Findings: 9
Open (9)
Default PTL configuration does not enable the LL userspace pipeline · New Execution model omits shared-state and synchronization IPC crossings · New Taxonomy rollout is incomplete across source directories · New DMA table omits inline implementations in the authoritative header · New send_resource_notif signature omits required payload arguments · New DMA implementation map omits inline header implementations · New Secondary user IPC initialization can occur after user threads start · New Privileged lock paths are incorrectly classified as context-agnostic · New Kernel file classification omits user-capable scheduling paths · New
What changed in this PR
Documents SOF’s Zephyr user/kernel boundary and adds contributor guidance for preserving it.
Changes:
- Defines execution tiers, userspace configuration, memory partitions, and placement rules.
- Adds a centralized syscall-boundary index.
- Adds “Runs in” banners and agent guardrails across subsystems.
| File | Description |
|---|---|
AGENTS.md |
Adds user/kernel contribution rules. |
src/arch/README.md |
Classifies architecture support. |
src/audio/README.md |
Describes application-tier audio code. |
src/audio/module_adapter/README.md |
Classifies the module adapter boundary. |
src/drivers/README.md |
Documents driver privilege requirements. |
src/idc/README.md |
Classifies inter-core communication. |
src/include/sof/userspace/README.md |
Defines the overall userspace architecture. |
src/include/sof/userspace/syscalls.h |
Indexes SOF syscalls. |
src/init/README.md |
Classifies initialization code. |
src/ipc/README.md |
Documents IPC boundary behavior. |
src/ipc/ipc4/README.md |
Documents the IPC4 split. |
src/lib/README.md |
Classifies common-library code. |
src/math/README.md |
Documents user-safe math routines. |
src/module/README.md |
Classifies the module API. |
src/platform/README.md |
Classifies platform integration. |
src/schedule/README.md |
Documents scheduler execution contexts. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| 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 **only sanctioned crossings** between the two are Zephyr syscalls (see | ||
| [The boundary is the syscall set](#the-boundary-is-the-syscall-set)). |
| 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. |
| | 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` | |
| * bool send_resource_notif(uint32_t resource_id, uint32_t event_type, | ||
| * uint32_t data); |
| * | ||
| * ## DMA | ||
| * Declared: sof/lib/sof_dma.h | ||
| * Kernel side: zephyr/syscall/sof_dma.c (z_vrfy_) / src/lib/dma.c (z_impl_) |
| > **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 |
| > **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. |
| > 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 |
| **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). | ||
|
|
There was a problem hiding this comment.
non-userspace Zephyr SOF builds still use __syscall declarations, only non-Zephyr builds and ztest builds use inline defines
| 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: |
There was a problem hiding this comment.
a C header that is never included and is only used for documentation? Not sure I'm a fan... :)
|
|
||
| | Marker | Partition | Availability | Use for | | ||
| |--------|-----------|--------------|---------| | ||
| | `APP_TASK_DATA` / `APP_TASK_BSS` | `common_partition` | `CONFIG_USERSPACE` | Data shared with all user-space modules/tasks | |
There was a problem hiding this comment.
so far this partition isn't accessible to DP modules in "application" mode AFAIK
| | 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 | |
| |--------|-----------|--------------|---------| | ||
| | `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`) | |
There was a problem hiding this comment.
not sure what is meant by this? Which component would want this? Is there an API for a component to call to map a partition to it? Not sure I understand. I see one use example with ipc_context_part but there the kernel-mode IPC part adds it to the userspace IPC domain. A "normal" user component won't have a kernel mode part to do that.
| 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. |
|
|
||
| # User threads / object grants / domains | ||
| grep -rl 'K_USER\|_access_grant\|mem_domain' src/<dir> | ||
| ``` |
There was a problem hiding this comment.
hmmm... in other words "Use the Source, Luke!" ;-)
| [`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. |
There was a problem hiding this comment.
what does this mean? Are only AI contributions allowed to this document? ;-)
|
|
||
| /** | ||
| * \file | ||
| * \brief Map of the SOF user/kernel system-call boundary. |
There was a problem hiding this comment.
not a fan of this file, sorry

Series of documentation patches to document the overall approach to user-space usage in SOF. This covers the main build options, adds a new central syscalls.h header for documentation and updates agent guardrails for new code.
Moving source files around was considered, but this is not yet done in this PR. As SOF supports multiple approaches: all-kernel (no MMU/MPU), mixed (single audio modules isolated) and app-code-all-in-user, for most source code files, the user/kernel split is not always the same. Starting with a documentation framework seems like the best approach to get started.