-
Notifications
You must be signed in to change notification settings - Fork 349
lib: dma: add support for user-space usage #10341
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
Conversation
1043d83 to
178df79
Compare
|
FYI @teburd @softwarecki |
lgirdwood
left a comment
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.
I think this looks good, the dma config part probably needs some refinement to minimise data passed to configure the channel and we need a ID instead of a pointer to pass to the kernel dma structure
src/include/sof/sof_syscall.h
Outdated
| __syscall uint32_t sof_local_lock(void); | ||
| __syscall void sof_local_unlock(uint32_t flags); | ||
|
|
||
| __syscall struct sof_dma *sof_dma_get(uint32_t dir, uint32_t caps, uint32_t dev, uint32_t flags); |
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.
I assume these are already exported for clients in dma.h with doxygen API. Btw, might be worth spelling out that the sys calls are for SOF infra usage and not intended for direct module usage as a comment.
| } | ||
| #include <zephyr/syscalls/sof_dma_release_channel_mrsh.c> | ||
|
|
||
| static inline int z_vrfy_sof_dma_config(struct sof_dma *dma, uint32_t channel, |
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.
So this is the tricky one @kv2019i @teburd.
We really have to be minimal data here for our end goal for both channel configuration and buffer descriptor configuration. e.g. (buffer config example - inspiration from DW HW LLI)
struct dma_desc {
void *src;
void *dest;
size_t bytes; // copy size for this descritptor
uint32_t flags; // should be generic - things like direction
struct dma_desc *next; // next desc, NULL if last - can also be cyclic
};This should allows us to specify our buffers, where we can then do
for_each_desc {
if (!k_thread_can access(desc->src) || !k_thread_can_access(desc->src + desc->size))
return -EINVAL;
//... check dest and next desc ptr
}where we need a k_thread_can_access() type API call that can lookup thread domain page tables.
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 I can take a stab at this. There's basicly two approaches that both seem possible:
- take the Zephyr DMA API and filter out forbidden fields in the syscall handler (if user-kernel split is enabled), or
- define new types that offer more limited interface and use that interface in the user-kernel interface (as per your comment above)
I agree (2) seems better in terms of longterm maintainance of the syscall handler code. The downside is that we then limit the DMA interface for all SOF users in dai/host-zephyr.c, whether they use user-space or not. But maybe this is still better in the end. We can cover all existing usage with the "safe wrappers" and if there are more needs in the future, the interface can be extended. In worst case, there's always the possibility to use a different implementation of host/dai modules. Less code reuse but less restrictions.
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 hit some roadblocks with this approach. Looking at dai-zephyr.c usage of this interface, I see the Zephyr DMA interface features are more widely used with the DAI DMAs (e.g. support for NXP DMAs uses more of Zephyr DMA features). So after I extended my "safe" variants to cover everything needed for host-zephyr.c and dai-zephyr. c, I'm basicly left with replicas of the Zephry dma.h structs, sans a few fields (for kernel callbacks). I don't think works out well.
Also dai-zephyr.c really uses scatter-gather (unlike host-zephyr.c and other simpler DMA users in SOF), so there are actually multiple descriptors. We can't trust the descriptor objects in user context (Tom commented about this earlier and matches guidance). So need to think how to copy over the descriptors to kernel space, and then pass to the driver.
Back to thinking hat with this one.
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.
In latest 14th Nov series of this PR, I now solved this by:
- still using the full Zephyr DMA interface
- do a full copy (including a deepcopy of the scatter-gather list) in kernel space, and only copy fields supported when using user-space DMA
- i.e. kernel doesn't try to verify all fields, but instead only uses fields it trusts and knows about. if new fields are added (to upstrema Zephyr DMA), they need to be explicitly added here in order to pass the new field/information to DMA drivers
zephyr/test/userspace/dma.c
Outdated
| static struct k_thread user_thread; | ||
| static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE); | ||
|
|
||
| static void user_function(void *p1, void *p2, void *p3) |
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.
Would be good to say what this test does e.g. userspace thread that copies memory from userspace buffers.
178df79 to
56d1e6b
Compare
|
V2:
|
|
V3:
|
|
Example test output on PTL with this PR: |
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.
Pull Request Overview
This PR adds support for user-space threads to operate DMA streams through a syscall layer, enabling safer isolation of DMA operations while maintaining compatibility with existing SOF targets that don't use user-space.
Key changes:
- Introduced new syscall interface for DMA operations with kernel-side validation
- Added standalone boot test infrastructure that can run complex tests using IPC
- Converted existing DMA function calls to use the new syscall-based API
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| zephyr/test/userspace/test_intel_hda_dma.c | New user-space test case for Intel HDA DMA functionality with kernel/user thread coordination |
| zephyr/test/userspace/README.md | Documentation for building and running the Intel HDA DMA user-space test |
| zephyr/test/CMakeLists.txt | Build configuration to include user-space DMA test when appropriate configs are enabled |
| zephyr/syscall/sof_dma.c | Implementation of syscall verification handlers with access validation and memory permission checks |
| zephyr/include/sof/lib/dma.h | Updated function declarations to use z_impl_ prefix for syscall implementation pattern |
| zephyr/Kconfig | New configuration option for standalone boot tests that interfere with normal SOF operation |
| zephyr/CMakeLists.txt | Build integration for syscall sources and header registration |
| src/lib/dma.c | Renamed DMA functions to z_impl_ pattern and added new wrapper functions for syscall interface |
| src/ipc/ipc-common.c | Added early return in IPC initialization when running standalone tests |
| src/include/sof/sof_syscall.h | New header defining syscall signatures for DMA operations |
| src/audio/host-zephyr.c | Updated all DMA API calls to use new sof_dma_* function names |
| app/userspace_overlay.conf | Configuration overlay enabling user-space and custom syscalls |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static inline struct dma_block_config *deep_copy_dma_blk_cfg_list(struct dma_config *cfg) | ||
| { | ||
| struct dma_block_config *kern_cfg = | ||
| rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); |
Copilot
AI
Nov 14, 2025
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.
Memory allocation for kern_cfg is not checked for NULL before use. If rmalloc fails, subsequent operations at lines 106-138 will cause undefined behavior. Add a null check immediately after allocation and return NULL if allocation fails.
| rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); | |
| rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); | |
| if (!kern_cfg) | |
| return NULL; |
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.
It's true kern_cfg is used to initialize other pointers, but it is not dereferences before a NULL check is done, so this is safe.
zephyr/syscall/sof_dma.c
Outdated
| kern_prev = kern_next; | ||
| } |
Copilot
AI
Nov 14, 2025
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.
On the first iteration when kern_prev is NULL, it is never initialized, causing all blocks except the first to be disconnected from the chain. Initialize kern_prev = kern_cfg before the loop or set it to kern_next unconditionally after processing each block.
| kern_prev = kern_next; | |
| } | |
| } | |
| kern_prev = kern_next; |
| LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", err, | ||
| i, stat.pending_length, stat.free); |
Copilot
AI
Nov 14, 2025
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.
The format string placeholders are misaligned with arguments. The first %d should print i, the second should print err, but they appear in the wrong order. The correct format should be: LOG_INF(\"sof_dma_get_status %d: ret %d pend %u free %u\", i, err, stat.pending_length, stat.free);
| LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", err, | |
| i, stat.pending_length, stat.free); | |
| LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", i, | |
| err, stat.pending_length, stat.free); |
src/audio/host-zephyr.c
Outdated
| cb(dev, bytes); | ||
|
|
||
| ret = dma_reload(hd->chan->dma->z_dev, hd->chan->index, 0, 0, bytes); | ||
| ret = sof_dma_reload(hd->chan->dma, hd->chan->index, 0, 0, bytes); |
Copilot
AI
Nov 14, 2025
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.
The call to sof_dma_reload passes 5 arguments, but the syscall signature at line 33 in src/include/sof/sof_syscall.h and implementation at line 189-192 in src/lib/dma.c only accept 3 parameters: dma, channel, and size. Remove the two extra 0, 0 arguments.
| ret = sof_dma_reload(hd->chan->dma, hd->chan->index, 0, 0, bytes); | |
| ret = sof_dma_reload(hd->chan->dma, hd->chan->index, bytes); |
| hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL, | ||
| IPC_TIMEOUT); | ||
|
|
||
| hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, channel, "host reset"); |
Copilot
AI
Nov 14, 2025
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.
The variable channel is used here but is a local variable in the intel_hda_dma_user function and not in scope within intel_hda_dma_kernel. This should likely be TEST_CHANNEL instead.
| hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, channel, "host reset"); | |
| hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset"); |
zephyr/Kconfig
Outdated
| help | ||
| Run extended set of tests at boot that can use IPC and interfere | ||
| with system state. Normal IPC handling of the SOF application | ||
| is disable to allow more complex tests to run. |
Copilot
AI
Nov 14, 2025
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.
Corrected spelling of 'disable' to 'disabled'.
| is disable to allow more complex tests to run. | |
| is disabled to allow more complex tests to run. |
85c9204 to
4d5e735
Compare
|
V4 pushed:
|
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.
Pull Request Overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| EXPORT_SYMBOL(z_impl_sof_dma_get); | ||
| EXPORT_SYMBOL(z_impl_sof_dma_put); |
Copilot
AI
Nov 14, 2025
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.
The remaining DMA API functions (sof_dma_config, sof_dma_start, sof_dma_stop, etc.) should also have EXPORT_SYMBOL declarations if they are intended to be used outside this module, to maintain consistency with the existing exports.
|
Intel Internal CI fails with not related test |
7660775 to
e024fec
Compare
|
V5 pushed:
|
|
@jsarha if you can also take a look. |
src/include/sof/sof_syscall.h
Outdated
|
|
||
| #include <stdint.h> | ||
|
|
||
| #error "should not get here" |
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.
mmh, not sure I understand?
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.
I think this is to catch inclusion on non userspace platforms, but does need more context added.
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.
Oops, this is a transitional file I was supposed to remove once all contents is moved elsewhere. Will address.
zephyr/syscall/sof_dma.c
Outdated
| for (d = info->dma_array; d < info->dma_array + info->num_dmas; d++) { | ||
| if (d == dma) { | ||
| dma_ko = d; | ||
| break; |
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.
nitpick: could just do here if (d == dma && sof_dma_has_access(dma)) return true;
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.
thanks @lyakh , more compact and still readable, will change
zephyr/syscall/sof_dma.c
Outdated
| struct sof_dma *dma = z_impl_sof_dma_get(dir, cap, dev, flags); | ||
|
|
||
| if (sof_dma_has_access(dma)) | ||
| return dma; |
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.
hm, firstly it's a bit unusual I think to check after executing the kernel function? Normally you'd want to prevent the "assuming benevolence" kernel function to execute on unsafe user parameters, but maybe in this case it's justified - the user can only pass integers. But if the user doesn't have access to that, shouldn't we put() before returning NULL?
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.
Good points. The order is on purpose, so let me add an longer inline comment to explain. And yes, the put() was missing, will also added that.
zephyr/syscall/sof_dma.c
Outdated
| if (!kern_cfg) | ||
| return NULL; | ||
|
|
||
| while (user_next) { |
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.
RFC:
for (user_next = cfg->head_block, kern_next = kern_cfg;
user_next;
user_next = user_next->next_block, user_next++)
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.
s/user_next++/kern_next++/ to the last statement, but yeah, easier to read this way, will add.
zephyr/syscall/sof_dma.c
Outdated
| case MEMORY_TO_MEMORY: | ||
| K_OOPS(K_SYSCALL_MEMORY_WRITE((void *)kern_next->dest_address, | ||
| kern_next->block_size)); | ||
| /* fallthrough */ |
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.
COMPILER_FALLTHROUGH
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.
Aa, thanks, changed.
app/userspace_overlay.conf
Outdated
| @@ -0,0 +1,3 @@ | |||
| CONFIG_USERSPACE=y | |||
| CONFIG_APPLICATION_DEFINED_SYSCALL=y | |||
| CONFIG_MAX_THREAD_BYTES=3 | |||
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.
we also have app/overlays/ptl/userspace_overlay.conf
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.
Hmm, I was thinking there's nothing board or Intel specific in this overlay, so it could be added as a top-level overlay. The ptl overlay does seem to use more stuff (that is more board specific), so maybe I'll drop this for now and update the docs to use the baord specific overlay instead.
| */ | ||
| dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); | ||
|
|
||
| #if NEEDS_FAULT_HANDLING |
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.
if NEEDS_FAULT_HANDLING can be undefined, as it seems to be in this version, then better #ifdef
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.
Let me drop this for now, doesn't need to be in this initial PR.
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.
LGTM so far. One comment is that does every permission failure have to be fatal ? i.e. should we return -EPERM instead of oops().
src/include/sof/sof_syscall.h
Outdated
|
|
||
| #include <stdint.h> | ||
|
|
||
| #error "should not get here" |
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.
I think this is to catch inclusion on non userspace platforms, but does need more context added.
src/lib/dma.c
Outdated
| k_spin_unlock(&dma->lock, key); | ||
| } | ||
|
|
||
| int z_impl_sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value) |
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.
optimization for later - these could be static inline in the syscall header ?
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.
True, good point @lgirdwood . I think I was thinking there would be more in the implementation of these functions, but it's not really needed. Let me inline these right away.
| endif() | ||
| zephyr_include_directories(${SOF_PLATFORM_PATH}/${PLATFORM}/include) | ||
|
|
||
| zephyr_library_sources_ifdef(CONFIG_USERSPACE |
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.
I think we now have several config options around userspace, i.e. one for DP only and one for DP + infra.
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 This is the canonical one defined in Zephyr and used all over Zephyr code when defining syscalls (in a way that still builds if user-space is not enabled in the build). I try to follow style and convetions of Zephyr even if these particular syscalls are on SOF side.
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.
got you - so we cant drill down further for the syscalls today
zephyr/syscall/sof_dma.c
Outdated
| if (!info->num_dmas) | ||
| return false; | ||
|
|
||
| for (d = info->dma_array; d < info->dma_array + info->num_dmas; d++) { |
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.
nitpick, if if info->num_dais is constant for the duration I would sum it before the loop.
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.
Will change.
| @@ -0,0 +1,22 @@ | |||
| intel_hda_dma test | |||
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.
Love this, easy to follow and reproduce !
kv2019i
left a comment
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.
Thanks to all reviewers! Will be uploading a new series with comments addressed soonish.
src/include/sof/sof_syscall.h
Outdated
|
|
||
| #include <stdint.h> | ||
|
|
||
| #error "should not get here" |
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.
Oops, this is a transitional file I was supposed to remove once all contents is moved elsewhere. Will address.
src/lib/dma.c
Outdated
| k_spin_unlock(&dma->lock, key); | ||
| } | ||
|
|
||
| int z_impl_sof_dma_get_attribute(struct sof_dma *dma, uint32_t type, uint32_t *value) |
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.
True, good point @lgirdwood . I think I was thinking there would be more in the implementation of these functions, but it's not really needed. Let me inline these right away.
| endif() | ||
| zephyr_include_directories(${SOF_PLATFORM_PATH}/${PLATFORM}/include) | ||
|
|
||
| zephyr_library_sources_ifdef(CONFIG_USERSPACE |
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 This is the canonical one defined in Zephyr and used all over Zephyr code when defining syscalls (in a way that still builds if user-space is not enabled in the build). I try to follow style and convetions of Zephyr even if these particular syscalls are on SOF side.
zephyr/syscall/sof_dma.c
Outdated
| if (!info->num_dmas) | ||
| return false; | ||
|
|
||
| for (d = info->dma_array; d < info->dma_array + info->num_dmas; d++) { |
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.
Will change.
zephyr/syscall/sof_dma.c
Outdated
| for (d = info->dma_array; d < info->dma_array + info->num_dmas; d++) { | ||
| if (d == dma) { | ||
| dma_ko = d; | ||
| break; |
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.
thanks @lyakh , more compact and still readable, will change
zephyr/syscall/sof_dma.c
Outdated
| case MEMORY_TO_MEMORY: | ||
| K_OOPS(K_SYSCALL_MEMORY_WRITE((void *)kern_next->dest_address, | ||
| kern_next->block_size)); | ||
| /* fallthrough */ |
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.
Aa, thanks, changed.
zephyr/syscall/sof_dma.c
Outdated
| if (!kern_cfg) | ||
| return NULL; | ||
|
|
||
| while (user_next) { |
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.
s/user_next++/kern_next++/ to the last statement, but yeah, easier to read this way, will add.
app/userspace_overlay.conf
Outdated
| @@ -0,0 +1,3 @@ | |||
| CONFIG_USERSPACE=y | |||
| CONFIG_APPLICATION_DEFINED_SYSCALL=y | |||
| CONFIG_MAX_THREAD_BYTES=3 | |||
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.
Hmm, I was thinking there's nothing board or Intel specific in this overlay, so it could be added as a top-level overlay. The ptl overlay does seem to use more stuff (that is more board specific), so maybe I'll drop this for now and update the docs to use the baord specific overlay instead.
|
|
||
| LOG_INF("SOF thread %s (%s)", | ||
| k_is_user_context() ? "UserSpace!" : "privileged mode.", | ||
| CONFIG_BOARD_TARGET); |
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.
@lyakh I infcat copied this from your early DP PRs. This is just a convenience to have a log message printed out at start of the thread. Could be dropped as well, but given this is test code, not harmful either. Adds a nice touch to the test execution output as it print outs the board name and includes the magic "UserSpace" bit in there.
| */ | ||
| dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); | ||
|
|
||
| #if NEEDS_FAULT_HANDLING |
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.
Let me drop this for now, doesn't need to be in this initial PR.
e024fec to
6f17a50
Compare
|
V6 pushed:
|
softwarecki
left a comment
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.
Please wrap all userspace-related changes in more specific ifdefs instead of the generic CONFIG_USERSPACE.
zephyr/syscall/sof_dma.c
Outdated
| * use the Zephyr dma.h device handle to check calling | ||
| * thread has access to it | ||
| */ | ||
| return k_object_is_valid(dma->z_dev, K_OBJ_ANY); |
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.
Why not K_OBJ_DRIVER_DMA ?
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.
Good question. The DMA syscall support is being removed in Zephyr, so such definition won't be available soon. See zephyrproject-rtos/zephyr@6b350da
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.
This definition will remain available even after the mentioned changes. It is generated by the gen_kobject_list.py script based on the driver API structure name, in this case struct dma_driver_api. The script removes the _driver_api suffix and then prefixes the name with K_OBJ_DRIVER_.
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.
You are right @softwarecki , changed to K_OBJ_DRIVER_DMA now in V8. Thanks!
zephyr/syscall/sof_dma.c
Outdated
| if (!info->num_dmas) | ||
| return false; | ||
|
|
||
| for (d = info->dma_array; d < array_end; d++) |
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.
I suggest renaming d to something more descriptive, for example entry. Actually, the whole loop is unnecessary. It's enough to check if the DMA address falls within the range info->dma_array to info->dma_array + info->num_dmas.
dma - info->dma_array < info->num_dmas + align check
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.
Ack @softwarecki . I copied the loop from existing SOF DMA code, but right, naming can be better.
I think the loop should be kept. User space can send arbitrary pointers, and we must also ensure the pointer matches start of a full kernel object, in addition to having access. Otherwise userspace can fool kernel to look up "dma->z_dev" from a middle of a struct. So the check has to ensure it is a valid pointer to kernel object, and only then proceed to use the object in kernel.
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.
@softwarecki Sorry, I forgot to change the name. :( Let's see if this version survives the CI runs. I can rename 'd' in a follow-up if no other changes are needed. If there are changes, I'll do the rename.
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.
@softwarecki Now changed the name
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.
uintptr_t offset = (uintptr_t)dma - (uintptr_t)info->dma_array;
if (dma < info->dma_array || dma >= array_end || offset % sizeof(struct sof_dma))
return false;
return sof_dma_has_access(dma);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.
@softwarecki Readability wise I find that harder to follow (but maybe just me) and the modulo-divide is not free. But this does avoid the loop, so let's go with your version, updated in V9.
zephyr/syscall/sof_dma.c
Outdated
| K_OOPS(++i > cfg->block_count); | ||
| K_OOPS(k_usermode_from_copy(kern_next, user_next, sizeof(*kern_next))); |
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.
macro specifically used to terminate the user-thread
So in case of an error, the user thread will be killed and we'll end up with a memory leak from the kernel?
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.
@softwarecki Excellent point. This does not happen now (xtensa will panic the whole system), but indeed wrong to leave this in the code. I addressed this is the new series just uploaded.
6f17a50 to
272cc16
Compare
|
V7 pushed:
|
| /* include definitions from generated file */ | ||
| #include <zephyr/syscalls/sof-dma.h> | ||
|
|
||
| #else /* !CONFIG_SOF_USERSPACE_INTERFACE_DMA */ |
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.
@softwarecki @lgirdwood I now added a separate build kconfig CONFIG_SOF_USERSPACE_INTERFACE_DMA to build with CONFIG_USERSPACE but without sof-dma.
This does come with a cost that is visible (above and below this comment). When using CONFIG_USERSPACE, Zephyr autogenerates you the code to handle both user and kernel usages (zephyr/syscall/sof-dma.h). If we don't use this generic infra in our headers that expose syscalls, we need to reimplement our own wrappers like I do here. Not a deal breaker, it just looks a bit more complicated (and different than most Zephyr syscall headers). FYI, just wanted to explain why I didn't want to do this in the first place.
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.
Thanks @kv2019i - I think we have to align with Zephyr, but add the configuraton flexibility on the SOF side.
| endif() | ||
| zephyr_include_directories(${SOF_PLATFORM_PATH}/${PLATFORM}/include) | ||
|
|
||
| zephyr_library_sources_ifdef(CONFIG_USERSPACE |
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.
got you - so we cant drill down further for the syscalls today
| /* include definitions from generated file */ | ||
| #include <zephyr/syscalls/sof-dma.h> | ||
|
|
||
| #else /* !CONFIG_SOF_USERSPACE_INTERFACE_DMA */ |
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.
Thanks @kv2019i - I think we have to align with Zephyr, but add the configuraton flexibility on the SOF side.
272cc16 to
5a6be58
Compare
|
V8 pushed:
|
Expose a subset of the Zephyr DMA interface to SOF user-space threads. The functionality is selected to cover current needs of SOF DMA client usage. SOF targets that do not use userspace, are not impacted. Use a simple object lookup for kernel objects passed from user-space and check the object is a valid kernel object before proceeding to syscall implementation. This is alternative to using Zephyr gen_kobjet_list.py infra, which would require adding the SOF DMA type to the list of Zephyr kobjects. Use a similar mechanism to look up and validate the kernel object, but use runtime methods to track objects. The permission is based on DMA devices. If a thread has access to DMA device, it will get access to all channels of the DMA controller. Memory accesses are checked for permission. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Use the sof/lib/dma.h wrapper interface to access DMA devices. This allows to run this code also from user-space. The commit has no impact to builds where userspace is not used. Also this will not impact builds where the host module is not run in user space. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Add a new variant to boot time testing for tests that will either need the IPC host interface for test purposes and is not guaranteed to be compatible with the normla host IPC interface, or the tests cause side-effects which can influence behaviour of the firmware after tests are complete. This option is intended to run complex tests that use SOF functionality, run on target hardware, but may not coexist with normal functionality provided by SOF. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
…space Test the SOF DMA interface from a user-space thread. Use all the exported functionality using SOF_DMA_DEV_HOST. This test is using the zephyr/soc/intel/intel_adsp/tools/cavstool.py as the host-side tool. This is required as the DMA programming is split between host and DSP side and test sequences need a test endpoint on both sides. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
5a6be58 to
0a79248
Compare
|
V9:
|
|
Ztest failure ( https://github.com/thesofproject/sof/actions/runs/19819752868/job/56779166869?pr=10341 ) is separate, fixed now in SOF main. Known fails in the jenkins CI runs. Given no further new comments after V8 and V9, I'll proceed with merge so I can line up the next PR that builds on this. |
Initial work to allow user-space threads to operate DMA streams. No impact to SOF targets that do not use user-space.
The first four commits are from #10119 , one market commit and two actual new commits.