From: Stuart Summers <stuart.summers@intel.com>
Cc: intel-xe@lists.freedesktop.org, fei.yang@intel.com,
rodrigo.vivi@intel.com, Stuart Summers <stuart.summers@intel.com>
Subject: [PATCH 1/1] drm/xe/configfs: Allow overriding the GuC hwconfig table
Date: Wed, 11 Nov 2026 00:08:18 +0000 [thread overview]
Message-ID: <20261111000818.238585-4-stuart.summers@intel.com> (raw)
In-Reply-To: <20261111000818.238585-3-stuart.summers@intel.com>
Add a binary configfs attribute holding a raw hwconfig blob. When set
before bind, it is loaded into the hwconfig BO instead of querying GuC,
so all consumers - including the DRM_XE_DEVICE_QUERY_HWCONFIG IOCTL -
see the override. This also permits supplying a table on platforms where
GuC exposes none, which is useful for testing userspace against
hwconfig data the hardware does not report.
Signed-off-by: Stuart Summers <stuart.summers@intel.com>
---
drivers/gpu/drm/xe/xe_configfs.c | 119 ++++++++++++++++++++++++++-
drivers/gpu/drm/xe/xe_configfs.h | 6 ++
drivers/gpu/drm/xe/xe_guc_hwconfig.c | 36 +++++---
3 files changed, 150 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index 2ddd707a6dbd..6f810b33e12d 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -63,7 +63,8 @@
* ├── engines_allowed
* ├── migrate_ulls_period_ms
* ├── enable_psmi
- * └── disable_vram_page_offline
+ * ├── disable_vram_page_offline
+ * └── hwconfig
*
* After configuring the attributes as per next section, the device can be
* probed with::
@@ -277,6 +278,22 @@
*
* This attribute can only be set before binding to the device.
*
+ * Hwconfig:
+ * ---------
+ *
+ * Override the hardware configuration table normally obtained from the GuC
+ * with a user-provided binary blob. When set, the driver uses this table in
+ * place of the GuC one everywhere it is consumed, including the table
+ * reported to userspace by the DRM_XE_DEVICE_QUERY_HWCONFIG query. This also
+ * allows a table to be provided on platforms where the GuC exposes none.
+ *
+ * The blob is the raw hwconfig table, so its size must be a multiple of 4
+ * bytes. Writing a new blob replaces the previous one::
+ *
+ * # cat hwconfig.bin > /sys/kernel/config/xe/0000:03:00.0/hwconfig
+ *
+ * This attribute can only be set before binding to the device.
+ *
* Remove devices
* ==============
*
@@ -291,6 +308,12 @@ struct wa_bb {
u32 len; /* in dwords */
};
+/* User-provided replacement for the GuC-provided hwconfig table */
+struct hwconfig_blob {
+ void *data;
+ size_t len; /* in bytes */
+};
+
struct xe_config_group_device {
struct config_group group;
struct config_group sriov;
@@ -299,6 +322,7 @@ struct xe_config_group_device {
u64 gt_types_allowed;
u64 engines_allowed;
u32 migrate_ulls_period_ms;
+ struct hwconfig_blob hwconfig;
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
struct wa_bb ctx_restore_mid_bb[XE_ENGINE_CLASS_MAX];
bool survivability_mode;
@@ -937,6 +961,50 @@ static ssize_t ctx_restore_post_bb_store(struct config_item *item,
return wa_bb_store(dev->config.ctx_restore_post_bb, dev, data, sz);
}
+static ssize_t hwconfig_read(struct config_item *item, void *buf, size_t count)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item);
+
+ guard(mutex)(&dev->lock);
+
+ if (!buf)
+ return dev->config.hwconfig.len;
+
+ count = min(count, dev->config.hwconfig.len);
+ memcpy(buf, dev->config.hwconfig.data, count);
+
+ return count;
+}
+
+static ssize_t hwconfig_write(struct config_item *item, const void *buf, size_t count)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item);
+ void *data = NULL;
+
+ /* The table is a sequence of dwords */
+ if (count % sizeof(u32))
+ return -EINVAL;
+
+ if (count) {
+ data = kmemdup(buf, count, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ }
+
+ guard(mutex)(&dev->lock);
+
+ if (is_bound(dev)) {
+ kfree(data);
+ return -EBUSY;
+ }
+
+ kfree(dev->config.hwconfig.data);
+ dev->config.hwconfig.data = data;
+ dev->config.hwconfig.len = count;
+
+ return count;
+}
+
CONFIGFS_ATTR(, ctx_restore_mid_bb);
CONFIGFS_ATTR(, ctx_restore_post_bb);
CONFIGFS_ATTR(, enable_multi_queue);
@@ -946,6 +1014,7 @@ CONFIGFS_ATTR(, disable_vram_page_offline);
CONFIGFS_ATTR(, engines_allowed);
CONFIGFS_ATTR(, gt_types_allowed);
CONFIGFS_ATTR(, survivability_mode);
+CONFIGFS_BIN_ATTR(, hwconfig, NULL, SZ_64K);
static struct configfs_attribute *xe_config_device_attrs[] = {
&attr_ctx_restore_mid_bb,
@@ -960,12 +1029,18 @@ static struct configfs_attribute *xe_config_device_attrs[] = {
NULL,
};
+static const struct configfs_bin_attribute *const xe_config_device_bin_attrs[] = {
+ &attr_hwconfig,
+ NULL,
+};
+
static void xe_config_device_release(struct config_item *item)
{
struct xe_config_group_device *dev = to_xe_config_group_device(item);
mutex_destroy(&dev->lock);
+ kfree(dev->config.hwconfig.data);
kfree(dev->config.ctx_restore_mid_bb[0].cs);
kfree(dev->config.ctx_restore_post_bb[0].cs);
kfree(dev);
@@ -1001,6 +1076,7 @@ static const struct config_item_type xe_config_device_type = {
.ct_item_ops = &xe_config_device_ops,
.ct_group_ops = &xe_config_device_group_ops,
.ct_attrs = xe_config_device_attrs,
+ .ct_bin_attrs = xe_config_device_bin_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1243,6 +1319,10 @@ static void dump_custom_dev_config(struct pci_dev *pdev,
PRI_CUSTOM_ATTR("%u", sriov.admin_only_pf);
#undef PRI_CUSTOM_ATTR
+
+ if (dev->config.hwconfig.len)
+ pci_info(pdev, "configfs: hwconfig = %zu bytes\n",
+ dev->config.hwconfig.len);
}
/**
@@ -1479,6 +1559,43 @@ u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
return len;
}
+/**
+ * xe_configfs_get_hwconfig - get a copy of the configfs hwconfig blob
+ * @pdev: pci device
+ * @blob: pointer to store the newly allocated copy of the blob
+ *
+ * If a hwconfig table override was written to configfs for @pdev, allocate a
+ * copy of it and store the pointer in @blob. The caller takes ownership of it
+ * and is responsible for freeing it with kfree().
+ *
+ * Return: size in bytes of the blob stored in @blob, or 0 if no override is
+ * set or it could not be copied.
+ */
+size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ size_t len = 0;
+
+ *blob = NULL;
+
+ if (!dev)
+ return 0;
+
+ scoped_guard(mutex, &dev->lock) {
+ if (!dev->config.hwconfig.len)
+ break;
+
+ *blob = kmemdup(dev->config.hwconfig.data,
+ dev->config.hwconfig.len, GFP_KERNEL);
+ if (*blob)
+ len = dev->config.hwconfig.len;
+ }
+
+ config_group_put(&dev->group);
+
+ return len;
+}
+
#ifdef CONFIG_PCI_IOV
/**
* xe_configfs_admin_only_pf() - Get PF's operational mode.
diff --git a/drivers/gpu/drm/xe/xe_configfs.h b/drivers/gpu/drm/xe/xe_configfs.h
index 327b648944ff..526250e9fdfe 100644
--- a/drivers/gpu/drm/xe/xe_configfs.h
+++ b/drivers/gpu/drm/xe/xe_configfs.h
@@ -32,6 +32,7 @@ u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
enum xe_engine_class class,
const u32 **cs);
+size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob);
#ifdef CONFIG_PCI_IOV
unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev);
bool xe_configfs_admin_only_pf(struct pci_dev *pdev);
@@ -54,6 +55,11 @@ static inline u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
static inline u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
enum xe_engine_class class,
const u32 **cs) { return 0; }
+static inline size_t xe_configfs_get_hwconfig(struct pci_dev *pdev, void **blob)
+{
+ *blob = NULL;
+ return 0;
+}
#ifdef CONFIG_PCI_IOV
static inline unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev)
{
diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
index dc667a71b438..33abfdfcff41 100644
--- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c
+++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
@@ -10,7 +10,9 @@
#include "abi/guc_actions_abi.h"
#include "xe_bo.h"
+#include "xe_configfs.h"
#include "xe_device_types.h"
+#include "xe_gt_printk.h"
#include "xe_gt_types.h"
#include "xe_guc.h"
#include "xe_map.h"
@@ -55,6 +57,7 @@ int xe_guc_hwconfig_init(struct xe_guc *guc)
struct xe_gt *gt = guc_to_gt(guc);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
+ void *blob;
u32 size;
int err;
@@ -68,25 +71,38 @@ int xe_guc_hwconfig_init(struct xe_guc *guc)
if (gt != xe_root_mmio_gt(xe))
return 0;
- /* ADL_P, DG2+ supports hwconfig table */
- if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P)
- return 0;
-
- err = guc_hwconfig_size(guc, &size);
- if (err)
- return err;
- if (!size)
- return -EINVAL;
+ size = xe_configfs_get_hwconfig(to_pci_dev(xe->drm.dev), &blob);
+ if (!size) {
+ /* ADL_P, DG2+ supports hwconfig table */
+ if (GRAPHICS_VERx100(xe) < 1255 && xe->info.platform != XE_ALDERLAKE_P)
+ return 0;
+
+ err = guc_hwconfig_size(guc, &size);
+ if (err)
+ return err;
+ if (!size)
+ return -EINVAL;
+ }
bo = xe_managed_bo_create_pin_map(xe, tile, PAGE_ALIGN(size),
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE);
- if (IS_ERR(bo))
+ if (IS_ERR(bo)) {
+ kfree(blob);
return PTR_ERR(bo);
+ }
guc->hwconfig.bo = bo;
guc->hwconfig.size = size;
+ if (blob) {
+ xe_map_memcpy_to(xe, &bo->vmap, 0, blob, size);
+ kfree(blob);
+ xe_gt_info(gt, "Using hwconfig table override from configfs (%u bytes)\n",
+ size);
+ return 0;
+ }
+
return guc_hwconfig_copy(guc);
}
--
2.43.0
next prev parent reply other threads:[~2026-11-11 0:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 0:08 [PATCH 0/1] Add a configfs to program the hwconfig Stuart Summers
2026-11-11 0:08 ` Stuart Summers [this message]
2026-11-11 0:25 ` [PATCH 1/1] drm/xe/configfs: Allow overriding the GuC hwconfig table sashiko-bot
2026-11-11 19:37 ` Rodrigo Vivi
2026-11-11 1:35 ` ✓ CI.KUnit: success for Add a configfs to program the hwconfig Patchwork
2026-11-11 2:09 ` ✓ Xe.CI.BAT: " Patchwork
2026-11-11 7:06 ` ✓ Xe.CI.FULL: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111000818.238585-4-stuart.summers@intel.com \
--to=stuart.summers@intel.com \
--cc=fei.yang@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=rodrigo.vivi@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox