mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] platform/x86: dell: dcdbas: Fix SMI completion wait and buffer handling
@ 2026-11-14  6:17 Rosen Penev
  2026-11-14  6:17 ` [PATCH 1/5] platform/x86: dell: dcdbas: Actually wait for the host control SMI Rosen Penev
                   ` (4 more replies)
  0 siblings, 5 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

Fixes for dcdbas.

Patch 1 fixes the host control SMI wait for types 2 and 3, which the
compiler removed entirely because the status was a plain memory read.
Patches 2 and 3 fix the SMI buffer lifetime on unbind and module
removal. Patch 4 validates the firmware-provided WSMT buffer
description. Patch 5 converts the type 1 wait to
read_poll_timeout_atomic() as well.

These patches were developed with the help of an AI coding assistant
(Claude Opus 5.5): it reviewed the drivers, found the issues and drafted
the fixes and changelogs. I reviewed every change and I am responsible
for them.

All patches build cleanly with W=1.

Tested on a Dell Precision M4800 running v7.2.15, using the equivalent
backports built as out-of-tree modules:
the driver probes, and growing smi_data_buf_size and then unloading and
reloading dcdbas with dell-smbios on top is clean, with the buffer freed
on remove. The host control SMI (only used at power-off) and the WSMT
paths were not exercised.


Rosen Penev (5):
  platform/x86: dell: dcdbas: Actually wait for the host control SMI
  platform/x86: dell: dcdbas: Suppress bind attributes of the platform
    driver
  platform/x86: dell: dcdbas: Release the SMI buffer in remove
  platform/x86: dell: dcdbas: Validate the WSMT buffer description
  platform/x86: dell: dcdbas: Use read_poll_timeout_atomic() for the
    type 1 SMI

 drivers/platform/x86/dell/dcdbas.c | 80 ++++++++++++++++++------------
 drivers/platform/x86/dell/dcdbas.h |  1 -
 2 files changed, 49 insertions(+), 32 deletions(-)


base-commit: 2800868f63227c8237d70c41e70d441c919620d6
-- 
2.56.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [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

end of thread, other threads:[~2026-11-14  6:18 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH 3/5] platform/x86: dell: dcdbas: Release the SMI buffer in remove 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®