[PATCH v2 08/14] platform/x86: dell-smbios: Introduce a WMI-ACPI interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The driver currently uses an SMI interface which grants direct access
to physical memory to the platform via a pointer.

Now add a WMI-ACPI interface that is detected by WMI probe and preferred
over the SMI interface.

Changing this to operate over WMI-ACPI will use an ACPI OperationRegion
for a buffer of data storage when platform calls are performed.

This is a safer approach to use in kernel drivers as the platform will
only have access to that OperationRegion.

As a result, this change removes the dependency on this driver on the
dcdbas kernel module.  It's now an optional compilation option.

Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxxx>
---
 drivers/platform/x86/Kconfig       |   8 +-
 drivers/platform/x86/dell-smbios.c | 150 ++++++++++++++++++++++++++++++-------
 drivers/platform/x86/dell-smbios.h |  15 +++-
 3 files changed, 140 insertions(+), 33 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 9e52f05daa2e..81d61c0f4ef8 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -92,13 +92,13 @@ config ASUS_LAPTOP
 	  If you have an ACPI-compatible ASUS laptop, say Y or M here.
 
 config DELL_SMBIOS
-	tristate
-	select DCDBAS
+	tristate "Dell WMI SMBIOS calling interface"
+	depends on ACPI_WMI
 	---help---
 	This module provides common functions for kernel modules using
-	Dell SMBIOS.
+	Dell SMBIOS over ACPI-WMI.
 
-	If you have a Dell laptop, say Y or M here.
+	If you have a Dell computer, say Y or M here.
 
 config DELL_LAPTOP
 	tristate "Dell Laptop Extras"
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
index 579be67de2a3..1fbc40791a48 100644
--- a/drivers/platform/x86/dell-smbios.c
+++ b/drivers/platform/x86/dell-smbios.c
@@ -4,6 +4,7 @@
  *  Copyright (c) Red Hat <mjg@xxxxxxxxxx>
  *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@xxxxxxxxx>
  *  Copyright (c) 2014 Pali Rohár <pali.rohar@xxxxxxxxx>
+ *  Copyright (c) 2017 Dell Inc.
  *
  *  Based on documentation in the libsmbios package:
  *  Copyright (C) 2005-2014 Dell Inc.
@@ -19,9 +20,15 @@
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
-#include "../../firmware/dcdbas.h"
+#include <linux/wmi.h>
 #include "dell-smbios.h"
 
+#ifdef CONFIG_DCDBAS
+#include "../../firmware/dcdbas.h"
+#endif
+
+#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+
 struct calling_interface_structure {
 	struct dmi_header header;
 	u16 cmdIOAddress;
@@ -30,12 +37,14 @@ struct calling_interface_structure {
 	struct calling_interface_token tokens[];
 } __packed;
 
-static struct calling_interface_buffer *buffer;
+static struct calling_interface_buffer *smi_buffer;
+static struct wmi_calling_interface_buffer *wmi_buffer;
 static DEFINE_MUTEX(buffer_mutex);
 
 static int da_command_address;
 static int da_command_code;
 static int da_num_tokens;
+static int has_wmi;
 static struct calling_interface_token *da_tokens;
 
 int dell_smbios_error(int value)
@@ -57,13 +66,20 @@ struct calling_interface_buffer *dell_smbios_get_buffer(void)
 {
 	mutex_lock(&buffer_mutex);
 	dell_smbios_clear_buffer();
-	return buffer;
+	if (has_wmi)
+		return &wmi_buffer->smi;
+	return smi_buffer;
 }
 EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
 
 void dell_smbios_clear_buffer(void)
 {
-	memset(buffer, 0, sizeof(struct calling_interface_buffer));
+	if (has_wmi)
+		memset(wmi_buffer, 0,
+		       sizeof(struct wmi_calling_interface_buffer));
+	else
+		memset(smi_buffer, 0,
+		       sizeof(struct calling_interface_buffer));
 }
 EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
 
@@ -73,20 +89,60 @@ void dell_smbios_release_buffer(void)
 }
 EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
 
-void dell_smbios_send_request(int class, int select)
+int run_wmi_smbios_call(struct wmi_calling_interface_buffer *buf)
 {
-	struct smi_cmd command;
+	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+	struct acpi_buffer input;
+	union acpi_object *obj;
+	acpi_status status;
+
+	input.length = sizeof(struct wmi_calling_interface_buffer);
+	input.pointer = buf;
+
+	status = wmi_evaluate_method(DELL_WMI_SMBIOS_GUID,
+				     0, 1, &input, &output);
+	if (ACPI_FAILURE(status)) {
+		pr_err("%x/%x [%x,%x,%x,%x] call failed\n",
+			buf->smi.class, buf->smi.select,
+			buf->smi.input[0], buf->smi.input[1],
+			buf->smi.input[2], buf->smi.input[3]);
+			return -EIO;
+	}
+	obj = (union acpi_object *)output.pointer;
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		pr_err("invalid type : %d\n", obj->type);
+		return -EIO;
+	}
+	memcpy(buf, obj->buffer.pointer, input.length);
 
-	command.magic = SMI_CMD_MAGIC;
-	command.command_address = da_command_address;
-	command.command_code = da_command_code;
-	command.ebx = virt_to_phys(buffer);
-	command.ecx = 0x42534931;
+	return 0;
+}
 
