[PATCH v2 4/7] platform/x86/amd/hsmp: Gate the data plane on a fully initialized socket
From: Muralidhara M K
Date: Thu Jun 25 2026 - 08:35:29 EST
hsmp_parse_acpi_table() published sock->dev before hsmp_read_acpi_crs()
had mapped virt_base_addr. sock->dev is the readiness gate for the
lock-free data plane, so on a multi-socket system - where socket 0
exposes /dev/hsmp before later sockets finish probing - an ioctl aimed
at a socket still in bring-up could pass the gate and dereference a NULL
virt_base_addr.
Pass the struct device explicitly to hsmp_read_acpi_crs() and
hsmp_read_acpi_dsd() instead of reading it back from sock->dev, publish
sock->dev last with smp_store_release() once virt_base_addr, the mailbox
offsets and the semaphore are initialized, and read it with
smp_load_acquire() in hsmp_send_message() so a non-NULL dev guarantees
the rest of the socket state is visible.
Signed-off-by: Muralidhara M K <muralidhara.mk@xxxxxxx>
---
drivers/platform/x86/amd/hsmp/acpi.c | 38 +++++++++++++++++++---------
drivers/platform/x86/amd/hsmp/hsmp.c | 13 ++++++----
2 files changed, 34 insertions(+), 17 deletions(-)
diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c
index 24296747df47..bf5601229c6c 100644
--- a/drivers/platform/x86/amd/hsmp/acpi.c
+++ b/drivers/platform/x86/amd/hsmp/acpi.c
@@ -112,7 +112,7 @@ static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
return AE_OK;
}
-static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
+static int hsmp_read_acpi_dsd(struct hsmp_socket *sock, struct device *dev)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *guid, *mailbox_package;
@@ -121,10 +121,10 @@ static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
int ret = 0;
int j;
- status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
+ status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL,
&buf, ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(status)) {
- dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
+ dev_err(dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
acpi_format_exception(status));
return -ENODEV;
}
@@ -147,7 +147,7 @@ static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
guid = &dsd->package.elements[0];
mailbox_package = &dsd->package.elements[1];
if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
- dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
+ dev_err(dev, "Invalid hsmp _DSD table data\n");
ret = -EINVAL;
goto free_buf;
}
@@ -197,14 +197,14 @@ static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
return ret;
}
-static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
+static int hsmp_read_acpi_crs(struct hsmp_socket *sock, struct device *dev)
{
acpi_status status;
- status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
+ status = acpi_walk_resources(ACPI_HANDLE(dev), METHOD_NAME__CRS,
hsmp_resource, sock);
if (ACPI_FAILURE(status)) {
- dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
+ dev_err(dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
acpi_format_exception(status));
return -EINVAL;
}
@@ -212,10 +212,10 @@ static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
return -EINVAL;
/* The mapped region should be un-cached */
- sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
+ sock->virt_base_addr = devm_ioremap_uc(dev, sock->mbinfo.base_addr,
sock->mbinfo.size);
if (!sock->virt_base_addr) {
- dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
+ dev_err(dev, "Failed to ioremap MP1 base address\n");
return -ENOMEM;
}
@@ -229,7 +229,6 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
int ret;
sock->sock_ind = sock_ind;
- sock->dev = dev;
sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr;
sema_init(&sock->hsmp_sem, 1);
@@ -237,12 +236,27 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
dev_set_drvdata(dev, sock);
/* Read MP1 base address from CRS method */
- ret = hsmp_read_acpi_crs(sock);
+ ret = hsmp_read_acpi_crs(sock, dev);
if (ret)
return ret;
/* Read mailbox offsets from DSD table */
- return hsmp_read_acpi_dsd(sock);
+ ret = hsmp_read_acpi_dsd(sock, dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Publish sock->dev last. hsmp_send_message() uses it (via
+ * smp_load_acquire()) as the readiness gate for the lock-free data
+ * plane, so it must become visible only after virt_base_addr, the
+ * mailbox offsets and the semaphore are fully initialized. On a
+ * multi-socket system socket 0 exposes /dev/hsmp before later sockets
+ * finish probing, so without this an ioctl aimed at a socket still in
+ * bring-up could pass the gate and dereference a NULL virt_base_addr.
+ */
+ smp_store_release(&sock->dev, dev);
+
+ return 0;
}
static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c
index a96c59fcba0a..c3939908d95f 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.c
+++ b/drivers/platform/x86/amd/hsmp/hsmp.c
@@ -228,12 +228,15 @@ int hsmp_send_message(struct hsmp_message *msg)
sock = &hsmp_pdev.sock[sock_ind];
/*
- * A slot exists for every possible socket, but its state (including
- * hsmp_sem) is only initialized once that socket has actually been
- * probed. Reject messages aimed at a socket that was never brought
- * up so we never operate on a zero-initialized semaphore.
+ * A slot exists for every possible socket, but it is only usable
+ * between a successful probe and the matching remove. Reject messages
+ * aimed at a socket that was never brought up, is still in bring-up, or
+ * has already been removed, so we never operate on a zero-initialized
+ * semaphore or an unmapped mailbox. A non-NULL dev also guarantees
+ * virt_base_addr, the mailbox offsets and the semaphore are visible.
*/
- if (!sock->dev)
+ /* Pairs with smp_store_release(&sock->dev) in hsmp_parse_acpi_table(). */
+ if (!smp_load_acquire(&sock->dev))
return -ENODEV;
ret = down_interruptible(&sock->hsmp_sem);
--
2.43.0