]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
accel/amdxdna: Support firmware debug buffer
authorLizhi Hou <lizhi.hou@amd.com>
Thu, 16 Oct 2025 20:30:16 +0000 (13:30 -0700)
committerLizhi Hou <lizhi.hou@amd.com>
Mon, 20 Oct 2025 16:07:12 +0000 (09:07 -0700)
To collect firmware debug information, the userspace application allocates
a AMDXDNA_BO_DEV buffer object through DRM_IOCTL_AMDXDNA_CREATE_BO.
Then it associates the buffer with the hardware context through
DRM_IOCTL_AMDXDNA_CONFIG_HWCTX which requests firmware to bind the buffer
through a mailbox command. The firmware then writes the debug data into
this buffer. The buffer can be mapped into userspace so that
applications can retrieve and analyze the firmware debug information.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Lizhi Hou <lizhi.hou@amd.com>
Link: https://lore.kernel.org/r/20251016203016.819441-1-lizhi.hou@amd.com
14 files changed:
drivers/accel/amdxdna/TODO
drivers/accel/amdxdna/aie2_ctx.c
drivers/accel/amdxdna/aie2_message.c
drivers/accel/amdxdna/aie2_msg_priv.h
drivers/accel/amdxdna/aie2_pci.c
drivers/accel/amdxdna/aie2_pci.h
drivers/accel/amdxdna/amdxdna_ctx.c
drivers/accel/amdxdna/amdxdna_ctx.h
drivers/accel/amdxdna/amdxdna_gem.c
drivers/accel/amdxdna/amdxdna_gem.h
drivers/accel/amdxdna/amdxdna_pci_drv.c
drivers/accel/amdxdna/amdxdna_pci_drv.h
drivers/accel/amdxdna/npu1_regs.c
drivers/accel/amdxdna/npu4_regs.c

index ad8ac6e315b6b279beea9b5d13d55c53d3a3b440..0e4bbebeaedfceb7206ba4314a3233363eb52854 100644 (file)
@@ -1,2 +1 @@
 - Add debugfs support
-- Add debug BO support
index ab4d66f1325dfacb48304891c51226dfb48b0950..63450b7773ac1db230e15d4ae2c05535311b5698 100644 (file)
@@ -226,11 +226,10 @@ out:
 }
 
 static int
-aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
+aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
 {
        struct amdxdna_sched_job *job = handle;
        int ret = 0;
-       u32 status;
 
        if (unlikely(!data))
                goto out;
@@ -240,8 +239,7 @@ aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
                goto out;
        }
 
-       status = readl(data);
-       XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
+       job->drv_cmd->result = readl(data);
 
 out:
        aie2_sched_notify(job);
@@ -314,8 +312,18 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
        kref_get(&job->refcnt);
        fence = dma_fence_get(job->fence);
 
-       if (unlikely(!cmd_abo)) {
-               ret = aie2_sync_bo(hwctx, job, aie2_sched_nocmd_resp_handler);
+       if (job->drv_cmd) {
+               switch (job->drv_cmd->opcode) {
+               case SYNC_DEBUG_BO:
+                       ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+                       break;
+               case ATTACH_DEBUG_BO:
+                       ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+               }
                goto out;
        }
 
@@ -766,6 +774,74 @@ free_cus:
        return ret;
 }
 
+static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
+{
+       struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
+
+       if (!out_fence) {
+               XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
+               return;
+       }
+
+       dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
+       dma_fence_put(out_fence);
+}
+
+static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
+                                  bool attach)
+{
+       struct amdxdna_client *client = hwctx->client;
+       struct amdxdna_dev *xdna = client->xdna;
+       struct amdxdna_drv_cmd cmd = { 0 };
+       struct amdxdna_gem_obj *abo;
+       u64 seq;
+       int ret;
+
+       abo = amdxdna_gem_get_obj(client, bo_hdl, AMDXDNA_BO_DEV);
+       if (!abo) {
+               XDNA_ERR(xdna, "Get bo %d failed", bo_hdl);
+               return -EINVAL;
+       }
+
+       if (attach) {
+               if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
+                       ret = -EBUSY;
+                       goto put_obj;
+               }
+               cmd.opcode = ATTACH_DEBUG_BO;
+       } else {
+               if (abo->assigned_hwctx != hwctx->id) {
+                       ret = -EINVAL;
+                       goto put_obj;
+               }
+               cmd.opcode = DETACH_DEBUG_BO;
+       }
+
+       ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+                                &bo_hdl, 1, hwctx->id, &seq);
+       if (ret) {
+               XDNA_ERR(xdna, "Submit command failed");
+               goto put_obj;
+       }
+
+       aie2_cmd_wait(hwctx, seq);
+       if (cmd.result) {
+               XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+               goto put_obj;
+       }
+
+       if (attach)
+               abo->assigned_hwctx = hwctx->id;
+       else
+               abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
+
+       XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
+
+put_obj:
+       amdxdna_gem_put_obj(abo);
+       return ret;
+}
+
 int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size)
 {
        struct amdxdna_dev *xdna = hwctx->client->xdna;
@@ -775,14 +851,40 @@ int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *bu
        case DRM_AMDXDNA_HWCTX_CONFIG_CU:
                return aie2_hwctx_cu_config(hwctx, buf, size);
        case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
+               return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, true);
        case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
