Native sim support#10621
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Zephyr native_sim support to enable running SOF Zephyr tests natively (including under Valgrind) and refines POSIX/libFuzzer integration so fuzz-specific code is only built/used when enabled.
Changes:
- Add a
native_simplatform target to the Zephyr build helper. - Make POSIX fuzzing sources/IPC hooks conditional on
CONFIG_ARCH_POSIX_LIBFUZZER. - Extend the run scripts to support
native_simexecution and optional--valgrind.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
zephyr/CMakeLists.txt |
Separates POSIX sources from fuzz-only sources so fuzz.c only builds when libFuzzer is enabled. |
src/platform/posix/ipc.c |
Gates fuzz ISR/IRQ plumbing behind CONFIG_ARCH_POSIX_LIBFUZZER. |
scripts/xtensa-build-zephyr.py |
Adds native_sim as a supported Zephyr platform config. |
scripts/sof-qemu-run.sh |
Adds --valgrind flag parsing and changes default build dir behavior for native_sim runs. |
scripts/sof-qemu-run.py |
Detects native_sim from CMakeCache.txt, supports running under Valgrind, and skips QEMU monitor steps for native_sim. |
app/boards/native_sim.conf |
Introduces a board-specific Kconfig fragment for native_sim. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| #ifdef CONFIG_ARCH_POSIX_LIBFUZZER | ||
| // Not an ISR, called from the native_posix fuzz interrupt. Left | ||
| // alone for general hygiene. This is how a IPC interrupt would look | ||
| // if we had one. |
There was a problem hiding this comment.
Inside the #ifdef CONFIG_ARCH_POSIX_LIBFUZZER block, the extern declaration for posix_fuzz_buf/posix_fuzz_sz (currently extern uint8_t *posix_fuzz_buf, posix_fuzz_sz;) does not match the definitions in src/platform/posix/fuzz.c (const uint8_t *posix_fuzz_buf; size_t posix_fuzz_sz;). This mismatch can cause incorrect reads/writes (e.g., posix_fuzz_sz = 0; only updating 1 byte). Please split these into separate externs with the correct types (and const).
There was a problem hiding this comment.
under investigation why we dont use header to align.
11e03f6 to
db6ddb6
Compare
|
|
||
| if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_ZTEST) | ||
| set(MATH_ZTEST_SOURCES | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_crc32_ztest.c | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c | ||
| ../../test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c | ||
| ) | ||
|
|
||
| zephyr_library_sources(${MATH_ZTEST_SOURCES}) | ||
|
|
||
| set_source_files_properties(${MATH_ZTEST_SOURCES} | ||
| PROPERTIES COMPILE_DEFINITIONS "CONFIG_NUMBERS_VECTOR_FIND=1;CONFIG_NUMBERS_NORM=1;UNIT_TEST=1" | ||
| ) | ||
| endif() |
There was a problem hiding this comment.
Either the commit message is wrong or there was some mistake here: "Make the maths tests available on native sim target.". These tests are currently built exclusively for native_sim.
There was a problem hiding this comment.
This is a good change but I would like to have the option to build native_sim in two variants. With Zephyr heap and with system heap. Please add a Kconfig enabling such a choice.
There was a problem hiding this comment.
fixed, have 2 options now.
f2a31fa to
4dbff45
Compare
lyakh
left a comment
There was a problem hiding this comment.
one critical suspected breakage, should be checked at least
| print("\n[sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)") | ||
| else: | ||
| print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") | ||
| print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") |
There was a problem hiding this comment.
can the second sentence be rephrased a bit like "Requesting registers from the QEMU Monitor?" I'm not sure I'd say "I'm going to interface with my neighbour to ask if they can borrow me 2 potatoes" :-)
| void *ptr; | ||
| void *raw; | ||
|
|
||
| if (bytes > 16 * 1024 * 1024) { |
There was a problem hiding this comment.
rmalloc_align() is also the entry point to DRAM (L3) allocations, where we already now have possibly more than 16M and we do large allocations there e.g. when downloading LLEXTs. I could imaging a very large LLEXT library with large "cold" parts that never get copied to SRAM. Can we make this something ridiculously large like 1G or maybe configurable? Not critical for now.
There was a problem hiding this comment.
this caught me out by surprise as one of the tests was to intentionally alloc a large region and expect failure (to pass), but in the case of running on host we fail this test since 16M allocations are easily supported on the host. So I think today this value is fine.
| sof_app_main(); | ||
| #if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) | ||
| #if CONFIG_SOF_BOOT_TEST | ||
| sof_run_boot_tests(); |
There was a problem hiding this comment.
I think this breaks platforms where CONFIG_BOOT_TEST_ALLOWED=y (e.g. PTL) because on them the boot test should be run later, as it's currently done.
There was a problem hiding this comment.
good, this should fix the breakage, but I'm not sure about "standalone" - @kv2019i is it needed there? I assume standalone testing is working already now without this change, so wouldn't this change break them by trying to run boot tests twice?
There was a problem hiding this comment.
I can drop this part for the moment, the critical part was that tests would not run under simulator without a kconfig force - this could be sim related or it could be documentation related.
There was a problem hiding this comment.
@lgirdwood Took a while to check this, but I think this is ok. This change the standalone logic so that normal boot tests get also run when CONFIG_SOF_BOOT_TEST_STANDALONE is defined, but I think this is ok and makes actually sense. We are still missing some convenient method how to enable/disable certain tests when building SOF. But this is for other PRs to address.
kv2019i
left a comment
There was a problem hiding this comment.
Looks good, only issue is the boot test change in the second-to-last commit.
| sof_app_main(); | ||
| #if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) | ||
| #if CONFIG_SOF_BOOT_TEST | ||
| sof_run_boot_tests(); |
37323c5 to
bb6566e
Compare
kv2019i
left a comment
There was a problem hiding this comment.
My concerns addressed now.
| if line.startswith("CACHED_BOARD:STRING=") or line.startswith("BOARD:STRING="): | ||
| if "native_sim" in line.split("=", 1)[1].strip(): | ||
| is_native_sim = True | ||
| break |
There was a problem hiding this comment.
maybe using the same method as xtensa-build-zephyr.py via cmake_cache = zcmake.CMakeCache.from_build_dir(abs_build_dir.parent) would be more elegant for cmake cache parsing
Add native_sim board configuration and support in the build script. This allows building and running tests on the host using Zephyr's native_sim target. native_sim leverages the POSIX architecture, but the libfuzzer support specifically requires CONFIG_ARCH_POSIX_LIBFUZZER to be set. Therefore, this wraps fuzzer-specific code in ipc.c and the build of fuzz.c behind this config to allow clean compilation on the standard native_sim board. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
3ed3179 to
67b92fd
Compare
| #endif | ||
|
|
||
|
|
||
| #if defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) |
There was a problem hiding this comment.
Agreed in principle. This one is tied to the "move the block to the bottom" suggestion below: a bare #else here would collide with the existing #else at the non-native rmalloc section, so it only works once the native_sim block is relocated so all the non-native code is contiguous. I have addressed the functional review points in this revision; happy to do that structural consolidation (single #if !defined(...) ... #else ... #endif with native_sim as the trailing #else) as a focused follow-up so this diff stays reviewable — let me know if you'd prefer it folded in now.
|
|
||
| return ptr; | ||
| } | ||
| EXPORT_SYMBOL(rmalloc_align); |
There was a problem hiding this comment.
I don't think you need LLEXT symbol exporting for the simulator
There was a problem hiding this comment.
Done — dropped the EXPORT_SYMBOL() calls from the native_sim allocator path (rmalloc_align/rmalloc/rzalloc/rballoc_align/rfree); the simulator does not load LLEXT modules so it does not need them.
| rfree(addr); | ||
| } | ||
|
|
||
| #else |
There was a problem hiding this comment.
maybe put this whole block at the bottom after line 777 (original 674)
There was a problem hiding this comment.
Makes sense — consolidating the native_sim implementation into one trailing block (the #else of the !defined(NATIVE_SIM) section) would remove the fragmentation. As noted on the #else comment above, I kept the current structure for this revision to keep the diff focused on the functional fixes; I can do the relocation as a follow-up commit if you want it in this PR.
| if (alignment < sizeof(void *)) | ||
| alignment = sizeof(void *); | ||
|
|
||
| raw = nsi_host_malloc(bytes + alignment + sizeof(void *)); | ||
| if (!raw) | ||
| return NULL; | ||
|
|
||
| ptr = (void *)(((uintptr_t)raw + sizeof(void *) + alignment - 1) & ~(alignment - 1)); | ||
| ((void **)ptr)[-1] = raw; |
There was a problem hiding this comment.
Fixed the power-of-two assumption: alignment is now rounded up to the next power of two before the mask-based round-up, so a non-power-of-two value can no longer yield a misaligned pointer. On the overflow point: this is the native_sim host build (64-bit size_t) and bytes is already capped at 16MB just above, so bytes + alignment + sizeof(void *) cannot overflow; I added a comment noting that rather than a dead runtime check.
| void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, | ||
| size_t alignment) | ||
| { | ||
| return rmalloc_align(flags, bytes, alignment); | ||
| } | ||
|
|
||
| void sof_heap_free(struct k_heap *heap, void *addr) | ||
| { | ||
| rfree(addr); | ||
| } |
There was a problem hiding this comment.
Fixed — the native_sim path now defines z_impl_sof_heap_alloc()/z_impl_sof_heap_free() and only defines the plain sof_heap_alloc()/sof_heap_free() wrappers under #ifndef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC, mirroring the non-native path. So with the syscall interface enabled the generated wrappers bind to z_impl_*, and there is no longer a conflicting direct definition.
| # Make absolute path just in case | ||
| # The shell script cd's into `args.build_dir` before executing us, so `args.build_dir` might be relative to the shell script's pwd. | ||
| # We resolve it relative to the python script's original invocation cwd. | ||
| build_dir = os.path.abspath(args.build_dir) |
There was a problem hiding this comment.
Fixed — updated the comment; the shell wrapper no longer cd's, so it now just notes that args.build_dir may be relative to the caller's cwd and is resolved to absolute.
| @@ -1195,7 +1199,7 @@ def install_platform(platform, sof_output_dir, platf_build_environ, platform_wco | |||
|
|
|||
| os.makedirs(install_key_dir, exist_ok=True) | |||
| # looses file owner and group - file is commonly accessible, dont install qemu. | |||
There was a problem hiding this comment.
Fixed — "looses" -> "loses", "dont" -> "don't".
| #if CONFIG_SOF_BOOT_TEST && (defined(QEMU_BOOT_TESTS) || CONFIG_SOF_BOOT_TEST_STANDALONE) | ||
| sof_run_boot_tests(); | ||
| qemu_xtensa_exit(0); | ||
| #endif |
There was a problem hiding this comment.
Fixed — the qemu_xtensa_exit(0) call is now wrapped in #if defined(QEMU_BOOT_TESTS), so a standalone boot test (e.g. native_sim) just returns from test_main() instead of referencing the QEMU-only symbol.
Add native_sim board target to the sof-qemu-run scripts, and add an option to additionally run it under valgrind. The default build directory is set to ../build-native_sim Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
When building the firmware for native_sim, debugging allocations with host machine tools like Valgrind is constrained due to Zephyr's internal minimal libc tracking the heap manually via static pools. By bypassing Zephyr's memory interception on native_sim using nsi_host_malloc, dynamically tracked memory can surface appropriately to Valgrind memory checkers without causing a libc heap pool panic. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Keep spinning in case user needs to inspect status via monitor. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
When building the native_sim fuzzer, the host allocator does not possess the strict bounds of the internal Zephyr memory pools. If the fuzzer generates a malformed payload requesting an excessively large size (e.g. 4GB), it passes directly to the host ASAN allocator which aborts due to OOM or protection limits. Adding a 16MB cap allows these to fail gracefully. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Only run sof_run_boot_tests() at boot for QEMU or native_sim (STANDALONE) targets. Other platforms trigger via IPC.
Add support for native sim target and include being able to run under valgrind. This should support all cmocka tests as ztests meaning more/all can be removed. Will be added to CI soon.