diff --git a/app/boards/native_sim.conf b/app/boards/native_sim.conf new file mode 100644 index 000000000000..6df3eb89b648 --- /dev/null +++ b/app/boards/native_sim.conf @@ -0,0 +1,4 @@ +CONFIG_ZEPHYR_POSIX=y +CONFIG_SYS_HEAP_BIG_ONLY=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_ZEPHYR_LOG=y diff --git a/scripts/sof-qemu-run.py b/scripts/sof-qemu-run.py index fc985e254d51..23d67b987a60 100755 --- a/scripts/sof-qemu-run.py +++ b/scripts/sof-qemu-run.py @@ -76,9 +76,12 @@ def main(): parser = argparse.ArgumentParser(description="Run QEMU via west and automatically decode crashes.") parser.add_argument("--build-dir", default="build", help="Path to the build directory containing zephyr.elf, linker.cmd, etc. Defaults to 'build'.") parser.add_argument("--log-file", default="qemu-run.log", help="Path to save the QEMU output log. Defaults to 'qemu-run.log'.") + parser.add_argument("--valgrind", action="store_true", help="Run the executable under Valgrind (only valid for native_sim).") args = parser.parse_args() # 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) print(f"Starting QEMU test runner. Monitoring for crashes (Build Dir: {args.build_dir})...") @@ -91,7 +94,53 @@ def main(): print("Please ensure you have sourced the Zephyr environment (e.g., source zephyr-env.sh).") sys.exit(1) - child = pexpect.spawn(west_path, ["-v", "build", "-t", "run"], encoding='utf-8') + # Detect the board configuration from CMakeCache.txt + is_native_sim = False + + cmake_cache = os.path.join(build_dir, "CMakeCache.txt") + + if os.path.isfile(cmake_cache): + with open(cmake_cache, "r") as f: + for line in f: + 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 + + # Determine execution command + # If the user is running the python script directly from outside the workspace, we need to provide the source directory. + # But if west finds it automatically (or we are in the build dir), providing `-s` might clear the CACHED_BOARD config. + run_cmd = [west_path, "-v", "build", "-d", build_dir] + + # Check if we are physically sitting inside the build directory + if os.path.abspath(".") != os.path.abspath(build_dir): + # We need to explicitly supply the app source to prevent west from crashing + app_source_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "app")) + run_cmd.extend(["-s", app_source_dir]) + + run_cmd.extend(["-t", "run"]) + + if args.valgrind: + if not is_native_sim: + print("[sof-qemu-run] Error: --valgrind is only supported for the native_sim board.") + sys.exit(1) + + print("[sof-qemu-run] Rebuilding before valgrind...") + subprocess.run([west_path, "build", "-d", build_dir], check=True) + + valgrind_path = shutil.which("valgrind") + if not valgrind_path: + print("[sof-qemu-run] Error: 'valgrind' command not found in PATH.") + sys.exit(1) + + exe_path = os.path.join(build_dir, "zephyr", "zephyr.exe") + if not os.path.isfile(exe_path): + print(f"[sof-qemu-run] Error: Executable not found at {exe_path}") + sys.exit(1) + + run_cmd = [valgrind_path, exe_path] + + child = pexpect.spawn(run_cmd[0], run_cmd[1:], encoding='utf-8') # We will accumulate output to check for crashes full_output = "" @@ -157,11 +206,14 @@ def main(): run_sof_crash_decode(build_dir, full_output) else: - print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") + if is_native_sim: + print("\n[sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)") + else: + print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") - # We need to send Ctrl-A c to enter the monitor - if child.isalive(): - child.send("\x01c") # Ctrl-A c + # We need to send Ctrl-A c to enter the monitor + if child.isalive(): + child.send("\x01c") # Ctrl-A c try: # Wait for (qemu) prompt child.expect(r"\(qemu\)", timeout=5) @@ -185,8 +237,8 @@ def main(): child.close(force=True) except pexpect.EOF: print("\n[sof-qemu-run] QEMU terminated before we could run monitor commands.") - else: - print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") + else: + print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") if __name__ == "__main__": main() diff --git a/scripts/sof-qemu-run.sh b/scripts/sof-qemu-run.sh index e1ece1dd5125..ba1d012d3a5b 100755 --- a/scripts/sof-qemu-run.sh +++ b/scripts/sof-qemu-run.sh @@ -2,8 +2,21 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2026 Intel Corporation. All rights reserved. -# Define the build directory from the first argument (or default) -BUILD_DIR="${1:-build}" +BUILD_DIR="../build-native_sim" +VALGRIND_ARG="" + +while [[ $# -gt 0 ]]; do + case $1 in + --valgrind) + VALGRIND_ARG="--valgrind" + shift + ;; + *) + BUILD_DIR="$1" + shift + ;; + esac +done # Find and source the zephyr environment script, typically via the sof-venv wrapper # or directly if running in a known zephyrproject layout. @@ -24,5 +37,5 @@ source ${VENV_DIR}/bin/activate cd "${BUILD_DIR}" || exit 1 # Finally run the python script which will now correctly inherit 'west' from the sourced environment. -python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" +python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" $VALGRIND_ARG diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index b6a2b8e91a90..0e1b8e7dfde8 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -235,6 +235,10 @@ class PlatformConfig: "zephyr", "qemu_xtensa/dc233c/mmu", "", "", "zephyr" ), + "native_sim" : PlatformConfig( + "zephyr", "native_sim", + "", "", "zephyr" + ), } platform_configs = platform_configs_all.copy() diff --git a/src/platform/posix/ipc.c b/src/platform/posix/ipc.c index 701686e6ff97..14e56da530db 100644 --- a/src/platform/posix/ipc.c +++ b/src/platform/posix/ipc.c @@ -14,6 +14,7 @@ SOF_DEFINE_REG_UUID(ipc_task_posix); static struct ipc *global_ipc; +#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. @@ -131,6 +132,7 @@ static void fuzz_isr(const void *arg) posix_ipc_isr(NULL); } +#endif // This API is... confounded by its history. With IPC3, the job of // this function is to get a newly-received IPC message header (!) @@ -172,12 +174,14 @@ int ipc_platform_compact_read_msg(struct ipc_cmd_hdr *hdr, int words) // Re-raise the interrupt if there's still fuzz data to process void ipc_platform_complete_cmd(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER extern void posix_sw_set_pending_IRQ(unsigned int IRQn); if (fuzz_in_sz > 0) { posix_fuzz_sz = 0; posix_sw_set_pending_IRQ(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); } +#endif } int ipc_platform_send_msg(const struct ipc_msg *msg) @@ -200,8 +204,10 @@ void ipc_platform_send_msg_direct(const struct ipc_msg *msg) int platform_ipc_init(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER IRQ_CONNECT(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ, 0, fuzz_isr, NULL, 0); irq_enable(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); +#endif global_ipc = ipc; schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_posix_uuid), diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 233c93f3e070..be87d9759228 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -484,6 +484,9 @@ zephyr_library_sources_ifdef(CONFIG_ZEPHYR_POSIX ${SOF_PLATFORM_PATH}/posix/dai.c ${SOF_PLATFORM_PATH}/posix/ipc.c ${SOF_PLATFORM_PATH}/posix/posix.c +) + +zephyr_library_sources_ifdef(CONFIG_ARCH_POSIX_LIBFUZZER ${SOF_PLATFORM_PATH}/posix/fuzz.c )