* [PATCH 1/5] platform/x86: dell: dcdbas: Actually wait for the host control SMI
2026-11-14 6:17 [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling Rosen Penev
@ 2026-11-14 6:17 ` Rosen Penev
2026-11-14 6:17 ` [PATCH 2/5] platform/x86: dell: dcdbas: Suppress bind attributes of the platform driver Rosen Penev
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Rosen Penev @ 2026-11-14 6:17 UTC (permalink / raw)
To: Stuart Hayes, Hans de Goede, Ilpo Järvinen, Doug Warzecha,
Andrew Morton, Andy Shevchenko
Cc: platform-driver-x86, linux-kernel
For SMI types 2 and 3, host_control_smi() writes the command status as
-1, raises the SMI and then waits for the BIOS to change the status:
while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) {
apm_cmd->status is a plain memory read, and neither outb() nor inb()
tells the compiler that memory may have changed, so the compiler
assumes status is still -1. The loop then has no side effects and its
return value is not used, so it is removed entirely: in the generated
code, the read of the control port is followed directly by the return.
The kernel goes on to power off right after raising the host control
SMI, without waiting for the BIOS to accept it.
Poll the status with read_poll_timeout_atomic() and READ_ONCE(), 1us
apart, for up to TIMEOUT_USEC_SHORT_SEMA_BLOCKING microseconds as the
name suggests. The type 1 loop polls an I/O port, which already takes
about a microsecond per read, and is left alone.
Fixes: 90563ec4129f ("[PATCH] dcdbas: add Dell Systems Management Base Driver with sysfs support")
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/platform/x86/dell/dcdbas.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index 678f44252a45..a5917093d539 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -21,6 +21,7 @@
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mc146818rtc.h>
#include <linux/module.h>
@@ -441,12 +442,11 @@ static int host_control_smi(void)
cmd_status = inb(PE1400_APM_CONTROL_PORT);
/* wait a few to see if it executed */
- num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
- while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) {
- num_ticks--;
- if (num_ticks == EXPIRED_TIMER)
- return -ETIME;
- }
+ if (read_poll_timeout_atomic(READ_ONCE, cmd_status,
+ cmd_status != ESM_STATUS_CMD_UNSUCCESSFUL,
+ 1, TIMEOUT_USEC_SHORT_SEMA_BLOCKING,
+ false, apm_cmd->status))
+ return -ETIME;
break;
default:
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/5] platform/x86: dell: dcdbas: Suppress bind attributes of the platform driver
2026-11-14 6:17 [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling Rosen Penev
2026-11-14 6:17 ` [PATCH 1/5] platform/x86: dell: dcdbas: Actually wait for the host control SMI Rosen Penev
@ 2026-11-14 6:17 ` Rosen Penev
2026-11-14 6:17 ` [PATCH 3/5] platform/x86: dell: dcdbas: Release the SMI buffer in remove Rosen Penev
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Rosen Penev @ 2026-11-14 6:17 UTC (permalink / raw)
To: Stuart Hayes, Hans de Goede, Ilpo Järvinen, Doug Warzecha,
Andrew Morton, Andy Shevchenko
Cc: platform-driver-x86, linux-kernel
The dcdbas platform device is created by the module itself and stays
in use for as long as the module is loaded: dell-smbios-smm allocates
its SMI buffers against it through dcdbas_smi_alloc() whether or not
the driver is bound.
Binding the driver again after an unbind also leaks state:
dcdbas_check_wsmt() runs a second time and memremap()s the firmware
SMI buffer again, overwriting bios_buffer and leaking the previous
mapping.
Unbinding or rebinding the device by hand is not useful, so hide the
bind and unbind files.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/platform/x86/dell/dcdbas.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index a5917093d539..601ee8ca92e4 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -708,6 +708,7 @@ static void dcdbas_remove(struct platform_device *dev)
static struct platform_driver dcdbas_driver = {
.driver = {
.name = DRIVER_NAME,
+ .suppress_bind_attrs = true,
},
.probe = dcdbas_probe,
.remove = dcdbas_remove,
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/5] platform/x86: dell: dcdbas: Release the SMI buffer in remove
2026-11-14 6:17 [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling Rosen Penev
2026-11-14 6:17 ` [PATCH 1/5] platform/x86: dell: dcdbas: Actually wait for the host control SMI Rosen Penev
2026-11-14 6:17 ` [PATCH 2/5] platform/x86: dell: dcdbas: Suppress bind attributes of the platform driver Rosen Penev
@ 2026-11-14 6:17 ` Rosen Penev
2026-11-14 6:17 ` [PATCH 4/5] platform/x86: dell: dcdbas: Validate the WSMT buffer description Rosen Penev
2026-11-14 6:17 ` [PATCH 5/5] platform/x86: dell: dcdbas: Use read_poll_timeout_atomic() for the type 1 SMI Rosen Penev
4 siblings, 0 replies; 6+ messages in thread
From: Rosen Penev @ 2026-11-14 6:17 UTC (permalink / raw)
To: Stuart Hayes, Hans de Goede, Ilpo Järvinen, Doug Warzecha,
Andrew Morton, Andy Shevchenko
Cc: platform-driver-x86, linux-kernel
dcdbas_exit() frees the SMI data buffer before
platform_device_unregister() removes the sysfs attributes, and without
holding smi_data_lock. The comment claims that only at module exit can
all sysfs attributes be released, but device attributes do not pin the
module: while rmmod runs, smi_data_write() or smi_data_buf_size_store()
can still be executing. They can then memcpy() into the buffer as it is
freed, or allocate a new buffer that is never freed.
sysfs_remove_group() in dcdbas_remove() waits for running attribute
handlers, and dcdbas_pdev is still valid at that point. Free the buffer
there, or unmap the firmware-provided buffer when WSMT is in use, and
also do it when probe fails after dcdbas_check_wsmt() mapped the
firmware buffer. Reset the WSMT state with it so a later probe starts
clean.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/platform/x86/dell/dcdbas.c | 46 ++++++++++++++++++------------
1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index 601ee8ca92e4..d5a93aca9c93 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -98,6 +98,22 @@ static void smi_data_buf_free(void)
dcdbas_smi_free(&smi_buf);
}
+/**
+ * dcdbas_release_buffer: free the SMI data buffer or unmap the WSMT one
+ */
+static void dcdbas_release_buffer(void)
+{
+ if (wsmt_enabled) {
+ memunmap(bios_buffer);
+ bios_buffer = NULL;
+ smi_buf = (struct smi_buffer){};
+ max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
+ wsmt_enabled = false;
+ } else {
+ smi_data_buf_free();
+ }
+}
+
/**
* smi_data_buf_realloc: grow SMI data buffer if needed
*/
@@ -685,11 +701,11 @@ static int dcdbas_probe(struct platform_device *dev)
*/
error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32));
if (error)
- return error;
+ goto err_release_buffer;
error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
if (error)
- return error;
+ goto err_release_buffer;
register_reboot_notifier(&dcdbas_reboot_nb);
@@ -697,12 +713,22 @@ static int dcdbas_probe(struct platform_device *dev)
DRIVER_DESCRIPTION, DRIVER_VERSION);
return 0;
+
+err_release_buffer:
+ dcdbas_release_buffer();
+ return error;
}
static void dcdbas_remove(struct platform_device *dev)
{
unregister_reboot_notifier(&dcdbas_reboot_nb);
sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
+
+ /*
+ * sysfs_remove_group() waited for any attribute handler still
+ * running, so nothing else uses the buffer now.
+ */
+ dcdbas_release_buffer();
}
static struct platform_driver dcdbas_driver = {
@@ -751,22 +777,6 @@ static int __init dcdbas_init(void)
*/
static void __exit dcdbas_exit(void)
{
- /*
- * make sure functions that use dcdbas_pdev are called
- * before platform_device_unregister
- */
- unregister_reboot_notifier(&dcdbas_reboot_nb);
-
- /*
- * We have to free the buffer here instead of dcdbas_remove
- * because only in module exit function we can be sure that
- * all sysfs attributes belonging to this module have been
- * released.
- */
- if (dcdbas_pdev)
- smi_data_buf_free();
- if (bios_buffer)
- memunmap(bios_buffer);
platform_device_unregister(dcdbas_pdev_reg);
platform_driver_unregister(&dcdbas_driver);
}
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/5] platform/x86: dell: dcdbas: Validate the WSMT buffer description
2026-11-14 6:17 [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling Rosen Penev
` (2 preceding siblings ...)
2026-11-14 6:17 ` [PATCH 3/5] platform/x86: dell: dcdbas: Release the SMI buffer in remove Rosen Penev
@ 2026-11-14 6:17 ` Rosen Penev
2026-11-14 6:17 ` [PATCH 5/5] platform/x86: dell: dcdbas: Use read_poll_timeout_atomic() for the type 1 SMI Rosen Penev
4 siblings, 0 replies; 6+ messages in thread
From: Rosen Penev @ 2026-11-14 6:17 UTC (permalink / raw)
To: Stuart Hayes, Hans de Goede, Ilpo Järvinen, Doug Warzecha,
Andrew Morton, Andy Shevchenko
Cc: platform-driver-x86, linux-kernel
dcdbas_check_wsmt() takes the address and size of the firmware SMI
buffer from an SMBIOS OEM string or from an EPS table in the BIOS area,
and sets
smi_buf.size = remap_size - 8;
max_smi_data_buf_size = smi_buf.size;
without checking that remap_size is larger than the 8 byte semaphore.
An OEM string such as "30[addr;0]" or an EPS table with
num_of_4k_pages == 0 makes the size wrap to almost 2^64, and smi_data
reads and writes can then access memory well past the mapping.
check_eps_table() also accepts a table with length 0, since the
checksum of zero bytes is zero, and then trusts the rest of it.
Reject buffers of 8 bytes or less, and EPS tables shorter than
struct smm_eps_table.
Fixes: 12c956c4f32e ("firmware: dcdbas: Add support for WSMT ACPI table")
Fixes: 47a94c551a74 ("platform/x86: dcdbas: Check SMBIOS for protected buffer address")
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/platform/x86/dell/dcdbas.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index d5a93aca9c93..af4c1d2fe497 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -540,6 +540,9 @@ static inline struct smm_eps_table *check_eps_table(u8 *addr)
if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0)
return NULL;
+ if (eps->length < sizeof(*eps))
+ return NULL;
+
if (checksum(addr, eps->length) != 0)
return NULL;
@@ -600,6 +603,12 @@ static int dcdbas_check_wsmt(void)
dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
return -EINVAL;
}
+ /* The first 8 bytes are a semaphore, so there must be room after them */
+ if (remap_size <= 8) {
+ dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer size %llu is too small\n",
+ remap_size);
+ return -EINVAL;
+ }
/*
* Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8
* bytes are used for a semaphore, not the data buffer itself).
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 5/5] platform/x86: dell: dcdbas: Use read_poll_timeout_atomic() for the type 1 SMI
2026-11-14 6:17 [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling Rosen Penev
` (3 preceding siblings ...)
2026-11-14 6:17 ` [PATCH 4/5] platform/x86: dell: dcdbas: Validate the WSMT buffer description Rosen Penev
@ 2026-11-14 6:17 ` Rosen Penev
4 siblings, 0 replies; 6+ messages in thread
From: Rosen Penev @ 2026-11-14 6:17 UTC (permalink / raw)
To: Stuart Hayes, Hans de Goede, Ilpo Järvinen, Doug Warzecha,
Andrew Morton, Andy Shevchenko
Cc: platform-driver-x86, linux-kernel
For SMI type 1, host_control_smi() polls the APM status port in a loop
that counts down TIMEOUT_USEC_SHORT_SEMA_BLOCKING iterations and relies
on each inb() taking about a microsecond. Use
read_poll_timeout_atomic() with a 1us delay instead, like the type 2
and 3 path, so the timeout is in microseconds and does not depend on
how long a port read takes. With the delay plus the port read, the
worst-case wait grows from about 10ms to about 20ms, which only matters
when the BIOS never answers.
This removes the last users of num_ticks and EXPIRED_TIMER.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/platform/x86/dell/dcdbas.c | 12 +++++-------
drivers/platform/x86/dell/dcdbas.h | 1 -
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index af4c1d2fe497..edc210852f2d 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -394,7 +394,6 @@ static int host_control_smi(void)
struct apm_cmd *apm_cmd;
u8 *data;
unsigned long flags;
- u32 num_ticks;
s8 cmd_status;
u8 index;
@@ -424,12 +423,11 @@ static int host_control_smi(void)
spin_unlock_irqrestore(&rtc_lock, flags);
/* wait a few to see if it executed */
- num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
- while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) {
- num_ticks--;
- if (num_ticks == EXPIRED_TIMER)
- return -ETIME;
- }
+ if (read_poll_timeout_atomic(inb, cmd_status,
+ cmd_status != ESM_STATUS_CMD_UNSUCCESSFUL,
+ 1, TIMEOUT_USEC_SHORT_SEMA_BLOCKING,
+ false, PCAT_APM_STATUS_PORT))
+ return -ETIME;
break;
case HC_SMITYPE_TYPE2:
diff --git a/drivers/platform/x86/dell/dcdbas.h b/drivers/platform/x86/dell/dcdbas.h
index a05d7f667586..d5ad2363dd9e 100644
--- a/drivers/platform/x86/dell/dcdbas.h
+++ b/drivers/platform/x86/dell/dcdbas.h
@@ -42,7 +42,6 @@
#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16)
#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000)
-#define EXPIRED_TIMER (0)
#define SMI_CMD_MAGIC (0x534D4931)
#define SMM_EPS_SIG "$SCB"
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread