From 2ea605021a5c3544a0635c6b79320c9518a9fabb Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Wed, 5 Aug 2026 07:49:00 -0700 Subject: [PATCH 1/3] examples: libmetal: amd_linux_userspace: open host demo on UIO bus Open the Linux irq_shmem_demo host path on the libmetal UIO bus using stable logical names exposed through /sys/class/uio/uioX/name. Read the host IPI remote kick bit from the backing device-tree node through libmetal,uio-ipi-bitmask, and derive descriptor and payload sizes from the opened IO regions so the host demo no longer depends on generated device names or carveout size macros. Signed-off-by: Ben Levinsky --- .../irq_shmem_demo/common/irq_shmem_demo.h | 3 + .../irq_shmem_demo/host/irq_shmem_demo.c | 59 ++--- .../machine/host/amd_linux_userspace/common.h | 2 +- .../host/amd_linux_userspace/platform_init.c | 239 +++++++++++++++--- .../host/amd_linux_userspace/platform_init.h | 2 +- 5 files changed, 242 insertions(+), 63 deletions(-) diff --git a/examples/libmetal/demos/irq_shmem_demo/common/irq_shmem_demo.h b/examples/libmetal/demos/irq_shmem_demo/common/irq_shmem_demo.h index b8524cf2..381da74d 100644 --- a/examples/libmetal/demos/irq_shmem_demo/common/irq_shmem_demo.h +++ b/examples/libmetal/demos/irq_shmem_demo/common/irq_shmem_demo.h @@ -25,6 +25,9 @@ struct channel_s { void *machine_ctx; /* Platform- or OS-private channel state */ uint32_t ipi_mask; /* RPU IPI mask */ int irq_vector_id; /* IRQ number. */ + uint32_t desc0_size; /* host to remote descriptor region size */ + uint32_t desc1_size; /* remote to host descriptor region size */ + uint32_t shm_payload_size; /* shared payload buffer size */ }; #endif /* __IRQ_SHMEM_DEMO_H__ */ diff --git a/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c b/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c index 589f8196..5c6163b1 100644 --- a/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c +++ b/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c @@ -37,30 +37,11 @@ #include "common.h" #include "platform_init.h" -/* Shared memory offsets */ -#define SHM_DESC_OFFSET_TX 0x0 -#define SHM_BUFF_OFFSET_TX 0x04000 -#define SHM_DESC_OFFSET_RX 0x02000 -#define SHM_BUFF_OFFSET_RX 0x104000 - /* Shared memory descriptors offset */ #define SHM_DESC_AVAIL_OFFSET 0x00 #define SHM_DESC_USED_OFFSET 0x04 #define SHM_DESC_ADDR_ARRAY_OFFSET 0x08 -/* Descriptor regions for each direction. */ -/* Note that H_TO_R_ is host to remote and R_TO_H_ is vice versa. */ -#define H_TO_R_DESC_ADDR_START SHM_DESC_ADDR_ARRAY_OFFSET -#define H_TO_R_DESC_ADDR_END SHM0_DESC_SIZE -#define R_TO_H_DESC_ADDR_START SHM_DESC_ADDR_ARRAY_OFFSET -#define R_TO_H_DESC_ADDR_END SHM1_DESC_SIZE - -/* Split of the data / payload area for each direction */ -#define H_TO_R_PAYLOAD_START SHM_PAYLOAD_RX_OFFSET -#define H_TO_R_PAYLOAD_END (SHM_PAYLOAD_RX_OFFSET + SHM_PAYLOAD_HALF_SIZE) -#define R_TO_H_PAYLOAD_START SHM_PAYLOAD_TX_OFFSET -#define R_TO_H_PAYLOAD_END (SHM_PAYLOAD_TX_OFFSET + SHM_PAYLOAD_HALF_SIZE) - #define PKGS_TOTAL 1024 #define BUF_SIZE_MAX 512 @@ -137,6 +118,8 @@ static int irq_shmem_echo(struct channel_s *ch) unsigned long tx_avail_offset, rx_avail_offset; unsigned long tx_addr_offset, rx_addr_offset; unsigned long tx_data_offset, rx_data_offset; + uint32_t h_to_r_desc_addr_end, h_to_r_payload_start; + uint32_t h_to_r_payload_end, r_to_h_payload_start; void *txbuf = NULL, *rxbuf = NULL, *tmpptr; long long tdiff_avg_s = 0, tdiff_avg_ns = 0; unsigned long long tstart, tend; @@ -163,23 +146,31 @@ static int irq_shmem_echo(struct channel_s *ch) if (!ch || !ch->shm_io || !ch->host_to_remote_desc_io || !ch->remote_to_host_desc_io || !ch->ipi_io) { - return -EINVAL; + ret = -EINVAL; + goto out; } + h_to_r_desc_addr_end = ch->desc0_size; + h_to_r_payload_start = 0; + h_to_r_payload_end = ch->shm_payload_size / 2; + r_to_h_payload_start = ch->shm_payload_size / 2; + /* Clear shared memory and descriptors */ - ret = metal_io_block_set(ch->shm_io, 0, 0, SHM_PAYLOAD_SIZE); + ret = metal_io_block_set(ch->shm_io, 0, 0, ch->shm_payload_size); if (ret < 0) { metal_err("HOST: Failed to clear payload area.\n"); goto out; } - ret = metal_io_block_set(ch->host_to_remote_desc_io, 0, 0, SHM0_DESC_SIZE); + ret = metal_io_block_set(ch->host_to_remote_desc_io, 0, 0, + ch->desc0_size); if (ret < 0) { metal_err("HOST: Failed to clear host to remote descriptor area.\n"); goto out; } - ret = metal_io_block_set(ch->remote_to_host_desc_io, 0, 0, SHM1_DESC_SIZE); + ret = metal_io_block_set(ch->remote_to_host_desc_io, 0, 0, + ch->desc1_size); if (ret < 0) { metal_err("HOST: Failed to clear remote to host descriptor area.\n"); goto out; @@ -189,10 +180,10 @@ static int irq_shmem_echo(struct channel_s *ch) tx_avail_offset = SHM_DESC_AVAIL_OFFSET; rx_avail_offset = SHM_DESC_AVAIL_OFFSET; rx_used_offset = SHM_DESC_USED_OFFSET; - tx_addr_offset = H_TO_R_DESC_ADDR_START; - rx_addr_offset = R_TO_H_DESC_ADDR_START; - tx_data_offset = H_TO_R_PAYLOAD_START; - rx_data_offset = R_TO_H_PAYLOAD_START; + tx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET; + rx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET; + tx_data_offset = h_to_r_payload_start; + rx_data_offset = r_to_h_payload_start; metal_info("HOST: Start echo flood testing....\n"); metal_info("HOST: Sending msgs to the remote.\n"); @@ -225,11 +216,12 @@ static int irq_shmem_echo(struct channel_s *ch) goto out; } - metal_io_write32(desc_host_to_remote, tx_addr_offset, tx_phy_addr_32); + metal_io_write32(desc_host_to_remote, tx_addr_offset, + tx_phy_addr_32); tx_data_offset += sizeof(struct msg_hdr_s) + msg_hdr->len; tx_addr_offset += sizeof(uint32_t); - if (tx_addr_offset >= H_TO_R_DESC_ADDR_END) - tx_addr_offset = H_TO_R_DESC_ADDR_START; + if (tx_addr_offset >= h_to_r_desc_addr_end) + tx_addr_offset = SHM_DESC_ADDR_ARRAY_OFFSET; /* Increase number of available buffers */ metal_io_write32(desc_host_to_remote, tx_avail_offset, (i + 1)); @@ -238,7 +230,7 @@ static int irq_shmem_echo(struct channel_s *ch) } metal_info("HOST: Waiting for messages to echo back and verify.\n"); i = 0; - tx_data_offset = H_TO_R_PAYLOAD_START; + tx_data_offset = h_to_r_payload_start; while (i != PKGS_TOTAL) { @@ -314,8 +306,11 @@ static int irq_shmem_echo(struct channel_s *ch) } tx_data_offset += sizeof(*msg_hdr) + sizeof(tstart); + if (tx_data_offset >= h_to_r_payload_end) + tx_data_offset = h_to_r_payload_start; /* Compare the received message and the sent message */ - ret = memcmp(rxbuf, txbuf, sizeof(*msg_hdr) + sizeof(tstart)); + ret = memcmp(rxbuf, txbuf, + sizeof(*msg_hdr) + sizeof(tstart)); if (ret) { metal_err("HOST: data[%u] verification failed.\n", i); metal_info("HOST: Expected:"); diff --git a/examples/libmetal/machine/host/amd_linux_userspace/common.h b/examples/libmetal/machine/host/amd_linux_userspace/common.h index a73f5ae8..7c440c44 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/common.h +++ b/examples/libmetal/machine/host/amd_linux_userspace/common.h @@ -7,6 +7,7 @@ #ifndef __COMMON_H__ #define __COMMON_H__ +#include #include #include @@ -17,7 +18,6 @@ #include #include "irq_shmem_demo.h" -#include "config.h" /* * Apply this snippet to the device tree in an overlay so that Linux userspace can diff --git a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c index 80950587..f912d3ec 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c +++ b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c @@ -4,15 +4,156 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include +#include +#include +#include +#include #include +#include #include #include +#include #include #include #include "common.h" static struct metal_device *rpu_to_apu_desc_dev, *apu_to_rpu_desc_dev; static struct metal_device *shm_dev, *ipi_dev, *ttc_dev; + +#define APP_UIO_BUS_NAME "uio" +#define APP_UIO_CLASS_PATH "/sys/class/uio" + +#define APP_SHM_DEV_NAME "libmetal-data" +#define APP_SHM0_DESC_DEV_NAME "libmetal-desc0" +#define APP_SHM1_DESC_DEV_NAME "libmetal-desc1" +#define APP_IPI_DEV_NAME "libmetal-ipi" +#define APP_TTC_DEV_NAME "libmetal-timer" + +#define APP_IPI_REMOTE_MASK_PROP "libmetal,uio-ipi-bitmask" + +static int app_read_first_line(const char *path, char *output, size_t output_len) +{ + FILE *fp; + char *newline; + + if (!path || !output || output_len < 2) + return -EINVAL; + + fp = fopen(path, "r"); + if (!fp) + return -errno; + + if (!fgets(output, output_len, fp)) { + int err = ferror(fp) ? -errno : -ENODATA; + + fclose(fp); + return err; + } + + fclose(fp); + + newline = strchr(output, '\n'); + if (newline) + *newline = '\0'; + + return 0; +} + +static int app_uio_find_path(const char *uio_name, char *uio_path, + size_t uio_path_len) +{ + DIR *dir; + struct dirent *entry; + char path[PATH_MAX]; + char value[PATH_MAX]; + bool found = false; + int ret = -ENODEV; + + if (!uio_name || !strlen(uio_name) || !uio_path || !uio_path_len) + return -EINVAL; + + dir = opendir(APP_UIO_CLASS_PATH); + if (!dir) + return -errno; + + while ((entry = readdir(dir)) != NULL) { + if (strncmp(entry->d_name, "uio", 3) != 0) + continue; + + ret = snprintf(path, sizeof(path), "%s/%s/name", + APP_UIO_CLASS_PATH, entry->d_name); + if (ret < 0 || ret >= (int)sizeof(path)) { + ret = -EOVERFLOW; + goto out; + } + + ret = app_read_first_line(path, value, sizeof(value)); + if (ret) + continue; + + if (strcmp(value, uio_name) != 0) + continue; + + if (found) { + ret = -EEXIST; + goto out; + } + found = true; + + ret = snprintf(uio_path, uio_path_len, "%s/%s", + APP_UIO_CLASS_PATH, entry->d_name); + if (ret < 0 || ret >= (int)uio_path_len) { + ret = -EOVERFLOW; + goto out; + } + } + + ret = found ? 0 : -ENODEV; + +out: + closedir(dir); + return ret; +} + +static int app_uio_read_dt_u32(const char *uio_name, const char *property, + uint32_t *value) +{ + unsigned char raw[4]; + char uio_path[PATH_MAX]; + char path[PATH_MAX]; + FILE *fp; + size_t len; + int ret; + + if (!uio_name || !property || !value) + return -EINVAL; + + ret = app_uio_find_path(uio_name, uio_path, sizeof(uio_path)); + if (ret) + return ret; + + ret = snprintf(path, sizeof(path), "%s/device/of_node/%s", + uio_path, property); + if (ret < 0 || ret >= (int)sizeof(path)) + return -EOVERFLOW; + + fp = fopen(path, "rb"); + if (!fp) + return -errno; + + len = fread(raw, 1, sizeof(raw), fp); + fclose(fp); + if (len != sizeof(raw)) + return -ENODATA; + + *value = ((uint32_t)raw[0] << 24) | + ((uint32_t)raw[1] << 16) | + ((uint32_t)raw[2] << 8) | + (uint32_t)raw[3]; + + return 0; +} /** * @brief close_metal_devices() - close libmetal devices * This function closes all the libmetal devices which have @@ -22,23 +163,33 @@ static struct metal_device *shm_dev, *ipi_dev, *ttc_dev; static void close_metal_devices(void) { /* Close shared memory device */ - if (shm_dev) + if (shm_dev) { metal_device_close(shm_dev); + shm_dev = NULL; + } /* Close IPI device */ - if (ipi_dev) + if (ipi_dev) { metal_device_close(ipi_dev); + ipi_dev = NULL; + } /* Close TTC device */ - if (ttc_dev) + if (ttc_dev) { metal_device_close(ttc_dev); + ttc_dev = NULL; + } /* Close descriptor devices */ - if (rpu_to_apu_desc_dev) + if (rpu_to_apu_desc_dev) { metal_device_close(rpu_to_apu_desc_dev); + rpu_to_apu_desc_dev = NULL; + } - if (apu_to_rpu_desc_dev) + if (apu_to_rpu_desc_dev) { metal_device_close(apu_to_rpu_desc_dev); + apu_to_rpu_desc_dev = NULL; + } } /** @@ -52,38 +203,38 @@ int open_metal_devices(void) int ret; /* Open shared memory device */ - ret = metal_device_open(BUS_NAME, SHM_DEV_NAME, &shm_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM_DEV_NAME, &shm_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", SHM_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", APP_SHM_DEV_NAME); goto out; } /* Open descriptor devices */ - ret = metal_device_open(BUS_NAME, SHM0_DESC_DEV_NAME, + ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM0_DESC_DEV_NAME, &apu_to_rpu_desc_dev); if (ret) { - metal_err("Failed to open device %s.\n", SHM0_DESC_DEV_NAME); + metal_err("Failed to open device %s.\n", APP_SHM0_DESC_DEV_NAME); goto out; } - ret = metal_device_open(BUS_NAME, SHM1_DESC_DEV_NAME, + ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM1_DESC_DEV_NAME, &rpu_to_apu_desc_dev); if (ret) { - metal_err("Failed to open device %s.\n", SHM1_DESC_DEV_NAME); + metal_err("Failed to open device %s.\n", APP_SHM1_DESC_DEV_NAME); goto out; } /* Open IPI device */ - ret = metal_device_open(BUS_NAME, IPI_DEV_NAME, &ipi_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, APP_IPI_DEV_NAME, &ipi_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", IPI_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", APP_IPI_DEV_NAME); goto out; } /* Open TTC device */ - ret = metal_device_open(BUS_NAME, TTC_DEV_NAME, &ttc_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, APP_TTC_DEV_NAME, &ttc_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", TTC_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", APP_TTC_DEV_NAME); goto out; } @@ -96,7 +247,7 @@ static int irq_isr(int vect_id, void *priv) struct channel_s *ch = (struct channel_s *)priv; struct channel_machine_ctx_s *machine = channel_machine_ctx(ch); struct metal_io_region *ipi_io = ch->ipi_io; - uint32_t ipi_mask = IPI_MASK; + uint32_t ipi_mask = ch->ipi_mask; uint64_t val = 1; (void)vect_id; @@ -116,6 +267,7 @@ int platform_init(struct channel_s *ch) { struct metal_init_params init_param = METAL_INIT_DEFAULTS; struct channel_machine_ctx_s *machine = channel_machine_ctx(ch); + uint32_t ipi_mask; int ret; ret = metal_init(&init_param); @@ -131,14 +283,15 @@ int platform_init(struct channel_s *ch) ret = open_metal_devices(); if (ret) { metal_err("HOST: Failed to open devices\n"); - return ret; + goto out_close; } /* Get shared memory device IO region */ ch->shm_io = metal_device_io_region(shm_dev, 0); if (!ch->shm_io) { metal_err("HOST: Failed to map io region for %s.\n", shm_dev->name); - return -ENODEV; + ret = -ENODEV; + goto out_close; } /* Get descriptor IO Regions */ @@ -146,52 +299,80 @@ int platform_init(struct channel_s *ch) if (!ch->host_to_remote_desc_io) { metal_err("Failed to map io region for %s.\n", apu_to_rpu_desc_dev->name); - return -ENODEV; + ret = -ENODEV; + goto out_close; } ch->remote_to_host_desc_io = metal_device_io_region(rpu_to_apu_desc_dev, 0); if (!ch->remote_to_host_desc_io) { metal_err("Failed to map io region for %s.\n", rpu_to_apu_desc_dev->name); - return -ENODEV; + ret = -ENODEV; + goto out_close; } /* Get IPI device IO region */ ch->ipi_io = metal_device_io_region(ipi_dev, 0); if (!ch->ipi_io) { metal_err("HOST: Failed to map io region for %s.\n", ipi_dev->name); - return -ENODEV; + ret = -ENODEV; + goto out_close; } - /* Get the IPI IRQ from the opened IPI device */ - ch->ipi_mask = IPI_MASK; + ch->desc0_size = (uint32_t)metal_io_region_size(ch->host_to_remote_desc_io); + ch->desc1_size = (uint32_t)metal_io_region_size(ch->remote_to_host_desc_io); + ch->shm_payload_size = (uint32_t)metal_io_region_size(ch->shm_io); + if (!ch->desc0_size || !ch->desc1_size || !ch->shm_payload_size) { + metal_err("HOST: Invalid descriptor or payload region size.\n"); + ret = -EINVAL; + goto out_close; + } + + ret = app_uio_read_dt_u32(APP_IPI_DEV_NAME, APP_IPI_REMOTE_MASK_PROP, + &ipi_mask); + if (ret) { + metal_err("HOST: Failed to read %s for %s.\n", + APP_IPI_REMOTE_MASK_PROP, APP_IPI_DEV_NAME); + goto out_close; + } + ch->ipi_mask = ipi_mask; /* Get TTC IO region */ ch->ttc_io = metal_device_io_region(ttc_dev, 0); if (!ch->ttc_io) { metal_err("HOST: Failed to map io region for %s.\n", ttc_dev->name); - return -ENODEV; + ret = -ENODEV; + goto out_close; } /* Get the IPI IRQ from the opened IPI device */ ch->irq_vector_id = (intptr_t)ipi_dev->irq_info; /* disable IPI interrupt */ - metal_io_write32(ch->ipi_io, IPI_IDR_OFFSET, IPI_MASK); + metal_io_write32(ch->ipi_io, IPI_IDR_OFFSET, ch->ipi_mask); /* clear old IPI interrupt */ - metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET, IPI_MASK); + metal_io_write32(ch->ipi_io, IPI_ISR_OFFSET, ch->ipi_mask); /* Register IPI irq handler */ - metal_irq_register(ch->irq_vector_id, irq_isr, ch); + ret = metal_irq_register(ch->irq_vector_id, irq_isr, ch); + if (ret) { + metal_err("HOST: Failed to register IRQ handler.\n"); + goto out_close; + } metal_irq_enable(ch->irq_vector_id); /* Enable IPI interrupt */ - metal_io_write32(ch->ipi_io, IPI_IER_OFFSET, IPI_MASK); + metal_io_write32(ch->ipi_io, IPI_IER_OFFSET, ch->ipi_mask); return 0; + +out_close: + close_metal_devices(); + metal_finish(); + return ret; } void platform_cleanup(struct channel_s *ch) { /* disable IPI interrupt */ - metal_io_write32(ch->ipi_io, IPI_IDR_OFFSET, IPI_MASK); + metal_io_write32(ch->ipi_io, IPI_IDR_OFFSET, ch->ipi_mask); /* unregister IPI irq handler by setting the handler to 0 */ metal_irq_disable(ch->irq_vector_id); metal_irq_unregister(ch->irq_vector_id); diff --git a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h index 3633f8ea..e856c86c 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h +++ b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h @@ -7,7 +7,7 @@ #ifndef __PLATFORM_INIT_H__ #define __PLATFORM_INIT_H__ -#include "platform_init.h" +struct channel_s; int platform_init(struct channel_s *ch); void platform_cleanup(struct channel_s *ch); From 26d918014f4ec9fad678187facabe24224c6fc29 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Wed, 5 Aug 2026 08:03:18 -0700 Subject: [PATCH 2/3] examples: libmetal: amd_linux_userspace: remove host config header flow Remove amd_linux_userspace host-side config header generation now that the Linux demo opens fixed logical UIO devices and derives the remaining runtime values directly from libmetal IO regions and the backing device tree. Drop config.h.in and the configure-time CMake path, and update the Linux host README to document the required UIO logical names, the libmetal,ipi-remote-mask property, and the direct run flow. Signed-off-by: Ben Levinsky --- .../host/amd_linux_userspace/CMakeLists.txt | 11 ------- .../host/amd_linux_userspace/README.md | 30 +++++++++++-------- .../host/amd_linux_userspace/config.h.in | 27 ----------------- 3 files changed, 17 insertions(+), 51 deletions(-) delete mode 100644 examples/libmetal/machine/host/amd_linux_userspace/config.h.in diff --git a/examples/libmetal/machine/host/amd_linux_userspace/CMakeLists.txt b/examples/libmetal/machine/host/amd_linux_userspace/CMakeLists.txt index 42cf77fe..17b2b0bd 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/CMakeLists.txt +++ b/examples/libmetal/machine/host/amd_linux_userspace/CMakeLists.txt @@ -1,17 +1,6 @@ # Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. # SPDX-License-Identifier: BSD-3-Clause -include (CheckIncludeFiles) -include (CheckCSourceCompiles) include (${APPS_ROOT_DIR}/../legacy_apps/cmake/options.cmake) -include (${APPS_ROOT_DIR}/../legacy_apps/cmake/collect.cmake) - -get_property (DEMO_CFG_FILE GLOBAL PROPERTY DEMO_CFG_FILE) -if (EXISTS ${DEMO_CFG_FILE}) - include(${DEMO_CFG_FILE}) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" - "${CMAKE_CURRENT_SOURCE_DIR}/config.h") - message("WARNING: DEMO_CFG_FILE ${DEMO_CFG_FILE} passed in at configure time. Using this to configure demo.") -endif() collect (APP_COMMON_SOURCES platform_init.c) collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/examples/libmetal/machine/host/amd_linux_userspace/README.md b/examples/libmetal/machine/host/amd_linux_userspace/README.md index d11f672d..d12e8d69 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/README.md +++ b/examples/libmetal/machine/host/amd_linux_userspace/README.md @@ -19,14 +19,15 @@ messages. - Signals completion via IPI and then disables the interrupt and releases the mapped devices. ## Prerequisites -- Linux kernel exposes the shared memory carveouts and descriptor UIOs - (`9868000.shm`, `9860000.shm_desc`, `9864000.shm_desc` by default; adjust to - the platform-specific devices/offsets), along with IPI and TTC peripherals, - to userspace with permissions suitable for the demo binary. +- Linux kernel exposes the shared memory carveouts and descriptor UIOs to + userspace with stable logical names: + `libmetal-data`, `libmetal-desc0`, `libmetal-desc1`, `libmetal-ipi`, and + `libmetal-timer`. +- The host IPI UIO node carries a `libmetal,ipi-remote-mask` device-tree + property so the demo can discover the platform-specific interrupt bit at + runtime. - libmetal (and dependent libraries) installed on the host system, as well as the `metal_xlnx_extension` library when required by the platform glue. -- The project toolchain file defines the correct platform macro (for example, - `-DPLATFORM_ZYNQMP`) so `common.h` selects the matching peripheral map. - Remote firmware is already loaded and waiting for interrupts before the host demo starts. @@ -51,22 +52,25 @@ The static executable is emitted at ## Run 1. Start the remote firmware so it sits in the notification loop. -2. Launch the host binary (root/sudo may be required for IPI device access): +2. Confirm the expected logical UIO names are visible: + ```bash + cat /sys/class/uio/uio*/name + ``` +3. Launch the host binary (root/sudo may be required for IPI device access): ```bash ./irq_shmem_demo-static ``` -3. Observe the console output for packet progress and the final average +4. Observe the console output for packet progress and the final average round-trip latency. ## [Shared Memory Layout](../../../demos/irq_shmem_demo/README.md#shared-memory-layout) Shared buffer map used by both sides of the demo. ## Troubleshooting -- **Hangs waiting for notification**: ensure the IPI mask configured in - `common.h` (or overridden via the optional demo config file) matches the - remote firmware and that the host process can write to the IPI device. +- **Hangs waiting for notification**: ensure the host IPI UIO node exposes + `libmetal,ipi-remote-mask`, that the host process can write to the IPI device, + and that the remote firmware uses the matching interrupt bit. - **Shared-memory access errors**: confirm the UIO entries expose the expected - descriptor/payload ranges (`0x09860000` base) with read/write permissions for - the demo user. + descriptor and payload regions with read/write permissions for the demo user. - **Mismatched payloads**: verify both sides agree on descriptor offsets and the `PKGS_TOTAL` value compiled into each binary. diff --git a/examples/libmetal/machine/host/amd_linux_userspace/config.h.in b/examples/libmetal/machine/host/amd_linux_userspace/config.h.in deleted file mode 100644 index 12de8f05..00000000 --- a/examples/libmetal/machine/host/amd_linux_userspace/config.h.in +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -/* UIO device names */ -#cmakedefine SHM_DEV_NAME "@SHM_DEV_NAME@" -#cmakedefine SHM0_DESC_DEV_NAME "@SHM0_DESC_DEV_NAME@" -#cmakedefine SHM1_DESC_DEV_NAME "@SHM1_DESC_DEV_NAME@" - -/* Carveout layout */ -#cmakedefine SHM_IMAGE_BASE @SHM_IMAGE_BASE@ -#cmakedefine SHM_IMAGE_SIZE @SHM_IMAGE_SIZE@ -#cmakedefine SHM0_DESC_BASE @SHM0_DESC_BASE@ -#cmakedefine SHM0_DESC_SIZE @SHM0_DESC_SIZE@ -#cmakedefine SHM1_DESC_BASE @SHM1_DESC_BASE@ -#cmakedefine SHM1_DESC_SIZE @SHM1_DESC_SIZE@ -#cmakedefine SHM_PAYLOAD_BASE @SHM_PAYLOAD_BASE@ -#cmakedefine SHM_PAYLOAD_SIZE @SHM_PAYLOAD_SIZE@ -#cmakedefine SHM_PAYLOAD_TX_OFFSET @SHM_PAYLOAD_TX_OFFSET@ -#cmakedefine SHM_PAYLOAD_RX_OFFSET @SHM_PAYLOAD_RX_OFFSET@ - -/* Peripheral names/masks */ -#cmakedefine IPI_DEV_NAME "@IPI_DEV_NAME@" -#cmakedefine IPI_MASK @IPI_MASK@ -#cmakedefine TTC_DEV_NAME "@TTC_DEV_NAME@" - -#cmakedefine BUS_NAME "@BUS_NAME@" -#endif /* CONFIG_H */ From a64e3e6cd8f4c87938e6ce629d36f75321bc0b56 Mon Sep 17 00:00:00 2001 From: Ben Levinsky Date: Wed, 5 Aug 2026 08:06:33 -0700 Subject: [PATCH 3/3] examples: libmetal: amd_linux_userspace: add command-line UIO overrides Allow the Linux host irq_shmem_demo to override the default logical UIO names and the IPI remote-mask property from the command line while keeping the stable libmetal defaults. Plumb a small platform options struct through platform_init(), parse long-form host arguments in main(), and document the override flow in the host README. --- .../irq_shmem_demo/host/irq_shmem_demo.c | 93 ++++++++++++++++++- .../host/amd_linux_userspace/README.md | 6 ++ .../host/amd_linux_userspace/platform_init.c | 65 ++++++++----- .../host/amd_linux_userspace/platform_init.h | 12 ++- 4 files changed, 148 insertions(+), 28 deletions(-) diff --git a/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c b/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c index 5c6163b1..0d45d180 100644 --- a/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c +++ b/examples/libmetal/demos/irq_shmem_demo/host/irq_shmem_demo.c @@ -24,8 +24,9 @@ * amd_linux_userspace/README.md. */ -#include +#include #include +#include #include #include #include @@ -54,6 +55,83 @@ struct msg_hdr_s { uint32_t len; }; +enum { + OPT_SHM_DEV = 1000, + OPT_DESC0_DEV, + OPT_DESC1_DEV, + OPT_IPI_DEV, + OPT_TTC_DEV, + OPT_IPI_MASK_PROP, +}; + +static void usage(const char *prog, const struct app_platform_options *options) +{ + fprintf(stderr, + "Usage: %s [options]\n" + " --shm-dev Shared-memory UIO name (default: %s)\n" + " --desc0-dev Host-to-remote descriptor UIO name (default: %s)\n" + " --desc1-dev Remote-to-host descriptor UIO name (default: %s)\n" + " --ipi-dev IPI UIO name (default: %s)\n" + " --ttc-dev Timer UIO name (default: %s)\n" + " --ipi-mask-prop IPI remote-mask DT property (default: %s)\n" + " -h, --help Show this help text\n", + prog, options->shm_dev_name, options->desc0_dev_name, + options->desc1_dev_name, options->ipi_dev_name, + options->ttc_dev_name, options->ipi_remote_mask_property); +} + +static int parse_args(int argc, char **argv, + struct app_platform_options *options) +{ + static const struct option long_options[] = { + { "shm-dev", required_argument, NULL, OPT_SHM_DEV }, + { "desc0-dev", required_argument, NULL, OPT_DESC0_DEV }, + { "desc1-dev", required_argument, NULL, OPT_DESC1_DEV }, + { "ipi-dev", required_argument, NULL, OPT_IPI_DEV }, + { "ttc-dev", required_argument, NULL, OPT_TTC_DEV }, + { "ipi-mask-prop", required_argument, NULL, OPT_IPI_MASK_PROP }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + int c; + + while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { + switch (c) { + case OPT_SHM_DEV: + options->shm_dev_name = optarg; + break; + case OPT_DESC0_DEV: + options->desc0_dev_name = optarg; + break; + case OPT_DESC1_DEV: + options->desc1_dev_name = optarg; + break; + case OPT_IPI_DEV: + options->ipi_dev_name = optarg; + break; + case OPT_TTC_DEV: + options->ttc_dev_name = optarg; + break; + case OPT_IPI_MASK_PROP: + options->ipi_remote_mask_property = optarg; + break; + case 'h': + usage(argv[0], options); + return 1; + default: + usage(argv[0], options); + return -EINVAL; + } + } + + if (optind != argc) { + usage(argv[0], options); + return -EINVAL; + } + + return 0; +} + /** * @brief wait_for_notified() - Loop until notified bit in channel is set. * @@ -370,16 +448,25 @@ static int irq_shmem_echo(struct channel_s *ch) return ret; } -int main(void) +int main(int argc, char **argv) { struct channel_machine_ctx_s ch_machine_s = {0}; + struct app_platform_options platform_options; struct channel_s ch_s = { .machine_ctx = &ch_machine_s, }; int ret = 0; + platform_get_default_options(&platform_options); + ret = parse_args(argc, argv, &platform_options); + if (ret) { + if (ret > 0) + return 0; + return ret; + } + /* platform_init will set the OS agnostic channel information */ - ret = platform_init(&ch_s); + ret = platform_init(&ch_s, &platform_options); if (ret) { metal_err("HOST: Failed to initialize system.\n"); return ret; diff --git a/examples/libmetal/machine/host/amd_linux_userspace/README.md b/examples/libmetal/machine/host/amd_linux_userspace/README.md index d12e8d69..14ebe466 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/README.md +++ b/examples/libmetal/machine/host/amd_linux_userspace/README.md @@ -60,6 +60,12 @@ The static executable is emitted at ```bash ./irq_shmem_demo-static ``` + Override individual logical names when the platform uses different UIO + aliases: + ```bash + ./irq_shmem_demo-static --shm-dev my-data --desc0-dev my-desc0 \ + --desc1-dev my-desc1 --ipi-dev my-ipi --ttc-dev my-timer + ``` 4. Observe the console output for packet progress and the final average round-trip latency. diff --git a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c index f912d3ec..db1185d9 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c +++ b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.c @@ -17,20 +17,21 @@ #include #include #include "common.h" +#include "platform_init.h" static struct metal_device *rpu_to_apu_desc_dev, *apu_to_rpu_desc_dev; static struct metal_device *shm_dev, *ipi_dev, *ttc_dev; - #define APP_UIO_BUS_NAME "uio" #define APP_UIO_CLASS_PATH "/sys/class/uio" -#define APP_SHM_DEV_NAME "libmetal-data" -#define APP_SHM0_DESC_DEV_NAME "libmetal-desc0" -#define APP_SHM1_DESC_DEV_NAME "libmetal-desc1" -#define APP_IPI_DEV_NAME "libmetal-ipi" -#define APP_TTC_DEV_NAME "libmetal-timer" - -#define APP_IPI_REMOTE_MASK_PROP "libmetal,uio-ipi-bitmask" +static const struct app_platform_options app_default_platform_options = { + .shm_dev_name = "libmetal-data", + .desc0_dev_name = "libmetal-desc0", + .desc1_dev_name = "libmetal-desc1", + .ipi_dev_name = "libmetal-ipi", + .ttc_dev_name = "libmetal-timer", + .ipi_remote_mask_property = "libmetal,uio-ipi-bitmask", +}; static int app_read_first_line(const char *path, char *output, size_t output_len) { @@ -154,6 +155,14 @@ static int app_uio_read_dt_u32(const char *uio_name, const char *property, return 0; } + +void platform_get_default_options(struct app_platform_options *options) +{ + if (!options) + return; + + *options = app_default_platform_options; +} /** * @brief close_metal_devices() - close libmetal devices * This function closes all the libmetal devices which have @@ -198,43 +207,46 @@ static void close_metal_devices(void) * * @return 0 - succeeded, non-zero for failures. */ -int open_metal_devices(void) +static int open_metal_devices(const struct app_platform_options *options) { int ret; /* Open shared memory device */ - ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM_DEV_NAME, &shm_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, options->shm_dev_name, &shm_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", APP_SHM_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", + options->shm_dev_name); goto out; } /* Open descriptor devices */ - ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM0_DESC_DEV_NAME, + ret = metal_device_open(APP_UIO_BUS_NAME, options->desc0_dev_name, &apu_to_rpu_desc_dev); if (ret) { - metal_err("Failed to open device %s.\n", APP_SHM0_DESC_DEV_NAME); + metal_err("Failed to open device %s.\n", options->desc0_dev_name); goto out; } - ret = metal_device_open(APP_UIO_BUS_NAME, APP_SHM1_DESC_DEV_NAME, + ret = metal_device_open(APP_UIO_BUS_NAME, options->desc1_dev_name, &rpu_to_apu_desc_dev); if (ret) { - metal_err("Failed to open device %s.\n", APP_SHM1_DESC_DEV_NAME); + metal_err("Failed to open device %s.\n", options->desc1_dev_name); goto out; } /* Open IPI device */ - ret = metal_device_open(APP_UIO_BUS_NAME, APP_IPI_DEV_NAME, &ipi_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, options->ipi_dev_name, &ipi_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", APP_IPI_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", + options->ipi_dev_name); goto out; } /* Open TTC device */ - ret = metal_device_open(APP_UIO_BUS_NAME, APP_TTC_DEV_NAME, &ttc_dev); + ret = metal_device_open(APP_UIO_BUS_NAME, options->ttc_dev_name, &ttc_dev); if (ret) { - metal_err("HOST: Failed to open device %s.\n", APP_TTC_DEV_NAME); + metal_err("HOST: Failed to open device %s.\n", + options->ttc_dev_name); goto out; } @@ -263,13 +275,17 @@ static int irq_isr(int vect_id, void *priv) return METAL_IRQ_NOT_HANDLED; } -int platform_init(struct channel_s *ch) +int platform_init(struct channel_s *ch, + const struct app_platform_options *options) { struct metal_init_params init_param = METAL_INIT_DEFAULTS; struct channel_machine_ctx_s *machine = channel_machine_ctx(ch); uint32_t ipi_mask; int ret; + if (!options) + options = &app_default_platform_options; + ret = metal_init(&init_param); if (ret) { metal_err("HOST: Failed to initialize libmetal\n"); @@ -280,7 +296,7 @@ int platform_init(struct channel_s *ch) machine->remote_nkicked = (atomic_flag)ATOMIC_FLAG_INIT; atomic_flag_test_and_set(&machine->remote_nkicked); - ret = open_metal_devices(); + ret = open_metal_devices(options); if (ret) { metal_err("HOST: Failed to open devices\n"); goto out_close; @@ -327,11 +343,12 @@ int platform_init(struct channel_s *ch) goto out_close; } - ret = app_uio_read_dt_u32(APP_IPI_DEV_NAME, APP_IPI_REMOTE_MASK_PROP, - &ipi_mask); + ret = app_uio_read_dt_u32(options->ipi_dev_name, + options->ipi_remote_mask_property, &ipi_mask); if (ret) { metal_err("HOST: Failed to read %s for %s.\n", - APP_IPI_REMOTE_MASK_PROP, APP_IPI_DEV_NAME); + options->ipi_remote_mask_property, + options->ipi_dev_name); goto out_close; } ch->ipi_mask = ipi_mask; diff --git a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h index e856c86c..24a2e81e 100644 --- a/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h +++ b/examples/libmetal/machine/host/amd_linux_userspace/platform_init.h @@ -8,8 +8,18 @@ #define __PLATFORM_INIT_H__ struct channel_s; +struct app_platform_options { + const char *shm_dev_name; + const char *desc0_dev_name; + const char *desc1_dev_name; + const char *ipi_dev_name; + const char *ttc_dev_name; + const char *ipi_remote_mask_property; +}; -int platform_init(struct channel_s *ch); +void platform_get_default_options(struct app_platform_options *options); +int platform_init(struct channel_s *ch, + const struct app_platform_options *options); void platform_cleanup(struct channel_s *ch); #endif /* __PLATFORM_INIT_H__ */