-               return -EOPNOTSUPP;
+               return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, false);
        default:
                XDNA_DBG(xdna, "Not supported type %d", type);
                return -EOPNOTSUPP;
        }
 }
 
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
+{
+       struct amdxdna_client *client = hwctx->client;
+       struct amdxdna_dev *xdna = client->xdna;
+       struct amdxdna_drv_cmd cmd = { 0 };
+       u64 seq;
+       int ret;
+
+       cmd.opcode = SYNC_DEBUG_BO;
+       ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+                                &debug_bo_hdl, 1, hwctx->id, &seq);
+       if (ret) {
+               XDNA_ERR(xdna, "Submit command failed");
+               return ret;
+       }
+
+       aie2_cmd_wait(hwctx, seq);
+       if (cmd.result) {
+               XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int aie2_populate_range(struct amdxdna_gem_obj *abo)
 {
        struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
index 4660e8297ed800fc88feab09519ab6b95a80967e..0ec1dc6fe668108bf4c7484188ab55a34acbff33 100644 (file)
@@ -749,7 +749,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
        int ret = 0;
 
        req.src_addr = 0;
-       req.dst_addr = abo->mem.dev_addr - hwctx->client->dev_heap->mem.dev_addr;
+       req.dst_addr = amdxdna_dev_bo_offset(abo);
        req.size = abo->mem.size;
 
        /* Device to Host */
@@ -773,3 +773,32 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
 
        return 0;
 }
+
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+                        int (*notify_cb)(void *, void __iomem *, size_t))
+{
+       struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+       struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
+       struct amdxdna_dev *xdna = hwctx->client->xdna;
+       struct config_debug_bo_req req;
+       struct xdna_mailbox_msg msg;
+
+       if (job->drv_cmd->opcode == ATTACH_DEBUG_BO)
+               req.config = DEBUG_BO_REGISTER;
+       else
+               req.config = DEBUG_BO_UNREGISTER;
+
+       req.offset = amdxdna_dev_bo_offset(abo);
+       req.size = abo->mem.size;
+
+       XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d",
+                req.offset, req.size, req.config);
+
+       msg.handle = job;
+       msg.notify_cb = notify_cb;
+       msg.send_data = (u8 *)&req;
+       msg.send_size = sizeof(req);
+       msg.opcode = MSG_OP_CONFIG_DEBUG_BO;
+
+       return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+}
index 6df9065b13f68593899ea78c60db0c050ad17f95..cb53132029eb3c4b90071d2349ca545a994d12ea 100644 (file)
@@ -18,6 +18,7 @@ enum aie2_msg_opcode {
        MSG_OP_CONFIG_CU                   = 0x11,
        MSG_OP_CHAIN_EXEC_BUFFER_CF        = 0x12,
        MSG_OP_CHAIN_EXEC_DPU              = 0x13,
+       MSG_OP_CONFIG_DEBUG_BO             = 0x14,
        MSG_OP_MAX_XRT_OPCODE,
        MSG_OP_SUSPEND                     = 0x101,
        MSG_OP_RESUME                      = 0x102,
@@ -365,4 +366,21 @@ struct sync_bo_req {
 struct sync_bo_resp {
        enum aie2_msg_status    status;
 } __packed;
+
+#define DEBUG_BO_UNREGISTER 0
+#define DEBUG_BO_REGISTER   1
+struct config_debug_bo_req {
+       __u64   offset;
+       __u64   size;
+       /*
+        * config operations.
+        *   DEBUG_BO_REGISTER: Register debug buffer
+        *   DEBUG_BO_UNREGISTER: Unregister debug buffer
+        */
+       __u32   config;
+} __packed;
+
+struct config_debug_bo_resp {
+       enum aie2_msg_status    status;
+} __packed;
 #endif /* _AIE2_MSG_PRIV_H_ */
index cfca4e456b614a378a2f5dc540a570ffc5e24eaa..f48045318dc02406bde118703611583731081ba7 100644 (file)
@@ -1004,6 +1004,7 @@ const struct amdxdna_dev_ops aie2_ops = {
        .hwctx_init = aie2_hwctx_init,
        .hwctx_fini = aie2_hwctx_fini,
        .hwctx_config = aie2_hwctx_config,
+       .hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo,
        .cmd_submit = aie2_cmd_submit,
        .hmm_invalidate = aie2_hmm_invalidate,
        .get_array = aie2_get_array,
index 34bc35479f42d08c6aba5a8ba1c7161354158dc3..243ac21d50c11612f584c3477134e30f30545fbb 100644 (file)
@@ -287,11 +287,14 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
                               int (*notify_cb)(void *, void __iomem *, size_t));
 int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
                 int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+                        int (*notify_cb)(void *, void __iomem *, size_t));
 
 /* aie2_hwctx.c */
 int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
 void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
 int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
 void aie2_hwctx_suspend(struct amdxdna_client *client);
 int aie2_hwctx_resume(struct amdxdna_client *client);
 int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
index 868ca369e0a08a09fcdc268ff8abffb38ce8c9b0..d18182c596683ac1d6729b02c233ee7890a7cba6 100644 (file)
@@ -328,6 +328,38 @@ unlock_srcu:
        return ret;
 }
 
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
+{
+       struct amdxdna_dev *xdna = client->xdna;
+       struct amdxdna_hwctx *hwctx;
+       struct amdxdna_gem_obj *abo;
+       struct drm_gem_object *gobj;
+       int ret, idx;
+
+       if (!xdna->dev_info->ops->hwctx_sync_debug_bo)
+               return -EOPNOTSUPP;
+
+       gobj = drm_gem_object_lookup(client->filp, debug_bo_hdl);
+       if (!gobj)
+               return -EINVAL;
+
+       abo = to_xdna_obj(gobj);
+       guard(mutex)(&xdna->dev_lock);
+       idx = srcu_read_lock(&client->hwctx_srcu);
+       hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
+       if (!hwctx) {
+               ret = -EINVAL;
+               goto unlock_srcu;
+       }
+
+       ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
+
+unlock_srcu:
+       srcu_read_unlock(&client->hwctx_srcu, idx);
+       drm_gem_object_put(gobj);
+       return ret;
+}
+
 static void
 amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
 {
@@ -393,6 +425,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
 }
 
 int amdxdna_cmd_submit(struct amdxdna_client *client,
+                      struct amdxdna_drv_cmd *drv_cmd,
                       u32 cmd_bo_hdl, u32 *arg_bo_hdls, u32 arg_bo_cnt,
                       u32 hwctx_hdl, u64 *seq)
 {
@@ -406,6 +439,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
        if (!job)
                return -ENOMEM;
 
+       job->drv_cmd = drv_cmd;
+
        if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) {
                job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD);
                if (!job->cmd_bo) {
@@ -413,8 +448,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
                        ret = -EINVAL;
                        goto free_job;
                }
-       } else {
-               job->cmd_bo = NULL;
        }
 
        ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
@@ -508,7 +541,7 @@ static int amdxdna_drm_submit_execbuf(struct amdxdna_client *client,
                }
        }
 