-	buffer->class = class;
-	buffer->select = select;
+void dell_smbios_send_request(int class, int select)
+{
+	if (has_wmi) {
+		wmi_buffer->smi.class = class;
+		wmi_buffer->smi.select = select;
+		run_wmi_smbios_call(wmi_buffer);
+	}
 
-	dcdbas_smi_request(&command);
+#ifdef CONFIG_DCDBAS
+	else {
+		if (!smi_buffer)
+			return;
+		struct smi_cmd command;
+
+		smi_buffer->class = class;
+		smi_buffer->select = select;
+		command.magic = SMI_CMD_MAGIC;
+		command.command_address = da_command_address;
+		command.command_code = da_command_code;
+		command.ebx = virt_to_phys(smi_buffer);
+		command.ecx = 0x42534931;
+
+		dcdbas_smi_request(&command);
+	}
+#endif
 }
 EXPORT_SYMBOL_GPL(dell_smbios_send_request);
 
@@ -167,10 +223,45 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 	}
 }
 
-static int __init dell_smbios_init(void)
+static int dell_smbios_wmi_probe(struct wmi_device *wdev)
 {
-	int ret;
+	/* WMI buffer should be 32k */
+	wmi_buffer = (void *)__get_free_pages(GFP_KERNEL, 3);
+	if (!wmi_buffer)
+		return -ENOMEM;
+
+#ifdef CONFIG_DCDBAS
+	/* no longer need the SMI page */
+	free_page((unsigned long)smi_buffer);
+	smi_buffer = NULL;
+#endif
+
+	has_wmi = 1;
+	return 0;
+}
+
+static int dell_smbios_wmi_remove(struct wmi_device *wdev)
+{
+	free_pages((unsigned long)wmi_buffer, 3);
+	return 0;
+}
+
+static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
+	{ .guid_string = DELL_WMI_SMBIOS_GUID },
+	{ },
+};
 
+static struct wmi_driver dell_wmi_smbios_driver = {
+	.driver = {
+		.name = "dell-smbios",
+	},
+	.probe = dell_smbios_wmi_probe,
+	.remove = dell_smbios_wmi_remove,
+	.id_table = dell_smbios_wmi_id_table,
+};
+
+static int __init dell_smbios_init(void)
+{
 	dmi_walk(find_tokens, NULL);
 
 	if (!da_tokens)  {
@@ -178,34 +269,41 @@ static int __init dell_smbios_init(void)
 		return -ENODEV;
 	}
 
+#ifdef CONFIG_DCDBAS
 	/*
 	 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
 	 * is passed to SMI handler.
 	 */
-	buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
-	if (!buffer) {
-		ret = -ENOMEM;
-		goto fail_buffer;
+	smi_buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
+#else
+	smi_buffer = NULL;
+#endif
+	wmi_driver_register(&dell_wmi_smbios_driver);
+
+	if (!smi_buffer && !has_wmi) {
+		kfree(da_tokens);
+		return -ENOMEM;
 	}
-
 	return 0;
-
-fail_buffer:
-	kfree(da_tokens);
-	return ret;
 }
 
 static void __exit dell_smbios_exit(void)
 {
 	kfree(da_tokens);
-	free_page((unsigned long)buffer);
+#ifdef CONFIG_DCDBAS
+	if (!has_wmi)
+		free_page((unsigned long)smi_buffer);
+#endif
+	wmi_driver_unregister(&dell_wmi_smbios_driver);
 }
 
 subsys_initcall(dell_smbios_init);
 module_exit(dell_smbios_exit);
 
+
 MODULE_AUTHOR("Matthew Garrett <mjg@xxxxxxxxxx>");
 MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@xxxxxxxxx>");
 MODULE_AUTHOR("Pali Rohár <pali.rohar@xxxxxxxxx>");
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@xxxxxxxx>");
 MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
 MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
index 45cbc2292cd3..2f6fce81ee69 100644
--- a/drivers/platform/x86/dell-smbios.h
+++ b/drivers/platform/x86/dell-smbios.h
@@ -4,6 +4,7 @@
  *  Copyright (c) Red Hat <mjg@xxxxxxxxxx>
  *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@xxxxxxxxx>
  *  Copyright (c) 2014 Pali Rohár <pali.rohar@xxxxxxxxx>
+ *  Copyright (c) 2017 Dell Inc.
  *
  *  Based on documentation in the libsmbios package:
  *  Copyright (C) 2005-2014 Dell Inc.
@@ -18,9 +19,10 @@
 
 struct notifier_block;
 
-/* This structure will be modified by the firmware when we enter
- * system management mode, hence the volatiles */
-
+/* If called through fallback SMI rather than WMI this structure will be
+ * modified by the firmware when we enter system management mode, hence the
+ * volatiles
+ */
 struct calling_interface_buffer {
 	u16 class;
 	u16 select;
@@ -28,6 +30,13 @@ struct calling_interface_buffer {
 	volatile u32 output[4];
 } __packed;
 
+struct wmi_calling_interface_buffer {
+	struct calling_interface_buffer smi;
+	u32 argattrib;
+	u32 blength;
+	u8 data[32724];
+} __packed;
+
 struct calling_interface_token {
 	u16 tokenID;
 	u16 location;
-- 
2.14.1




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux