Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9513631
zephyr: Kconfig: add SOF shell commands menu
lrgirdwo May 4, 2026
3893d80
zephyr/shell: add DSP status commands and fix heap_usage
lrgirdwo May 4, 2026
cdd4ad1
zephyr/shell: add module list command with manifest lookup
lrgirdwo May 4, 2026
b71ef74
zephyr/shell: add IPC4 pipeline construction commands
lrgirdwo May 4, 2026
2bdd564
zephyr/shell: add Intel ADSP MTL TLB debug commands
lrgirdwo May 4, 2026
44ced55
zephyr/shell: add Xtensa hardware DTLB debug commands (PTL)
lrgirdwo May 4, 2026
e28869a
zephyr/shell: add interactive llext module load command
lrgirdwo May 6, 2026
6d956cf
zephyr/shell: fix llext_load hint: use dd not cat; fix lib_manager in…
lrgirdwo May 6, 2026
cab3ee7
shell: add llext_list and llext_purge commands
lrgirdwo May 6, 2026
248923a
shell: add core_on and core_off commands for secondary core power con…
lrgirdwo May 6, 2026
88694e0
shell: add support to qemu for shell
lrgirdwo Mar 4, 2026
261112a
vregion: add shell info dump for vpages and vregions
lrgirdwo May 7, 2026
6b0bf0f
zephyr/shell: add vpage/vregion/pipeline/module info commands
lrgirdwo May 7, 2026
2b4ebe5
ipc: add lightweight RX/TX statistics and shell ipc_stats/ipc_last co…
lrgirdwo May 7, 2026
1c8f2ca
zephyr/shell: add buffer_list and buffer_info commands
lrgirdwo May 7, 2026
053c962
schedule: add scheduler_dump_tasks op and shell sched_tasks/sched_load
lrgirdwo May 7, 2026
2482f44
shell: add log_status and mtrace_dump commands
lrgirdwo May 7, 2026
b8a1121
shell: add mailbox_hex and dbgwin_dump commands
lrgirdwo May 7, 2026
229bfdd
shell: add perf_status command for SOF telemetry
lrgirdwo May 7, 2026
b54c365
shell: add dai_list and dma_status commands
lrgirdwo May 7, 2026
3f83a8b
shell: add kctl_list command
lrgirdwo May 7, 2026
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
3 changes: 3 additions & 0 deletions app/no_auth_llext_overlay.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Disable library authentication for testing on hardware without OTC key support
# This allows loading community-signed llext libraries without CSE authentication
CONFIG_LIBRARY_AUTH_SUPPORT=n
9 changes: 8 additions & 1 deletion app/shell_overlay.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CONFIG_SHELL=y
CONFIG_SHELL_HELP=y
CONFIG_SHELL_CMDS=y
CONFIG_SHELL_LOG_BACKEND=n
CONFIG_SHELL_LOG_BACKEND=y
CONFIG_SHELL_AUTOSTART=y

CONFIG_SHELL_BACKEND_ADSP_MEMORY_WINDOW=y
Expand All @@ -18,3 +18,10 @@ CONFIG_WINSTREAM_CONSOLE=y
# these must be disabled in order to use the console.
CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=n
CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=n
CONFIG_SOF_SHELL_LLEXT_LOAD=y
CONFIG_SOF_SHELL_LLEXT_LIST=y
CONFIG_SOF_SHELL_LLEXT_PURGE=y
CONFIG_SOF_SHELL_CORE_POWER=y

# Disable REBOOT since qemu_xtensa does not implement sys_arch_reboot
CONFIG_REBOOT=n
4 changes: 4 additions & 0 deletions app/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ void test_main(void)
sof_app_main();
#if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS)
sof_run_boot_tests();
#ifdef CONFIG_SHELL
k_sleep(K_FOREVER);
#else
qemu_xtensa_exit(0);
#endif
#endif
}
#else
int main(void)
Expand Down
20 changes: 20 additions & 0 deletions scripts/sof-qemu-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@ def main():

crashed = check_for_crash(full_output)

shell_enabled = False
config_file = os.path.join(build_dir, "zephyr", ".config")
if not os.path.isfile(config_file):
config_file = os.path.join("zephyr", ".config")

if os.path.isfile(config_file):
with open(config_file, "r") as f:
for line in f:
if line.strip() == "CONFIG_SHELL=y":
shell_enabled = True
break

if crashed:
print("\n[sof-qemu-run] Detected crash signature in standard output!")
# Stop QEMU if it's still running
Expand All @@ -156,6 +168,14 @@ def main():
child.close(force=True)