-       ret = amdxdna_cmd_submit(client, cmd_bo_hdl, arg_bo_hdls,
+       ret = amdxdna_cmd_submit(client, NULL, cmd_bo_hdl, arg_bo_hdls,
                                 args->arg_count, args->hwctx, &args->seq);
        if (ret)
                XDNA_DBG(xdna, "Submit cmds failed, ret %d", ret);
index 7cd7a55936f09939fa7ea5a6e9d48c9169fc1e16..cbe60efbe60ba292cb3a8889145d7cf924b226a6 100644 (file)
@@ -95,6 +95,17 @@ struct amdxdna_hwctx {
 #define drm_job_to_xdna_job(j) \
        container_of(j, struct amdxdna_sched_job, base)
 
+enum amdxdna_job_opcode {
+       SYNC_DEBUG_BO,
+       ATTACH_DEBUG_BO,
+       DETACH_DEBUG_BO,
+};
+
+struct amdxdna_drv_cmd {
+       enum amdxdna_job_opcode opcode;
+       u32                     result;
+};
+
 struct amdxdna_sched_job {
        struct drm_sched_job    base;
        struct kref             refcnt;
@@ -106,6 +117,7 @@ struct amdxdna_sched_job {
        struct dma_fence        *out_fence;
        bool                    job_done;
        u64                     seq;
+       struct amdxdna_drv_cmd  *drv_cmd;
        struct amdxdna_gem_obj  *cmd_bo;
        size_t                  bo_cnt;
        struct drm_gem_object   *bos[] __counted_by(bo_cnt);
@@ -143,9 +155,11 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job);
 void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
 int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
                       int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
 
 int amdxdna_cmd_submit(struct amdxdna_client *client,
-                      u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt,
+                      struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
+                      u32 *arg_bo_hdls, u32 arg_bo_cnt,
                       u32 hwctx_hdl, u64 *seq);
 
 int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
index 7f91863c3f24ce5cfd15a1d475d7b7e1b3f3b2ec..61e0136c21a8906116589063c71072705621d232 100644 (file)
@@ -962,6 +962,9 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
        XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
                 args->handle, args->offset, args->size);
 
+       if (args->direction == SYNC_DIRECT_FROM_DEVICE)
+               ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle);
+
 put_obj:
        drm_gem_object_put(gobj);
        return ret;
index ae29db94a9d33483f6fe708000e43f3c639dbc09..f79fc7f3c93bc484cdf53fc055a151a82cb33061 100644 (file)
@@ -7,6 +7,7 @@
 #define _AMDXDNA_GEM_H_
 
 #include <linux/hmm.h>
+#include "amdxdna_pci_drv.h"
 
 struct amdxdna_umap {
        struct vm_area_struct           *vma;
@@ -62,6 +63,11 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
        drm_gem_object_put(to_gobj(abo));
 }
 
+static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
+{
+       return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr;
+}
+
 void amdxdna_umap_put(struct amdxdna_umap *mapp);
 
 struct drm_gem_object *
index 696fdac8ad3c00af2ba26989a71ee96bdd0c920c..3599e713bfcb5f1685ef9f53471de4ac10260127 100644 (file)
@@ -28,9 +28,10 @@ MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
  * 0.0: Initial version
  * 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY
  * 0.2: Support getting last error hardware error
+ * 0.3: Support firmware debug buffer
  */
 #define AMDXDNA_DRIVER_MAJOR           0
-#define AMDXDNA_DRIVER_MINOR           2
+#define AMDXDNA_DRIVER_MINOR           3
 
 /*
  * Bind the driver base on (vendor_id, device_id) pair and later use the
index 626beebf730e3f88658468c68ff2b71cc3ebd2ca..c99477f5e454f01328c2ed15245344500146df28 100644 (file)
@@ -55,6 +55,7 @@ struct amdxdna_dev_ops {
        int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
        void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
        int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+       int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
        void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
        int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
        int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
index e4f6dac7d00fdb0812b5b1a2b0d37876bd06b50d..10124cccb1024ab14939430c5c8d74005a85b8cb 100644 (file)
@@ -46,6 +46,7 @@
 
 const struct rt_config npu1_default_rt_cfg[] = {
        { 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+       { 4, 1, AIE2_RT_CFG_INIT }, /* Debug BO */
        { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
        { 0 },
 };
index 9f2e33182ec6fe7cd649bb04c75eb7c0bd403d32..e1da882420eca4a68cad03f95fff880d08024f45 100644 (file)
@@ -63,6 +63,7 @@
 
 const struct rt_config npu4_default_rt_cfg[] = {
        { 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+       { 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
        { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
        { 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
        { 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */