-
Notifications
You must be signed in to change notification settings - Fork 352
Native sim support #10621
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
base: main
Are you sure you want to change the base?
Native sim support #10621
Changes from all commits
49d9c4e
29dd433
5f6c2f2
cd8c007
9c4f29f
c06f542
365142d
01e4691
e3b8ad9
4dbff45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| CONFIG_ZEPHYR_POSIX=y | ||
| CONFIG_SYS_HEAP_BIG_ONLY=y | ||
| CONFIG_ZEPHYR_NATIVE_DRIVERS=y | ||
| CONFIG_ZEPHYR_LOG=y | ||
| CONFIG_ZTEST=y | ||
| CONFIG_SOF_BOOT_TEST_STANDALONE=y | ||
| CONFIG_NUMBERS_VECTOR_FIND=y | ||
| CONFIG_NUMBERS_NORM=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
|
||
|
lgirdwood marked this conversation as resolved.
|
||
| 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,36 +206,39 @@ 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...") | ||
|
|
||
| # 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) | ||
| # Send "info registers" | ||
| child.sendline("info registers") | ||
| # Wait for the next prompt | ||
| child.expect(r"\(qemu\)", timeout=5) | ||
|
|
||
| info_regs_output = child.before | ||
| print("\n[sof-qemu-run] Successfully extracted registers from QEMU monitor.\n") | ||
|
|
||
| # Quit qemu safely | ||
| child.sendline("quit") | ||
| child.expect(pexpect.EOF, timeout=2) | ||
| child.close() | ||
|
|
||
| # Run the decoder on the intercepted register output | ||
| run_sof_crash_decode(build_dir, info_regs_output) | ||
| except pexpect.TIMEOUT: | ||
| print("\n[sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?") | ||
| child.close(force=True) | ||
| except pexpect.EOF: | ||
| print("\n[sof-qemu-run] QEMU terminated before we could run monitor commands.") | ||
| 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] Process is no longer alive, cannot extract registers.") | ||
| print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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" :-) |
||
|
|
||
| # 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) | ||
| # Send "info registers" | ||
| child.sendline("info registers") | ||
| # Wait for the next prompt | ||
| child.expect(r"\(qemu\)", timeout=5) | ||
|
|
||
| info_regs_output = child.before | ||
| print("\n[sof-qemu-run] Successfully extracted registers from QEMU monitor.\n") | ||
|
|
||
| # Quit qemu safely | ||
| child.sendline("quit") | ||
| child.expect(pexpect.EOF, timeout=2) | ||
| child.close() | ||
|
|
||
| # Run the decoder on the intercepted register output | ||
| run_sof_crash_decode(build_dir, info_regs_output) | ||
| except pexpect.TIMEOUT: | ||
| print("\n[sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?") | ||
| 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.") | ||
|
|
||
|
lgirdwood marked this conversation as resolved.
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* SPDX-License-Identifier: BSD-3-Clause | ||
| * | ||
| * Copyright(c) 2026 Intel Corporation. All rights reserved. | ||
| */ | ||
|
|
||
| /** | ||
| * Symbols shared between the native_posix fuzz harness | ||
| * (src/platform/posix/fuzz.c) and the SOF posix IPC layer | ||
| * (src/platform/posix/ipc.c). | ||
| * | ||
| * Defining them in one place avoids type-mismatch bugs (e.g. a single | ||
| * `extern uint8_t *posix_fuzz_buf, posix_fuzz_sz;` declaring `posix_fuzz_sz` | ||
| * as a `uint8_t` rather than a `size_t`). | ||
| */ | ||
|
|
||
| #ifndef PLATFORM_POSIX_FUZZ_H | ||
| #define PLATFORM_POSIX_FUZZ_H | ||
|
|
||
| #include <stddef.h> | ||
| #include <stdint.h> | ||
|
|
||
| extern const uint8_t *posix_fuzz_buf; | ||
| extern size_t posix_fuzz_sz; | ||
|
|
||
| #endif /* PLATFORM_POSIX_FUZZ_H */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
Comment on lines
+17
to
20
|
||
|
|
@@ -23,7 +24,7 @@ static void posix_ipc_isr(void *arg) | |
| } | ||
|
|
||
| // External symbols set up by the fuzzing layer | ||
| extern uint8_t *posix_fuzz_buf, posix_fuzz_sz; | ||
| #include <platform/posix_fuzz.h> | ||
|
|
||
| // Lots of space. Should really synchronize with the -max_len | ||
| // parameter to libFuzzer (defaults to 4096), but that requires | ||
|
|
@@ -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), | ||
|
|
||
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 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.