run_sof_crash_decode(build_dir, full_output)
elif shell_enabled:
print("\n[sof-qemu-run] Shell is enabled. Entering interactive mode...")
if child.isalive():
try:
child.interact()
except Exception as e:
print(f"Error during interaction: {e}")
child.close(force=True)
else:
print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...")

Expand Down
23 changes: 20 additions & 3 deletions scripts/sof-qemu-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,25 @@
# 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}"
# Parse arguments to determine the build directory
BUILD_DIR="build"

while [[ $# -gt 0 ]]; do
case "$1" in
--build-dir)
BUILD_DIR="$2"
shift 2
;;
-*)
echo "Unknown option $1"
exit 1
;;
*)
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 +41,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 .

60 changes: 60 additions & 0 deletions src/include/sof/ipc/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,66 @@ struct ipc_cmd_hdr *mailbox_validate(void);
*/
void ipc_cmd(struct ipc_cmd_hdr *_hdr);

/**
* \brief Lightweight IPC counters and last-message snapshot for diagnostics.
*
* Populated by the platform RX/TX hooks. Safe to read from any context;
* fields are 32-bit and updated under @ref ipc::lock when written.
*/
struct ipc_stats {
uint32_t rx_count; /**< total IPC messages received */
uint32_t tx_count; /**< total IPC messages sent */
uint32_t tx_direct_count; /**< messages sent via the direct path */
uint32_t rx_errors; /**< RX path errors / unknown targets */
uint32_t tx_errors; /**< TX path send failures */

/* last RX */
uint32_t last_rx_pri;
uint32_t last_rx_ext;
uint64_t last_rx_time; /**< platform cycles */

/* last TX */
uint32_t last_tx_pri;
uint32_t last_tx_ext;
uint64_t last_tx_time;
};

/**
* \brief Record an inbound IPC for the stats snapshot.
*
* @param[in] pri Primary header word.
* @param[in] ext Extension header word.
*/
void ipc_stats_record_rx(uint32_t pri, uint32_t ext);

/**
* \brief Record an outbound IPC for the stats snapshot.
*
* @param[in] pri Primary header word.
* @param[in] ext Extension header word.
* @param[in] direct True if the message used the "direct" send path.
* @param[in] err Send result (negative on error).
*/
void ipc_stats_record_tx(uint32_t pri, uint32_t ext, bool direct, int err);

/**
* \brief Increment the RX error counter without updating the last-message
* snapshot. Used for unknown targets / dispatch failures.
*/
void ipc_stats_inc_rx_error(void);

/**
* \brief Read a copy of the current IPC statistics.
*
* @param[out] out Destination snapshot.
*/
void ipc_stats_get(struct ipc_stats *out);

/**
* \brief Reset all IPC statistics counters.
*/
void ipc_stats_reset(void);

/**
* \brief IPC message to be processed on other core.
* @param[in] core Core id for IPC to be processed on.
Expand Down
10 changes: 10 additions & 0 deletions src/include/sof/lib/vregion.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <stddef.h>

struct shell;

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -123,6 +125,13 @@ void vregion_info(struct vregion *vr);
*/
void vregion_mem_info(struct vregion *vr, size_t *size, uintptr_t *start);

/**
* @brief Dump all virtual regions info
*
* @param[in] sh Shell context to print to.
*/
void vregion_info_all(const struct shell *sh);

#else /* CONFIG_SOF_VREGIONS */

#include <rtos/alloc.h>
Expand Down Expand Up @@ -176,6 +185,7 @@ static inline void vregion_mem_info(struct vregion *vr, size_t *size, uintptr_t
if (size)
*size = 0;
}
static inline void vregion_info_all(const struct shell *sh) {}

#endif /* CONFIG_SOF_VREGIONS */

Expand Down
15 changes: 15 additions & 0 deletions src/include/sof/lib_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,19 @@ void lib_notif_msg_send(struct ipc_msg *msg);
*/
void lib_notif_msg_clean(bool leave_one_handle);

/*
* \brief Purge (free) a loadable library from IMR/DRAM storage
*
* param[in] lib_id - library slot id (1 .. LIB_MANAGER_MAX_LIBS-1)
*
* Removes the library binary from DRAM/IMR and releases the
* lib_manager_mod_ctx entry so that the slot can be reused by a
* future LOAD_LIBRARY call. Returns -EBUSY if any module file from
* the library is still mapped in SRAM (i.e., has active instances
* or is still linked as a dependency).
*
* Return: 0 on success, negative errno on error.
*/
int lib_manager_purge_library(uint32_t lib_id);

#endif /* __SOF_LIB_MANAGER_H__ */
15 changes: 15 additions & 0 deletions src/include/sof/schedule/schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,21 @@ struct scheduler_ops {
* This operation is optional.
*/
int (*scheduler_restore)(void *data);

/**
* Iterate over all tasks owned by the scheduler and invoke @p cb on each.
* The scheduler is responsible for taking its own lock around the walk.
* @param data Private data of selected scheduler.
* @param cb Callback called once per task; must not block.
* @param ctx Opaque context passed to @p cb.
*
* This operation is optional and exists only for diagnostics
* (e.g. shell commands). Schedulers that do not implement it are
* silently skipped by enumeration tools.
*/
void (*scheduler_dump_tasks)(void *data,
void (*cb)(struct task *task, void *ctx),
void *ctx);
};

/** \brief Holds information about scheduler. */
Expand Down
110 changes: 110 additions & 0 deletions src/ipc/ipc-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,125 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <sof/debug/telemetry/performance_monitor.h>
#include <rtos/timer.h>

LOG_MODULE_REGISTER(ipc, CONFIG_SOF_LOG_LEVEL);

SOF_DEFINE_REG_UUID(ipc);

DECLARE_TR_CTX(ipc_tr, SOF_UUID(ipc_uuid), LOG_LEVEL_INFO);

/* Lightweight IPC stats. Updated under ipc->lock on the write paths and
* read with the same lock from the shell. Single instance is sufficient
* since there is only one IPC instance per firmware image.
*/
static struct ipc_stats g_ipc_stats;

void ipc_stats_record_rx(uint32_t pri, uint32_t ext)
{
struct ipc *ipc = ipc_get();
k_spinlock_key_t key;

if (!ipc) {
g_ipc_stats.rx_count++;
g_ipc_stats.last_rx_pri = pri;
g_ipc_stats.last_rx_ext = ext;
g_ipc_stats.last_rx_time = sof_cycle_get_64();
return;
}

key = k_spin_lock(&ipc->lock);
g_ipc_stats.rx_count++;
g_ipc_stats.last_rx_pri = pri;
g_ipc_stats.last_rx_ext = ext;
g_ipc_stats.last_rx_time = sof_cycle_get_64();
k_spin_unlock(&ipc->lock, key);
}

void ipc_stats_record_tx(uint32_t pri, uint32_t ext, bool direct, int err)
{
struct ipc *ipc = ipc_get();
k_spinlock_key_t key;

if (!ipc) {
if (err < 0)
g_ipc_stats.tx_errors++;
else if (direct)
g_ipc_stats.tx_direct_count++;
else
g_ipc_stats.tx_count++;
g_ipc_stats.last_tx_pri = pri;
g_ipc_stats.last_tx_ext = ext;
g_ipc_stats.last_tx_time = sof_cycle_get_64();
return;
}

key = k_spin_lock(&ipc->lock);
if (err < 0) {
g_ipc_stats.tx_errors++;
} else {
if (direct)
g_ipc_stats.tx_direct_count++;
else
g_ipc_stats.tx_count++;
g_ipc_stats.last_tx_pri = pri;
g_ipc_stats.last_tx_ext = ext;
g_ipc_stats.last_tx_time = sof_cycle_get_64();
}
k_spin_unlock(&ipc->lock, key);
}

void ipc_stats_inc_rx_error(void)
{
struct ipc *ipc = ipc_get();
k_spinlock_key_t key;

if (!ipc) {
g_ipc_stats.rx_errors++;
return;
}

key = k_spin_lock(&ipc->lock);
g_ipc_stats.rx_errors++;
k_spin_unlock(&ipc->lock, key);
}

void ipc_stats_get(struct ipc_stats *out)
{
struct ipc *ipc = ipc_get();
k_spinlock_key_t key;

if (!out)
return;

if (!ipc) {
*out = g_ipc_stats;
return;
}

key = k_spin_lock(&ipc->lock);
*out = g_ipc_stats;
k_spin_unlock(&ipc->lock, key);
}

void ipc_stats_reset(void)
{
struct ipc *ipc = ipc_get();
k_spinlock_key_t key;

if (!ipc) {
memset(&g_ipc_stats, 0, sizeof(g_ipc_stats));
return;
}

key = k_spin_lock(&ipc->lock);
memset(&g_ipc_stats, 0, sizeof(g_ipc_stats));
k_spin_unlock(&ipc->lock, key);
}

int ipc_process_on_core(uint32_t core, bool blocking)
{
struct ipc *ipc = ipc_get();
Expand Down
Loading
Loading