Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/boards/native_sim.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_ZEPHYR_POSIX=y
CONFIG_SYS_HEAP_BIG_ONLY=y
CONFIG_ZEPHYR_NATIVE_DRIVERS=y
CONFIG_ZEPHYR_LOG=y
66 changes: 59 additions & 7 deletions scripts/sof-qemu-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Comment on lines 82 to 86
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_dir = os.path.abspath(args.build_dir) is resolved relative to the current working directory. When sof-qemu-run.sh does cd "$BUILD_DIR" and still passes --build-dir "$BUILD_DIR" (typically a relative path like build-qemu_xtensa), this becomes <builddir>/build-qemu_xtensa, and the script will fail to find CMakeCache.txt/zephyr/. Either stop cd-ing in the shell wrapper, pass an absolute path (compute it before cd), or have the Python script detect this case and fall back to os.getcwd() when args.build_dir doesn’t point at a valid Zephyr build directory.

Copilot uses AI. Check for mistakes.
print(f"Starting QEMU test runner. Monitoring for crashes (Build Dir: {args.build_dir})...")
Expand All @@ -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 = ""
Expand Down Expand Up @@ -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:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this try block also be under if - indented?

# Wait for (qemu) prompt
child.expect(r"\(qemu\)", timeout=5)
Expand All @@ -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()
19 changes: 16 additions & 3 deletions scripts/sof-qemu-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=""
Comment on lines +5 to +6
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the default build directory from the previous ${1:-build} behavior to a hard-coded ../build-native_sim. That makes ./scripts/sof-qemu-run.sh (with no args) run a different target than before and can be surprising for existing QEMU workflows. Consider keeping the previous default (build) and/or selecting a native_sim default only when explicitly requested (e.g., via a flag), so the script remains backward compatible.

Copilot uses AI. Check for mistakes.

while [[ $# -gt 0 ]]; do
case $1 in
--valgrind)
VALGRIND_ARG="--valgrind"
shift
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could just have a single shift after esac

;;
*)
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.
Expand All @@ -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
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapper cds into BUILD_DIR and then still passes --build-dir "${BUILD_DIR}" to the Python runner. If BUILD_DIR is a relative path like build-qemu_xtensa, the Python script will resolve it relative to the new cwd and end up with <build-qemu_xtensa>/build-qemu_xtensa, breaking path lookups. Please either: (1) pass --build-dir . after the cd, (2) compute an absolute BUILD_DIR before cd, or (3) remove the cd and let the Python script run from its invocation directory.

Suggested change
python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" $VALGRIND_ARG
python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir . $VALGRIND_ARG

Copilot uses AI. Check for mistakes.

4 changes: 4 additions & 0 deletions scripts/xtensa-build-zephyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions src/platform/posix/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -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 (!)
Expand Down Expand Up @@ -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)
Expand All @@ -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),
Expand Down
3 changes: 3 additions & 0 deletions zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
Loading