* [PATCH v4 01/14] platform/x86: wmi: Add new method wmidev_evaluate_method
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
Drivers properly using the wmibus can pass their wmi_device
pointer rather than the GUID back to the WMI bus to evaluate
the proper methods.
Any "new" drivers added that use the WMI bus should use this
rather than the old wmi_evaluate_method that would take the
GUID.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/wmi.c | 28 ++++++++++++++++++++++++----
include/linux/wmi.h | 6 ++++++
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 7a05843aff19..4d73a87c2ddf 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -200,6 +200,28 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
*/
acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
+{
+ struct wmi_block *wblock = NULL;
+
+ if (!find_guid(guid_string, &wblock))
+ return AE_ERROR;
+ return wmidev_evaluate_method(&wblock->dev, instance, method_id,
+ in, out);
+}
+EXPORT_SYMBOL_GPL(wmi_evaluate_method);
+
+/**
+ * wmidev_evaluate_method - Evaluate a WMI method
+ * @wdev: A wmi bus device from a driver
+ * @instance: Instance index
+ * @method_id: Method ID to call
+ * &in: Buffer containing input for the method call
+ * &out: Empty buffer to return the method results
+ *
+ * Call an ACPI-WMI method
+ */
+acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance,
+ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
@@ -209,9 +231,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
union acpi_object params[3];
char method[5] = "WM";
- if (!find_guid(guid_string, &wblock))
- return AE_ERROR;
-
+ wblock = container_of(wdev, struct wmi_block, dev);
block = &wblock->gblock;
handle = wblock->acpi_device->handle;
@@ -246,7 +266,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
return status;
}
-EXPORT_SYMBOL_GPL(wmi_evaluate_method);
+EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
struct acpi_buffer *out)
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index cd0d7734dc49..2cd10c3b89e9 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -26,6 +26,12 @@ struct wmi_device {
bool setable;
};
+/* evaluate the ACPI method associated with this device */
+extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
+ u8 instance, u32 method_id,
+ const struct acpi_buffer *in,
+ struct acpi_buffer *out);
+
/* Caller must kfree the result. */
extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
u8 instance);
--
2.14.1
^ permalink raw reply related
* [PATCH v4 03/14] platform/x86: dell-wmi: allow 32k return size in the descriptor
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
Some platforms this year will be adopting 32k WMI buffer, so don't
complain when encountering those.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/dell-wmi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 5973aa20f10e..4807ec4a2e8f 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -624,7 +624,7 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
* Vendor Signature 0 4 "DELL"
* Object Signature 4 4 " WMI"
* WMI Interface Version 8 4 <version>
- * WMI buffer length 12 4 4096
+ * WMI buffer length 12 4 4096 or 32768
*/
static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
{
@@ -673,7 +673,7 @@ static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
buffer[2]);
- if (buffer[3] != 4096)
+ if (desc_buffer[3] != 4096 && desc_buffer[3] != 32768)
dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%u)\n",
buffer[3]);
--
2.14.1
^ permalink raw reply related
* [PATCH v4 02/14] platform/x86: dell-wmi: clean up wmi descriptor check
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
Some cases the wrong type was used for errors and checks can be
done more cleanly.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Reviewed-by: Edward O'Callaghan <quasisec@google.com>
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
drivers/platform/x86/dell-wmi.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 1fbef560ca67..5973aa20f10e 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -665,16 +665,16 @@ static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
buffer = (u32 *)obj->buffer.pointer;
- if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
- dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
- 8, buffer);
+ if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0)
+ dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
+ buffer);
if (buffer[2] != 0 && buffer[2] != 1)
- dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
+ dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
buffer[2]);
if (buffer[3] != 4096)
- dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
+ dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%u)\n",
buffer[3]);
priv->interface_version = buffer[2];
--
2.14.1
^ permalink raw reply related
* [PATCH v4 12/14] platform/x86: wmi: create character devices when requested by drivers
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
For WMI operations that are only Set or Query read or write sysfs
attributes created by WMI vendor drivers make sense.
For other WMI operations that are run on Method, there needs to be a
way to guarantee to userspace that the results from the method call
belong to the data request to the method call. Sysfs attributes don't
work well in this scenario because two userspace processes may be
competing at reading/writing an attribute and step on each other's
data.
When a WMI vendor driver declares an ioctl in a file_operations object
the WMI bus driver will create a character device that maps to those
file operations.
That character device will correspond to this path:
/dev/wmi/$driver
The WMI bus driver will interpret the IOCTL calls, test them for
a valid instance and pass them on to the vendor driver to run.
This creates an implicit policy that only driver per character
device. If a module matches multiple GUID's, the wmi_devices
will need to be all handled by the same wmi_driver if the same
character device is used.
The WMI vendor drivers will be responsible for managing access to
this character device and proper locking on it.
When a WMI vendor driver is unloaded the WMI bus driver will clean
up the character device.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
MAINTAINERS | 1 +
drivers/platform/x86/wmi.c | 67 +++++++++++++++++++++++++++++++++++++++++++++-
include/linux/wmi.h | 2 ++
include/uapi/linux/wmi.h | 10 +++++++
4 files changed, 79 insertions(+), 1 deletion(-)
create mode 100644 include/uapi/linux/wmi.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 0357e9b1cfaf..6db1d84999bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -372,6 +372,7 @@ ACPI WMI DRIVER
L: platform-driver-x86@vger.kernel.org
S: Orphan
F: drivers/platform/x86/wmi.c
+F: include/uapi/linux/wmi.h
AD1889 ALSA SOUND DRIVER
M: Thibaut Varene <T-Bone@parisc-linux.org>
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index bcb41c1c7f52..5aef052b4aab 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -38,6 +38,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -69,6 +70,7 @@ struct wmi_block {
struct wmi_device dev;
struct list_head list;
struct guid_block gblock;
+ struct miscdevice misc_dev;
struct acpi_device *acpi_device;
wmi_notify_handler handler;
void *handler_data;
@@ -765,22 +767,80 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
return 0;
}
+static long wmi_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct wmi_driver *wdriver;
+ struct wmi_block *wblock;
+ const char *driver_name;
+ struct list_head *p;
+ bool found = false;
+
+ if (_IOC_TYPE(cmd) != WMI_IOC)
+ return -ENOTTY;
+
+ driver_name = filp->f_path.dentry->d_iname;
+
+ list_for_each(p, &wmi_block_list) {
+ wblock = list_entry(p, struct wmi_block, list);
+ wdriver = container_of(wblock->dev.dev.driver,
+ struct wmi_driver, driver);
+ if (strcmp(driver_name, wdriver->driver.name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found ||
+ !wdriver->file_operations ||
+ !wdriver->file_operations->unlocked_ioctl)
+ return -ENODEV;
+
+ /* make sure we're not calling a higher instance */
+ if (_IOC_NR(cmd) > wblock->gblock.instance_count)
+ return -EINVAL;
+
+ return wdriver->file_operations->unlocked_ioctl(filp, cmd, arg);
+}
+
+static const struct file_operations wmi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = wmi_ioctl,
+};
+
static int wmi_dev_probe(struct device *dev)
{
struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver =
container_of(dev->driver, struct wmi_driver, driver);
int ret = 0;
+ char *buf;
if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
dev_warn(dev, "failed to enable device -- probing anyway\n");
+ /* driver wants a character device made */
+ if (wdriver->file_operations) {
+ buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ strcpy(buf, "wmi/");
+ strcpy(buf + 4, wdriver->driver.name);
+ wblock->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ wblock->misc_dev.name = buf;
+ wblock->misc_dev.fops = &wmi_fops;
+ ret = misc_register(&wblock->misc_dev);
+ if (ret) {
+ dev_warn(dev, "failed to register char dev: %d", ret);
+ kfree(buf);
+ }
+ }
+
if (wdriver->probe) {
ret = wdriver->probe(dev_to_wdev(dev));
if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0)))
dev_warn(dev, "failed to disable device\n");
}
-
return ret;
}
@@ -791,6 +851,11 @@ static int wmi_dev_remove(struct device *dev)
container_of(dev->driver, struct wmi_driver, driver);
int ret = 0;
+ if (wdriver->file_operations) {
+ kfree(wblock->misc_dev.name);
+ misc_deregister(&wblock->misc_dev);
+ }
+
if (wdriver->remove)
ret = wdriver->remove(dev_to_wdev(dev));
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index ddee427e0721..c84db3e8038d 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/acpi.h>
+#include <uapi/linux/wmi.h>
struct wmi_device {
struct device dev;
@@ -43,6 +44,7 @@ struct wmi_device_id {
struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
+ const struct file_operations *file_operations;
int (*probe)(struct wmi_device *wdev);
int (*remove)(struct wmi_device *wdev);
diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h
new file mode 100644
index 000000000000..6a811ead7db8
--- /dev/null
+++ b/include/uapi/linux/wmi.h
@@ -0,0 +1,10 @@
+#ifndef _UAPI_LINUX_WMI_H
+#define _UAPI_LINUX_WMI_H
+
+#define WMI_IOC 'W'
+#define WMI_IO(instance) _IO(WMI_IOC, instance)
+#define WMI_IOR(instance) _IOR(WMI_IOC, instance, void*)
+#define WMI_IOW(instance) _IOW(WMI_IOC, instance, void*)
+#define WMI_IOWR(instance) _IOWR(WMI_IOC, instance, void*)
+
+#endif
--
2.14.1
^ permalink raw reply related
* [PATCH v4 11/14] platform/x86: dell-smbios-wmi: Add new WMI dispatcher driver
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
The dell-smbios stack only currently uses an SMI interface which grants
direct access to physical memory to the firmware SMM methods via a pointer.
This dispatcher driver adds a WMI-ACPI interface that is detected by WMI
probe and preferred over the SMI interface in dell-smbios.
Changing this to operate over WMI-ACPI will use an ACPI OperationRegion
for a buffer of data storage when SMM calls are performed.
This is a safer approach to use in kernel drivers as the SMM will
only have access to that OperationRegion.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
MAINTAINERS | 6 +
drivers/platform/x86/Kconfig | 16 ++-
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/dell-smbios-wmi.c | 215 +++++++++++++++++++++++++++++++++
drivers/platform/x86/dell-smbios-wmi.h | 28 +++++
5 files changed, 265 insertions(+), 1 deletion(-)
create mode 100644 drivers/platform/x86/dell-smbios-wmi.c
create mode 100644 drivers/platform/x86/dell-smbios-wmi.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a8fcb4a4e195..0357e9b1cfaf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3980,6 +3980,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/dell-smbios-smm.*
+DELL SMBIOS WMI DRIVER
+M: Mario Limonciello <mario.limonciello@dell.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/dell-smbios-wmi.*
+
DELL LAPTOP DRIVER
M: Matthew Garrett <mjg59@srcf.ucam.org>
M: Pali Rohár <pali.rohar@gmail.com>
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f0b97cb8e449..ef597f440d2e 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -93,13 +93,27 @@ config ASUS_LAPTOP
config DELL_SMBIOS
tristate "Dell SMBIOS calling interface"
- depends on DELL_SMBIOS_SMM
+ depends on DELL_SMBIOS_WMI || DELL_SMBIOS_SMM
---help---
This module provides common functions for kernel modules using
Dell SMBIOS.
If you have a Dell laptop, say Y or M here.
+config DELL_SMBIOS_WMI
+ tristate "Dell SMBIOS calling interface (WMI implementation)"
+ depends on ACPI_WMI
+ depends on DELL_WMI_DESCRIPTOR
+ default ACPI_WMI
+ select DELL_SMBIOS
+ ---help---
+ This provides an implementation for the Dell SMBIOS calling interface
+ communicated over ACPI-WMI.
+
+ If you have a Dell computer from >2007 you should say Y or M here.
+ If you aren't sure and this module doesn't work for your computer
+ it just won't load.
+
config DELL_SMBIOS_SMM
tristate "Dell SMBIOS calling interface (SMM implementation)"
depends on DCDBAS
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index e743615241f8..1c4234861de0 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
+obj-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c
new file mode 100644
index 000000000000..32e4e7dbf575
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios-wmi.c
@@ -0,0 +1,215 @@
+/*
+ * WMI methods for use with dell-smbios
+ *
+ * Copyright (c) 2017 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/wmi.h>
+#include "dell-smbios-wmi.h"
+#include "dell-wmi-descriptor.h"
+static DEFINE_MUTEX(wmi_mutex);
+static int wmi_supported;
+
+struct misc_bios_flags_structure {
+ struct dmi_header header;
+ u32 flags0;
+} __packed;
+#define FLAG_HAS_ACPI_WMI 0x02
+
+#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+
+struct wmi_smbios_priv {
+ struct wmi_calling_interface_buffer *buf;
+ struct list_head list;
+ struct wmi_device *wdev;
+ struct device *child;
+ u32 buffer_size;
+};
+static LIST_HEAD(wmi_list);
+
+static struct wmi_device *get_first_wmi_device(void)
+{
+ struct wmi_smbios_priv *priv;
+
+ priv = list_first_entry_or_null(&wmi_list,
+ struct wmi_smbios_priv,
+ list);
+ return priv ? priv->wdev : NULL;
+}
+
+static int run_smbios_call(struct wmi_device *wdev)
+{
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct wmi_smbios_priv *priv;
+ struct acpi_buffer input;
+ union acpi_object *obj;
+ acpi_status status;
+
+ priv = dev_get_drvdata(&wdev->dev);
+ input.length = priv->buffer_size;
+ input.pointer = priv->buf;
+
+ status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
+ if (ACPI_FAILURE(status)) {
+ pr_err("%x/%x [%x,%x,%x,%x] call failed\n",
+ priv->buf->class, priv->buf->select,
+ priv->buf->input[0], priv->buf->input[1],
+ priv->buf->input[2], priv->buf->input[3]);
+ return -EIO;
+ }
+ obj = (union acpi_object *)output.pointer;
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ if (obj->type == ACPI_TYPE_INTEGER)
+ pr_err("SMBIOS call failed: %llu\n",
+ obj->integer.value);
+ else
+ pr_err("invalid type: %d\n", obj->type);
+ return -EIO;
+ }
+ memcpy(priv->buf, obj->buffer.pointer, input.length);
+
+ return 0;
+}
+
+void dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
+{
+ struct wmi_smbios_priv *priv;
+ struct wmi_device *wdev;
+ size_t difference;
+ size_t size;
+ u8 *start;
+
+ wdev = get_first_wmi_device();
+ if (!wdev) {
+ pr_info("No WMI devices bound");
+ return;
+ }
+ priv = dev_get_drvdata(&wdev->dev);
+ size = sizeof(struct calling_interface_buffer);
+ difference = priv->buffer_size - size;
+ start = (u8 *) priv->buf + size;
+ mutex_lock(&wmi_mutex);
+ memset(start, 0, difference);
+ memcpy(priv->buf, buffer, size);
+ run_smbios_call(wdev);
+ memcpy(buffer, priv->buf, size);
+ mutex_unlock(&wmi_mutex);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_wmi_call);
+
+static int dell_smbios_wmi_probe(struct wmi_device *wdev)
+{
+ struct wmi_smbios_priv *priv;
+ int count;
+ int ret;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* WMI buffer size will be either 4k or 32k depending on machine */
+ if (!dell_wmi_get_size(&priv->buffer_size))
+ return -EINVAL;
+
+ count = get_order(priv->buffer_size);
+ priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
+ if (!priv->buf)
+ return -ENOMEM;
+
+ /* ID is used by dell-smbios to set priority of drivers */
+ wdev->dev.id = 1;
+ ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
+ if (ret)
+ goto fail_register;
+
+ priv->wdev = wdev;
+ dev_set_drvdata(&wdev->dev, priv);
+ list_add_tail(&priv->list, &wmi_list);
+
+ return 0;
+
+fail_register:
+ free_pages((unsigned long)priv->buf, count);
+ return ret;
+}
+
+static int dell_smbios_wmi_remove(struct wmi_device *wdev)
+{
+ struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
+ int count;
+
+ list_del(&priv->list);
+ dell_smbios_unregister_device(&wdev->dev);
+ count = get_order(priv->buffer_size);
+ free_pages((unsigned long)priv->buf, count);
+ return 0;
+}
+
+static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
+ { .guid_string = DELL_WMI_SMBIOS_GUID },
+ { },
+};
+
+static void __init parse_b1_table(const struct dmi_header *dm)
+{
+ struct misc_bios_flags_structure *flags =
+ container_of(dm, struct misc_bios_flags_structure, header);
+
+ /* 4 bytes header, and one word of flags */
+ if (dm->length < 12)
+ return;
+ if ((flags->flags0 & FLAG_HAS_ACPI_WMI))
+ wmi_supported = 1;
+}
+
+static void __init find_b1(const struct dmi_header *dm, void *dummy)
+{
+ switch (dm->type) {
+ case 0xb1: /* misc bios flags */
+ parse_b1_table(dm);
+ break;
+ }
+}
+
+static struct wmi_driver dell_smbios_wmi_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 init_dell_smbios_wmi(void)
+{
+ dmi_walk(find_b1, NULL);
+
+ if (!wmi_supported)
+ return -ENODEV;
+
+ return wmi_driver_register(&dell_smbios_wmi_driver);
+}
+
+static void __exit exit_dell_smbios_wmi(void)
+{
+ wmi_driver_unregister(&dell_smbios_wmi_driver);
+}
+
+module_init(init_dell_smbios_wmi);
+module_exit(exit_dell_smbios_wmi);
+
+MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID);
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell SMBIOS communications over WMI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios-wmi.h b/drivers/platform/x86/dell-smbios-wmi.h
new file mode 100644
index 000000000000..5dd91d8ff3d8
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios-wmi.h
@@ -0,0 +1,28 @@
+/*
+ * WMI methods for use with dell-smbios
+ *
+ * Copyright (c) 2017 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DELL_SMBIOS_WMI_H_
+#define _DELL_SMBIOS_WMI_H_
+
+#include "dell-smbios.h"
+
+struct wmi_calling_interface_buffer {
+ u16 class;
+ u16 select;
+ u32 input[4];
+ u32 output[4];
+ u32 argattrib;
+ u32 blength;
+ u8 *data;
+} __packed;
+
+void dell_smbios_wmi_call(struct calling_interface_buffer *buffer);
+
+#endif
--
2.14.1
^ permalink raw reply related
* [PATCH v4 10/14] platform/x86: dell-smbios-smm: test for WSMT
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
WSMT is as an attestation to the OS that the platform won't
modify memory outside of pre-defined areas.
If a platform has WSMT enabled in BIOS setup, SMM calls through
dcdbas will fail. The only way to access platform data in these
instances is through the WMI SMBIOS calling interface.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/dell-smbios-smm.c | 36 ++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c
index 2e1c5d2dfd61..3271e9a876e9 100644
--- a/drivers/platform/x86/dell-smbios-smm.c
+++ b/drivers/platform/x86/dell-smbios-smm.c
@@ -25,6 +25,8 @@ static struct calling_interface_buffer *buffer;
struct platform_device *platform_device;
static DEFINE_MUTEX(smm_mutex);
+#define WSMT_EN_TOKEN 0x04EC
+
static const struct dmi_system_id dell_device_table[] __initconst = {
{
.ident = "Dell laptop",
@@ -76,6 +78,32 @@ void dell_smbios_smm_call(struct calling_interface_buffer *input)
}
EXPORT_SYMBOL_GPL(dell_smbios_smm_call);
+static int test_wsmt_enabled(void)
+{
+ struct calling_interface_token *token;
+ int ret;
+
+ /* if token doesn't exist, SMM will work */
+ token = dell_smbios_find_token(WSMT_EN_TOKEN);
+ if (!token)
+ return 0;
+
+ /* if token exists, try to access over SMM */
+ buffer->class = 0;
+ buffer->select = 0;
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->input[0] = token->location;
+ dell_smbios_smm_call(buffer);
+
+ /* if lookup failed, we know WSMT was enabled */
+ if (buffer->output[0] != 0)
+ return 1;
+
+ /* query token status if it didn't fail */
+ ret = (buffer->output[1] == token->value);
+ return ret;
+}
+
static int __init dell_smbios_smm_init(void)
{
int ret;
@@ -88,6 +116,13 @@ static int __init dell_smbios_smm_init(void)
return -ENOMEM;
dell_smbios_get_smm_address(&da_command_address, &da_command_code);
+ ret = test_wsmt_enabled();
+ pr_debug("WSMT enable test: %d\n", ret);
+ if (ret) {
+ ret = -ENODEV;
+ goto fail_wsmt;
+ }
+
platform_device = platform_device_alloc("dell-smbios", 1);
if (!platform_device) {
ret = -ENOMEM;
@@ -111,6 +146,7 @@ static int __init dell_smbios_smm_init(void)
fail_platform_device_add:
platform_device_put(platform_device);
+fail_wsmt:
fail_platform_device_alloc:
free_page((unsigned long)buffer);
return ret;
--
2.14.1
^ permalink raw reply related
* [PATCH v4 09/14] platform/x86: dell-smbios: Introduce dispatcher for SMM calls
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
This splits up the dell-smbios driver into two drivers:
* dell-smbios
* dell-smbios-smm
dell-smbios can operate with multiple different dispatcher drivers to
perform SMBIOS operations.
Also modify the interface that dell-laptop and dell-wmi use align to this
model more closely. Rather than a single global buffer being allocated
for all drivers, each driver will allocate and be responsible for it's own
buffer. The pointer will be passed to the calling function and each
dispatcher driver will then internally copy it to the proper location to
perform it's call.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
MAINTAINERS | 6 ++
drivers/platform/x86/Kconfig | 17 ++-
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/dell-laptop.c | 191 ++++++++++++++++-----------------
drivers/platform/x86/dell-smbios-smm.c | 136 +++++++++++++++++++++++
drivers/platform/x86/dell-smbios-smm.h | 22 ++++
drivers/platform/x86/dell-smbios.c | 120 +++++++++++++--------
drivers/platform/x86/dell-smbios.h | 13 ++-
drivers/platform/x86/dell-wmi.c | 8 +-
9 files changed, 361 insertions(+), 153 deletions(-)
create mode 100644 drivers/platform/x86/dell-smbios-smm.c
create mode 100644 drivers/platform/x86/dell-smbios-smm.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 2e3f2aea0370..a8fcb4a4e195 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3974,6 +3974,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/dell-smbios.*
+DELL SMBIOS SMM DRIVER
+M: Mario Limonciello <mario.limonciello@dell.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/dell-smbios-smm.*
+
DELL LAPTOP DRIVER
M: Matthew Garrett <mjg59@srcf.ucam.org>
M: Pali Rohár <pali.rohar@gmail.com>
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index bc347c326d87..f0b97cb8e449 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -92,14 +92,27 @@ config ASUS_LAPTOP
If you have an ACPI-compatible ASUS laptop, say Y or M here.
config DELL_SMBIOS
- tristate
- select DCDBAS
+ tristate "Dell SMBIOS calling interface"
+ depends on DELL_SMBIOS_SMM
---help---
This module provides common functions for kernel modules using
Dell SMBIOS.
If you have a Dell laptop, say Y or M here.
+config DELL_SMBIOS_SMM
+ tristate "Dell SMBIOS calling interface (SMM implementation)"
+ depends on DCDBAS
+ default DCDBAS
+ select DELL_SMBIOS
+ ---help---
+ This provides an implementation for the Dell SMBIOS calling interface
+ communicated over ACPI-WMI.
+
+ If you have a Dell computer from >2007 you should say Y or M here.
+ If you aren't sure and this module doesn't work for your computer
+ it just won't load.
+
config DELL_LAPTOP
tristate "Dell Laptop Extras"
depends on DMI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 8636f5d3424f..e743615241f8 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
+obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index f42159fd2031..c648bbfcc394 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -85,6 +85,7 @@ static struct platform_driver platform_driver = {
}
};
+static struct calling_interface_buffer *buffer;
static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
@@ -405,7 +406,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
static int dell_rfkill_set(void *data, bool blocked)
{
- struct calling_interface_buffer *buffer;
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
int hwswitch_bit = (unsigned long)data - 1;
@@ -413,19 +413,21 @@ static int dell_rfkill_set(void *data, bool blocked)
int status;
int ret;
- buffer = dell_smbios_get_buffer();
-
- dell_smbios_send_request(17, 11);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0)
goto out;
- dell_smbios_clear_buffer();
-
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
buffer->input[0] = 0x2;
- dell_smbios_send_request(17, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
hwswitch = buffer->output[1];
@@ -435,28 +437,29 @@ static int dell_rfkill_set(void *data, bool blocked)
(status & BIT(0)) && !(status & BIT(16)))
disable = 1;
- dell_smbios_clear_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
- dell_smbios_send_request(17, 11);
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
out:
- dell_smbios_release_buffer();
return dell_smbios_error(ret);
}
-/* Must be called with the buffer held */
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
- int status,
- struct calling_interface_buffer *buffer)
+ int status)
{
if (status & BIT(0)) {
/* Has hw-switch, sync sw_state to BIOS */
int block = rfkill_blocked(rfkill);
- dell_smbios_clear_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
buffer->input[0] = (1 | (radio << 8) | (block << 16));
- dell_smbios_send_request(17, 11);
+ dell_smbios_call(buffer);
} else {
/* No hw-switch, sync BIOS state to sw_state */
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -472,32 +475,30 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
- struct calling_interface_buffer *buffer;
int radio = ((unsigned long)data & 0xF);
int hwswitch;
int status;
int ret;
- buffer = dell_smbios_get_buffer();
-
- dell_smbios_send_request(17, 11);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0 || !(status & BIT(0))) {
- dell_smbios_release_buffer();
return;
}
- dell_smbios_clear_buffer();
-
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
buffer->input[0] = 0x2;
- dell_smbios_send_request(17, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
hwswitch = buffer->output[1];
- dell_smbios_release_buffer();
-
if (ret != 0)
return;
@@ -513,27 +514,26 @@ static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
{
- struct calling_interface_buffer *buffer;
int hwswitch_state;
int hwswitch_ret;
int status;
int ret;
- buffer = dell_smbios_get_buffer();
-
- dell_smbios_send_request(17, 11);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
status = buffer->output[1];
- dell_smbios_clear_buffer();
-
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
buffer->input[0] = 0x2;
- dell_smbios_send_request(17, 11);
+ dell_smbios_call(buffer);
hwswitch_ret = buffer->output[0];
hwswitch_state = buffer->output[1];
- dell_smbios_release_buffer();
-
seq_printf(s, "return:\t%d\n", ret);
seq_printf(s, "status:\t0x%X\n", status);
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
@@ -613,24 +613,25 @@ static const struct file_operations dell_debugfs_fops = {
static void dell_update_rfkill(struct work_struct *ignored)
{
- struct calling_interface_buffer *buffer;
int hwswitch = 0;
int status;
int ret;
- buffer = dell_smbios_get_buffer();
-
- dell_smbios_send_request(17, 11);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
status = buffer->output[1];
if (ret != 0)
- goto out;
-
- dell_smbios_clear_buffer();
+ return;
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
buffer->input[0] = 0x2;
- dell_smbios_send_request(17, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
if (ret == 0 && (status & BIT(0)))
@@ -638,21 +639,17 @@ static void dell_update_rfkill(struct work_struct *ignored)
if (wifi_rfkill) {
dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
- dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
+ dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
}
if (bluetooth_rfkill) {
dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
hwswitch);
- dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
- buffer);
+ dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
}
if (wwan_rfkill) {
dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
- dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
+ dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
}
-
- out:
- dell_smbios_release_buffer();
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
@@ -696,7 +693,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
static int __init dell_setup_rfkill(void)
{
- struct calling_interface_buffer *buffer;
int status, ret, whitelisted;
const char *product;
@@ -712,11 +708,12 @@ static int __init dell_setup_rfkill(void)
if (!force_rfkill && !whitelisted)
return 0;
- buffer = dell_smbios_get_buffer();
- dell_smbios_send_request(17, 11);
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 17;
+ buffer->select = 11;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
status = buffer->output[1];
- dell_smbios_release_buffer();
/* dell wireless info smbios call is not supported */
if (ret != 0)
@@ -869,7 +866,6 @@ static void dell_cleanup_rfkill(void)
static int dell_send_intensity(struct backlight_device *bd)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
int ret;
@@ -877,24 +873,22 @@ static int dell_send_intensity(struct backlight_device *bd)
if (!token)
return -ENODEV;
- buffer = dell_smbios_get_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
buffer->input[0] = token->location;
buffer->input[1] = bd->props.brightness;
-
+ buffer->class = 1;
if (power_supply_is_system_supplied() > 0)
- dell_smbios_send_request(1, 2);
+ buffer->select = 2;
else
- dell_smbios_send_request(1, 1);
-
+ buffer->select = 1;
+ dell_smbios_call(buffer);
ret = dell_smbios_error(buffer->output[0]);
- dell_smbios_release_buffer();
return ret;
}
static int dell_get_intensity(struct backlight_device *bd)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
int ret;
@@ -902,20 +896,20 @@ static int dell_get_intensity(struct backlight_device *bd)
if (!token)
return -ENODEV;
- buffer = dell_smbios_get_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
buffer->input[0] = token->location;
-
+ buffer->class = 0;
if (power_supply_is_system_supplied() > 0)
- dell_smbios_send_request(0, 2);
+ buffer->select = 2;
else
- dell_smbios_send_request(0, 1);
+ buffer->select = 1;
+ dell_smbios_call(buffer);
if (buffer->output[0])
ret = dell_smbios_error(buffer->output[0]);
else
ret = buffer->output[1];
- dell_smbios_release_buffer();
return ret;
}
@@ -1179,14 +1173,14 @@ static DEFINE_MUTEX(kbd_led_mutex);
static int kbd_get_info(struct kbd_info *info)
{
- struct calling_interface_buffer *buffer;
u8 units;
int ret;
- buffer = dell_smbios_get_buffer();
-
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 4;
+ buffer->select = 11;
buffer->input[0] = 0x0;
- dell_smbios_send_request(4, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
if (ret) {
@@ -1210,7 +1204,6 @@ static int kbd_get_info(struct kbd_info *info)
info->days = (buffer->output[3] >> 24) & 0xFF;
out:
- dell_smbios_release_buffer();
return ret;
}
@@ -1269,13 +1262,13 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
static int kbd_get_state(struct kbd_state *state)
{
- struct calling_interface_buffer *buffer;
int ret;
- buffer = dell_smbios_get_buffer();
-
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 4;
+ buffer->select = 11;
buffer->input[0] = 0x1;
- dell_smbios_send_request(4, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
if (ret) {
@@ -1297,16 +1290,16 @@ static int kbd_get_state(struct kbd_state *state)
state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;
out:
- dell_smbios_release_buffer();
return ret;
}
static int kbd_set_state(struct kbd_state *state)
{
- struct calling_interface_buffer *buffer;
int ret;
- buffer = dell_smbios_get_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 4;
+ buffer->select = 11;
buffer->input[0] = 0x2;
buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
buffer->input[1] |= (state->triggers & 0xFF) << 16;
@@ -1316,9 +1309,8 @@ static int kbd_set_state(struct kbd_state *state)
buffer->input[2] |= (state->level & 0xFF) << 16;
buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24;
buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30;
- dell_smbios_send_request(4, 11);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
- dell_smbios_release_buffer();
return dell_smbios_error(ret);
}
@@ -1345,7 +1337,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
static int kbd_set_token_bit(u8 bit)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
int ret;
@@ -1356,19 +1347,19 @@ static int kbd_set_token_bit(u8 bit)
if (!token)
return -EINVAL;
- buffer = dell_smbios_get_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
buffer->input[0] = token->location;
buffer->input[1] = token->value;
- dell_smbios_send_request(1, 0);
+ buffer->class = 1;
+ buffer->select = 0;
+ dell_smbios_call(buffer);
ret = buffer->output[0];
- dell_smbios_release_buffer();
return dell_smbios_error(ret);
}
static int kbd_get_token_bit(u8 bit)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
int ret;
int val;
@@ -1380,12 +1371,14 @@ static int kbd_get_token_bit(u8 bit)
if (!token)
return -EINVAL;
- buffer = dell_smbios_get_buffer();
+
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 0;
+ buffer->select = 0;
buffer->input[0] = token->location;
- dell_smbios_send_request(0, 0);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
val = buffer->output[1];
- dell_smbios_release_buffer();
if (ret)
return dell_smbios_error(ret);
@@ -2102,7 +2095,6 @@ static struct notifier_block dell_laptop_notifier = {
int dell_micmute_led_set(int state)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
if (state == 0)
@@ -2115,11 +2107,12 @@ int dell_micmute_led_set(int state)
if (!token)
return -ENODEV;
- buffer = dell_smbios_get_buffer();
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ buffer->class = 1;
+ buffer->select = 0;
buffer->input[0] = token->location;
buffer->input[1] = token->value;
- dell_smbios_send_request(1, 0);
- dell_smbios_release_buffer();
+ dell_smbios_call(buffer);
return state;
}
@@ -2127,7 +2120,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set);
static int __init dell_init(void)
{
- struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
int max_intensity = 0;
int ret;
@@ -2158,6 +2150,10 @@ static int __init dell_init(void)
goto fail_rfkill;
}
+ buffer = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!buffer)
+ goto fail_buffer;
+
if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev);
@@ -2175,12 +2171,12 @@ static int __init dell_init(void)
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
if (token) {
- buffer = dell_smbios_get_buffer();
buffer->input[0] = token->location;
- dell_smbios_send_request(0, 2);
+ buffer->class = 0;
+ buffer->select = 2;
+ dell_smbios_call(buffer);
if (buffer->output[0] == 0)
max_intensity = buffer->output[3];
- dell_smbios_release_buffer();
}
if (max_intensity) {
@@ -2214,6 +2210,8 @@ static int __init dell_init(void)
fail_get_brightness:
backlight_device_unregister(dell_backlight_device);
fail_backlight:
+ free_page((unsigned long)buffer);
+fail_buffer:
dell_cleanup_rfkill();
fail_rfkill:
platform_device_del(platform_device);
@@ -2233,6 +2231,7 @@ static void __exit dell_exit(void)
touchpad_led_exit();
kbd_led_exit();
backlight_device_unregister(dell_backlight_device);
+ free_page((unsigned long)buffer);
dell_cleanup_rfkill();
if (platform_device) {
platform_device_unregister(platform_device);
diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c
new file mode 100644
index 000000000000..2e1c5d2dfd61
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios-smm.c
@@ -0,0 +1,136 @@
+/*
+ * SMI methods for use with dell-smbios
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (c) 2017 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include "../../firmware/dcdbas.h"
+#include "dell-smbios.h"
+
+static int da_command_address;
+static int da_command_code;
+static struct calling_interface_buffer *buffer;
+struct platform_device *platform_device;
+static DEFINE_MUTEX(smm_mutex);
+
+static const struct dmi_system_id dell_device_table[] __initconst = {
+ {
+ .ident = "Dell laptop",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
+ },
+ },
+ {
+ .ident = "Dell Computer Corporation",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
+ },
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(dmi, dell_device_table);
+
+void dell_smbios_smm_call(struct calling_interface_buffer *input)
+{
+ struct smi_cmd command;
+ size_t size;
+
+ size = sizeof(struct calling_interface_buffer);
+ 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;
+
+ mutex_lock(&smm_mutex);
+ memcpy(buffer, input, size);
+ dcdbas_smi_request(&command);
+ memcpy(input, buffer, size);
+ mutex_unlock(&smm_mutex);
+}
+EXPORT_SYMBOL_GPL(dell_smbios_smm_call);
+
+static int __init dell_smbios_smm_init(void)
+{
+ int ret;
+ /*
+ * 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)
+ return -ENOMEM;
+ dell_smbios_get_smm_address(&da_command_address, &da_command_code);
+
+ platform_device = platform_device_alloc("dell-smbios", 1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device_alloc;
+ }
+
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device_add;
+
+ ret = dell_smbios_register_device(&platform_device->dev,
+ &dell_smbios_smm_call);
+ if (ret)
+ goto fail_register;
+
+ return 0;
+
+fail_register:
+ platform_device_del(platform_device);
+
+fail_platform_device_add:
+ platform_device_put(platform_device);
+
+fail_platform_device_alloc:
+ free_page((unsigned long)buffer);
+ return ret;
+}
+
+static void __exit dell_smbios_smm_exit(void)
+{
+ if (platform_device) {
+ dell_smbios_unregister_device(&platform_device->dev);
+ platform_device_unregister(platform_device);
+ free_page((unsigned long)buffer);
+ }
+}
+
+subsys_initcall(dell_smbios_smm_init);
+module_exit(dell_smbios_smm_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell SMBIOS communications over SMI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios-smm.h b/drivers/platform/x86/dell-smbios-smm.h
new file mode 100644
index 000000000000..3bbf716a1bdb
--- /dev/null
+++ b/drivers/platform/x86/dell-smbios-smm.h
@@ -0,0 +1,22 @@
+/*
+ * SMI methods for use with dell-smbios
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (c) 2017 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DELL_SMBIOS_SMI_H_
+#define _DELL_SMBIOS_SMI_H_
+
+#include "dell-smbios.h"
+
+void dell_smbios_smm_call(struct calling_interface_buffer *input);
+//extern struct platform_driver dell_smbios_smm_platform_driver;
+
+#endif
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
index 7275d1d48190..cdfea9e9610b 100644
--- a/drivers/platform/x86/dell-smbios.c
+++ b/drivers/platform/x86/dell-smbios.c
@@ -18,11 +18,9 @@
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/err.h>
-#include <linux/gfp.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/io.h>
-#include "../../firmware/dcdbas.h"
#include <linux/platform_device.h>
#include "dell-smbios.h"
@@ -34,14 +32,27 @@ struct calling_interface_structure {
struct calling_interface_token tokens[];
} __packed;
-static struct calling_interface_buffer *buffer;
-static DEFINE_MUTEX(buffer_mutex);
-
static int da_command_address;
static int da_command_code;
static int da_num_tokens;
static struct platform_device *platform_device;
static struct calling_interface_token *da_tokens;
+static DEFINE_MUTEX(smbios_mutex);
+
+struct smbios_device {
+ struct list_head list;
+ struct device *device;
+ void (*call_fn)(struct calling_interface_buffer *);
+};
+
+static LIST_HEAD(smbios_device_list);
+
+void dell_smbios_get_smm_address(int *address, int *code)
+{
+ *address = da_command_address;
+ *code = da_command_code;
+}
+EXPORT_SYMBOL_GPL(dell_smbios_get_smm_address);
int dell_smbios_error(int value)
{
@@ -58,42 +69,64 @@ int dell_smbios_error(int value)
}
EXPORT_SYMBOL_GPL(dell_smbios_error);
-struct calling_interface_buffer *dell_smbios_get_buffer(void)
-{
- mutex_lock(&buffer_mutex);
- dell_smbios_clear_buffer();
- return buffer;
-}
-EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
-
-void dell_smbios_clear_buffer(void)
+int dell_smbios_register_device(struct device *d, void *call_fn)
{
- memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ struct smbios_device *priv;
+
+ pr_debug("Added device: %s:%s\n", d->driver->name, dev_name(d));
+ priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ get_device(d);
+ priv->device = d;
+ priv->call_fn = call_fn;
+ mutex_lock(&smbios_mutex);
+ list_add_tail(&priv->list, &smbios_device_list);
+ mutex_unlock(&smbios_mutex);
+ return 0;
}
-EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
+EXPORT_SYMBOL_GPL(dell_smbios_register_device);
-void dell_smbios_release_buffer(void)
+void dell_smbios_unregister_device(struct device *d)
{
- mutex_unlock(&buffer_mutex);
+ struct smbios_device *priv;
+
+ pr_debug("Remove device: %s:%s\n", d->driver->name, dev_name(d));
+ mutex_lock(&smbios_mutex);
+ list_for_each_entry(priv, &smbios_device_list, list) {
+ if (priv->device == d) {
+ list_del(&priv->list);
+ put_device(d);
+ break;
+ }
+ };
+ mutex_unlock(&smbios_mutex);
}
-EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
+EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
-void dell_smbios_send_request(int class, int select)
+void dell_smbios_call(struct calling_interface_buffer *buffer)
{
- struct smi_cmd command;
-
- 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;
-
- buffer->class = class;
- buffer->select = select;
-
- dcdbas_smi_request(&command);
+ void (*call_fn)(struct calling_interface_buffer *) = NULL;
+ struct smbios_device *priv;
+ int id = 0;
+
+ mutex_lock(&smbios_mutex);
+ list_for_each_entry(priv, &smbios_device_list, list) {
+ if (priv->device->id >= id) {
+ pr_debug("Setting %s (ID: %d)\n",
+ dev_name(priv->device), priv->device->id);
+ call_fn = priv->call_fn;
+ id = priv->device->id;
+ }
+ };
+
+ if (call_fn)
+ call_fn(buffer);
+ else
+ pr_err("No dell-smbios drivers are loaded\n");
+ mutex_unlock(&smbios_mutex);
}
-EXPORT_SYMBOL_GPL(dell_smbios_send_request);
+EXPORT_SYMBOL_GPL(dell_smbios_call);
struct calling_interface_token *dell_smbios_find_token(int tokenid)
{
@@ -224,15 +257,6 @@ static int __init dell_smbios_init(void)
return -ENODEV;
}
- /*
- * 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;
- }
ret = platform_driver_register(&platform_driver);
if (ret)
goto fail_platform_driver;
@@ -262,17 +286,19 @@ static int __init dell_smbios_init(void)
platform_driver_unregister(&platform_driver);
fail_platform_driver:
- free_page((unsigned long)buffer);
-
-fail_buffer:
kfree(da_tokens);
return ret;
}
static void __exit dell_smbios_exit(void)
{
- kfree(da_tokens);
- free_page((unsigned long)buffer);
+ if (platform_device) {
+ sysfs_remove_group(&platform_device->dev.kobj,
+ &smbios_attribute_group);
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+ kfree(da_tokens);
+ }
}
subsys_initcall(dell_smbios_init);
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
index 45cbc2292cd3..33eb8633a1c1 100644
--- a/drivers/platform/x86/dell-smbios.h
+++ b/drivers/platform/x86/dell-smbios.h
@@ -16,6 +16,8 @@
#ifndef _DELL_SMBIOS_H_
#define _DELL_SMBIOS_H_
+#include <linux/device.h>
+
struct notifier_block;
/* This structure will be modified by the firmware when we enter
@@ -37,12 +39,13 @@ struct calling_interface_token {
};
};
-int dell_smbios_error(int value);
-struct calling_interface_buffer *dell_smbios_get_buffer(void);
-void dell_smbios_clear_buffer(void);
-void dell_smbios_release_buffer(void);
-void dell_smbios_send_request(int class, int select);
+int dell_smbios_register_device(struct device *d, void *call_fn);
+void dell_smbios_unregister_device(struct device *d);
+
+void dell_smbios_get_smm_address(int *address, int *command);
+int dell_smbios_error(int value);
+void dell_smbios_call(struct calling_interface_buffer *buffer);
struct calling_interface_token *dell_smbios_find_token(int tokenid);
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 90d7e8e55e9b..5e04e202d928 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -641,13 +641,15 @@ static int dell_wmi_events_set_enabled(bool enable)
struct calling_interface_buffer *buffer;
int ret;
- buffer = dell_smbios_get_buffer();
+ buffer = (void *)get_zeroed_page(GFP_KERNEL);
+ buffer->class = 17;
+ buffer->select = 3;
buffer->input[0] = 0x10000;
buffer->input[1] = 0x51534554;
buffer->input[3] = enable;
- dell_smbios_send_request(17, 3);
+ dell_smbios_call(buffer);
ret = buffer->output[0];
- dell_smbios_release_buffer();
+ free_page((unsigned long)buffer);
return dell_smbios_error(ret);
}
--
2.14.1
^ permalink raw reply related
* [PATCH v4 08/14] platform/x86: dell-smbios: Add a sysfs interface for SMBIOS tokens
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
Currently userspace tools can access system tokens via the dcdbas
kernel module and a SMI call that will cause the platform to execute
SMM code.
With a goal in mind of deprecating the dcdbas kernel module a different
method for accessing these tokens from userspace needs to be created.
This is intentionally marked to only be readable as root as it can
contain sensitive information about the platform's configuration.
MAINTAINERS was missing for this driver. Add myself and Pali to
maintainers list for it.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
.../ABI/testing/sysfs-platform-dell-smbios | 16 ++++++
MAINTAINERS | 7 +++
drivers/platform/x86/dell-smbios.c | 64 ++++++++++++++++++++++
3 files changed, 87 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-smbios
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-smbios b/Documentation/ABI/testing/sysfs-platform-dell-smbios
new file mode 100644
index 000000000000..d97f4bd5bd91
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-dell-smbios
@@ -0,0 +1,16 @@
+What: /sys/devices/platform/<platform>/tokens
+Date: November 2017
+KernelVersion: 4.15
+Contact: "Mario Limonciello" <mario.limonciello@dell.com>
+Description:
+ A read-only description of Dell platform tokens
+ available on the machine.
+
+ The tokens will be displayed in the following
+ machine readable format with each token on a
+ new line:
+
+ ID Location value
+
+ For example token:
+ 5 5 3
diff --git a/MAINTAINERS b/MAINTAINERS
index 659dbeec4191..2e3f2aea0370 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3967,6 +3967,13 @@ M: "Maciej W. Rozycki" <macro@linux-mips.org>
S: Maintained
F: drivers/net/fddi/defxx.*
+DELL SMBIOS DRIVER
+M: Pali Rohár <pali.rohar@gmail.com>
+M: Mario Limonciello <mario.limonciello@dell.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/dell-smbios.*
+
DELL LAPTOP DRIVER
M: Matthew Garrett <mjg59@srcf.ucam.org>
M: Pali Rohár <pali.rohar@gmail.com>
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
index 873d1c3f7641..7275d1d48190 100644
--- a/drivers/platform/x86/dell-smbios.c
+++ b/drivers/platform/x86/dell-smbios.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include "../../firmware/dcdbas.h"
+#include <linux/platform_device.h>
#include "dell-smbios.h"
struct calling_interface_structure {
@@ -39,6 +40,7 @@ static DEFINE_MUTEX(buffer_mutex);
static int da_command_address;
static int da_command_code;
static int da_num_tokens;
+static struct platform_device *platform_device;
static struct calling_interface_token *da_tokens;
int dell_smbios_error(int value)
@@ -170,6 +172,40 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
}
}
+static ssize_t tokens_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ size_t off = 0;
+ int to_print;
+ int i;
+
+ to_print = min(da_num_tokens, (int)(PAGE_SIZE - 1) / 15);
+ for (i = 0; i < to_print; i++) {
+ off += scnprintf(buf+off, PAGE_SIZE-off, "%04x\t%04x\t%04x\n",
+ da_tokens[i].tokenID, da_tokens[i].location,
+ da_tokens[i].value);
+ }
+
+ return off;
+}
+
+DEVICE_ATTR(tokens, 0400, tokens_show, NULL);
+
+static struct attribute *smbios_attrs[] = {
+ &dev_attr_tokens.attr,
+ NULL
+};
+
+static const struct attribute_group smbios_attribute_group = {
+ .attrs = smbios_attrs,
+};
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "dell-smbios",
+ },
+};
+
static int __init dell_smbios_init(void)
{
const struct dmi_device *valid;
@@ -197,9 +233,37 @@ static int __init dell_smbios_init(void)
ret = -ENOMEM;
goto fail_buffer;
}
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+
+ platform_device = platform_device_alloc("dell-smbios", 0);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device_alloc;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device_add;
+ ret = sysfs_create_group(&platform_device->dev.kobj,
+ &smbios_attribute_group);
+ if (ret)
+ goto fail_create_group;
return 0;
+fail_create_group:
+ platform_device_del(platform_device);
+
+fail_platform_device_add:
+ platform_device_put(platform_device);
+
+fail_platform_device_alloc:
+ platform_driver_unregister(&platform_driver);
+
+fail_platform_driver:
+ free_page((unsigned long)buffer);
+
fail_buffer:
kfree(da_tokens);
return ret;
--
2.14.1
^ permalink raw reply related
* [PATCH v4 07/14] platform/x86: dell-smbios: only run if proper oem string is detected
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
The proper way to indicate that a system is a 'supported' Dell System
is by the presence of this string in OEM strings.
Allowing the driver to load on non-Dell systems will have undefined
results.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/dell-smbios.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c
index e9b1ca07c872..873d1c3f7641 100644
--- a/drivers/platform/x86/dell-smbios.c
+++ b/drivers/platform/x86/dell-smbios.c
@@ -172,8 +172,15 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
static int __init dell_smbios_init(void)
{
+ const struct dmi_device *valid;
int ret;
+ valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL);
+ if (!valid) {
+ pr_info("Unable to run on non-Dell system\n");
+ return -ENODEV;
+ }
+
dmi_walk(find_tokens, NULL);
if (!da_tokens) {
--
2.14.1
^ permalink raw reply related
* [PATCH v4 06/14] platform/x86: wmi: Don't allow drivers to get each other's GUIDs
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
The only driver using this was dell-wmi, and it really was a hack.
The driver was getting a data attribute from another driver and this
type of action should not be encouraged.
Rather drivers that need to interact with one another should pass
data back and forth via exported functions.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/wmi.c | 17 -----------------
include/linux/wmi.h | 4 ----
2 files changed, 21 deletions(-)
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 4d73a87c2ddf..bcb41c1c7f52 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -368,23 +368,6 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
}
EXPORT_SYMBOL_GPL(wmidev_block_query);
-struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
- const char *guid_string)
-{
- struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev);
- struct wmi_block *other_wb;
-
- if (!find_guid(guid_string, &other_wb))
- return NULL;
-
- if (other_wb->acpi_device != this_wb->acpi_device)
- return NULL;
-
- get_device(&other_wb->dev.dev);
- return &other_wb->dev;
-}
-EXPORT_SYMBOL_GPL(wmidev_get_other_guid);
-
/**
* wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 2cd10c3b89e9..ddee427e0721 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -36,10 +36,6 @@ extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
u8 instance);
-/* Gets another device on the same bus. Caller must put_device the result. */
-extern struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
- const char *guid_string);
-
struct wmi_device_id {
const char *guid_string;
};
--
2.14.1
^ permalink raw reply related
* [PATCH v4 05/14] platform/x86: dell-wmi-descriptor: split WMI descriptor into it's own driver
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
All communication on individual GUIDs should occur in separate drivers.
Allowing a driver to communicate with the bus to another GUID is just
a hack that discourages drivers to adopt the bus model.
The information found from the WMI descriptor driver is now exported
for use by other drivers.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
MAINTAINERS | 5 +
drivers/platform/x86/Kconfig | 12 +++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/dell-wmi-descriptor.c | 162 +++++++++++++++++++++++++++++
drivers/platform/x86/dell-wmi-descriptor.h | 18 ++++
drivers/platform/x86/dell-wmi.c | 88 ++--------------
6 files changed, 205 insertions(+), 81 deletions(-)
create mode 100644 drivers/platform/x86/dell-wmi-descriptor.c
create mode 100644 drivers/platform/x86/dell-wmi-descriptor.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 08b96f77f618..659dbeec4191 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4002,6 +4002,11 @@ M: Pali Rohár <pali.rohar@gmail.com>
S: Maintained
F: drivers/platform/x86/dell-wmi.c
+DELL WMI DESCRIPTOR DRIVER
+M: Mario Limonciello <mario.limonciello@dell.com>
+S: Maintained
+F: drivers/platform/x86/dell-wmi-descriptor.c
+
DELTA ST MEDIA DRIVER
M: Hugues Fruchet <hugues.fruchet@st.com>
L: linux-media@vger.kernel.org
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 1f7959ff055c..bc347c326d87 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -121,6 +121,7 @@ config DELL_WMI
depends on DMI
depends on INPUT
depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on DELL_WMI_DESCRIPTOR
select DELL_SMBIOS
select INPUT_SPARSEKMAP
---help---
@@ -129,6 +130,17 @@ config DELL_WMI
To compile this driver as a module, choose M here: the module will
be called dell-wmi.
+config DELL_WMI_DESCRIPTOR
+ tristate "Dell WMI descriptor"
+ depends on ACPI_WMI
+ default ACPI_WMI
+ ---help---
+ Say Y here if you want to be able to read the format of WMI
+ communications on some Dell laptops, desktops and IoT gateways.
+
+ To compile this driver as a module, choose M here: the module will
+ be called dell-wmi-descriptor.
+
config DELL_WMI_AIO
tristate "WMI Hotkeys for Dell All-In-One series"
depends on ACPI_WMI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2b315d0df3b7..8636f5d3424f 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
+obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c
new file mode 100644
index 000000000000..5c2bdda11916
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-descriptor.c
@@ -0,0 +1,162 @@
+/*
+ * Dell WMI descriptor driver
+ *
+ * Copyright (C) 2017 Dell Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+#include "dell-wmi-descriptor.h"
+
+#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
+
+struct descriptor_priv {
+ struct list_head list;
+ u32 interface_version;
+ u32 size;
+};
+static LIST_HEAD(wmi_list);
+
+bool dell_wmi_get_interface_version(u32 *version)
+{
+ struct descriptor_priv *priv;
+
+ priv = list_first_entry_or_null(&wmi_list,
+ struct descriptor_priv,
+ list);
+ if (!priv)
+ return false;
+ *version = priv->interface_version;
+ return true;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
+
+bool dell_wmi_get_size(u32 *size)
+{
+ struct descriptor_priv *priv;
+
+ priv = list_first_entry_or_null(&wmi_list,
+ struct descriptor_priv,
+ list);
+ if (!priv)
+ return false;
+ *size = priv->size;
+ return true;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_get_size);
+
+/*
+ * Descriptor buffer is 128 byte long and contains:
+ *
+ * Name Offset Length Value
+ * Vendor Signature 0 4 "DELL"
+ * Object Signature 4 4 " WMI"
+ * WMI Interface Version 8 4 <version>
+ * WMI buffer length 12 4 4096 or 32768
+ */
+static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
+{
+ union acpi_object *obj = NULL;
+ struct descriptor_priv *priv;
+ u32 *buffer;
+ int ret;
+
+ obj = wmidev_block_query(wdev, 0);
+ if (!obj) {
+ dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Although it's not technically a failure, this would lead to
+ * unexpected behavior
+ */
+ if (obj->buffer.length != 128) {
+ dev_err(&wdev->dev,
+ "Dell descriptor buffer has unexpected length (%d)\n",
+ obj->buffer.length);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ buffer = (u32 *)obj->buffer.pointer;
+
+ if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
+ dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
+ buffer);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (buffer[2] != 0 && buffer[2] != 1)
+ dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
+ buffer[2]);
+
+ if (buffer[3] != 4096 && buffer[3] != 32768)
+ dev_warn(&wdev->dev, "Dell descriptor buffer has unexpected buffer length (%u)\n",
+ buffer[3]);
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
+ GFP_KERNEL);
+
+ priv->interface_version = buffer[2];
+ priv->size = buffer[3];
+ ret = 0;
+ dev_set_drvdata(&wdev->dev, priv);
+ list_add_tail(&priv->list, &wmi_list);
+
+ /* remove me */
+ dev_info(&wdev->dev, "Detected Dell WMI interface version %u and buffer size %u\n",
+ priv->interface_version, priv->size);
+
+out:
+ kfree(obj);
+ return ret;
+}
+
+static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
+{
+ struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ list_del(&priv->list);
+ return 0;
+}
+
+static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
+ { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
+ { },
+};
+
+static struct wmi_driver dell_wmi_descriptor_driver = {
+ .driver = {
+ .name = "dell-wmi-descriptor",
+ },
+ .probe = dell_wmi_descriptor_probe,
+ .remove = dell_wmi_descriptor_remove,
+ .id_table = dell_wmi_descriptor_id_table,
+};
+
+module_wmi_driver(dell_wmi_descriptor_driver);
+
+MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell WMI descriptor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-wmi-descriptor.h b/drivers/platform/x86/dell-wmi-descriptor.h
new file mode 100644
index 000000000000..3e652c6da034
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-descriptor.h
@@ -0,0 +1,18 @@
+/*
+ *
+ * Copyright (c) 2017 Dell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DELL_WMI_DESCRIPTOR_H_
+#define _DELL_WMI_DESCRIPTOR_H_
+
+#include <linux/wmi.h>
+
+bool dell_wmi_get_interface_version(u32 *version);
+bool dell_wmi_get_size(u32 *size);
+
+#endif
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 1366bccdf275..90d7e8e55e9b 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -39,6 +39,7 @@
#include <linux/wmi.h>
#include <acpi/video.h>
#include "dell-smbios.h"
+#include "dell-wmi-descriptor.h"
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
@@ -46,7 +47,6 @@ MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
-#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
static bool wmi_requires_smbios_request;
@@ -54,7 +54,6 @@ MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
struct dell_wmi_priv {
struct input_dev *input_dev;
- u32 interface_version;
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -347,9 +346,9 @@ static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
static void dell_wmi_notify(struct wmi_device *wdev,
union acpi_object *obj)
{
- struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
u16 *buffer_entry, *buffer_end;
acpi_size buffer_size;
+ u32 interface_version;
int len, i;
if (obj->type != ACPI_TYPE_BUFFER) {
@@ -376,7 +375,11 @@ static void dell_wmi_notify(struct wmi_device *wdev,
* So to prevent reading garbage from buffer we will process only first
* one event on devices with WMI interface version 0.
*/
- if (priv->interface_version == 0 && buffer_entry < buffer_end)
+ if (!dell_wmi_get_interface_version(&interface_version)) {
+ pr_warn("WMI descriptor driver not ready or unavailable");
+ return;
+ }
+ if (interface_version == 0 && buffer_entry < buffer_end)
if (buffer_end > buffer_entry + buffer_entry[0] + 1)
buffer_end = buffer_entry + buffer_entry[0] + 1;
@@ -617,78 +620,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
input_unregister_device(priv->input_dev);
}
-/*
- * Descriptor buffer is 128 byte long and contains:
- *
- * Name Offset Length Value
- * Vendor Signature 0 4 "DELL"
- * Object Signature 4 4 " WMI"
- * WMI Interface Version 8 4 <version>
- * WMI buffer length 12 4 4096 or 32768
- */
-static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
-{
- struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
- union acpi_object *obj = NULL;
- struct wmi_device *desc_dev;
- u32 *buffer;
- int ret;
-
- desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
- if (!desc_dev) {
- dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
- return -ENODEV;
- }
-
- obj = wmidev_block_query(desc_dev, 0);
- if (!obj) {
- dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
- ret = -EIO;
- goto out;
- }
-
- if (obj->type != ACPI_TYPE_BUFFER) {
- dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
- ret = -EINVAL;
- goto out;
- }
-
- if (obj->buffer.length != 128) {
- dev_err(&wdev->dev,
- "Dell descriptor buffer has invalid length (%d)\n",
- obj->buffer.length);
- ret = -EINVAL;
- goto out;
- }
-
- buffer = (u32 *)obj->buffer.pointer;
-
- if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
- dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
- buffer);
- ret = -EINVAL;
- goto out;
- }
- if (buffer[2] != 0 && buffer[2] != 1)
- dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
- buffer[2]);
-
- if (desc_buffer[3] != 4096 && desc_buffer[3] != 32768)
- dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%u)\n",
- buffer[3]);
-
- priv->interface_version = buffer[2];
- ret = 0;
-
- dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
- priv->interface_version);
-
-out:
- kfree(obj);
- put_device(&desc_dev->dev);
- return ret;
-}
-
/*
* According to Dell SMBIOS documentation:
*
@@ -724,7 +655,6 @@ static int dell_wmi_events_set_enabled(bool enable)
static int dell_wmi_probe(struct wmi_device *wdev)
{
struct dell_wmi_priv *priv;
- int err;
priv = devm_kzalloc(
&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
@@ -732,10 +662,6 @@ static int dell_wmi_probe(struct wmi_device *wdev)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, priv);
- err = dell_wmi_check_descriptor_buffer(wdev);
- if (err)
- return err;
-
return dell_wmi_input_setup(wdev);
}
--
2.14.1
^ permalink raw reply related
* [PATCH v4 04/14] platform/x86: dell-wmi: increase severity of some failures
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
In-Reply-To: <cover.1507156392.git.mario.limonciello@dell.com>
There is a lot of error checking in place for the format of the WMI
descriptor buffer, but some of the potentially raised issues should
be considered critical failures.
If the buffer size or header don't match, this is a good indication
that the buffer format changed in a way that the rest of the data
should not be relied upon.
For the remaining data set vectors, continue to notate a warning
in undefined results, but as those are fields that the descriptor
intended to refer to other applications, don't fail if they're new
values.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
---
drivers/platform/x86/dell-wmi.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 4807ec4a2e8f..1366bccdf275 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -657,18 +657,18 @@ static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
dev_err(&wdev->dev,
"Dell descriptor buffer has invalid length (%d)\n",
obj->buffer.length);
- if (obj->buffer.length < 16) {
- ret = -EINVAL;
- goto out;
- }
+ ret = -EINVAL;
+ goto out;
}
buffer = (u32 *)obj->buffer.pointer;
- if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0)
- dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
+ if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
+ dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
buffer);
-
+ ret = -EINVAL;
+ goto out;
+ }
if (buffer[2] != 0 && buffer[2] != 1)
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n",
buffer[2]);
--
2.14.1
^ permalink raw reply related
* [PATCH v4 00/14] Introduce support for Dell SMBIOS over WMI
From: Mario Limonciello @ 2017-10-04 22:48 UTC (permalink / raw)
To: dvhart, Andy Shevchenko
Cc: LKML, platform-driver-x86, Andy Lutomirski, quasisec, pali.rohar,
rjw, mjg59, hch, Greg KH, Mario Limonciello
The existing way that the dell-smbios helper module and associated
other drivers (dell-laptop, dell-wmi) communicate with the platform
really isn't secure. It requires creating a buffer in physical
DMA32 memory space and passing that to the platform via SMM.
Since the platform got a physical memory pointer, you've just got
to trust that the platform has only modified (and accessed) memory
within that buffer.
Dell Platform designers recognize this security risk and offer a
safer way to communicate with the platform over ACPI. This is
in turn exposed via a WMI interface to the OS.
When communicating over WMI-ACPI the communication doesn't occur
with physical memory pointers. When the ASL is invoked, the fixed
length ACPI buffer is copied to a small operating region. The ASL
will invoke the SMI, and SMM will only have access to this operating
region. When the ASL returns the buffer is copied back for the OS
to process.
This method of communication should also deprecate the usage of the
dcdbas kernel module and software dependent upon it's interface.
Instead offer a character device interface for communicating with this
ASL method to allow userspace to use instead.
To faciliate that this patch series introduces a generic way for WMI
drivers to be able to create discoverable character devices with
a predictable IOCTL interface through the WMI bus when desired.
Requiring WMI drivers to explicitly ask for this functionality will
act as an effective vendor whitelist to character device creation.
Some of this work is the basis for what will be a proper interpreter
of MOF in the kernel and controls for what drivers will be able to
do with that MOF.
NOTE: This patch is intended to go on top of the 6 patches already in
Darren's review tree.
For convenience the entire series including those is also available here:
https://github.com/dell/linux/tree/wmi-smbios
I also have some PoC application use around this here:
https://github.com/superm1/smbios-wmi-poc
changes between v3 and v4:
* Make Dell WMI notifications driver fail notifications fail
when WMI descriptor driver is unavailable.
* Add a patch to check for Dell OEM string to stop dell-smbios
module from being loaded in unsupported systems manually.
* Split Dell WMI descriptor into it's own driver for others to
query.
* Test the misc BIOS flags table to decide whether to run in WMI
or SMI mode.
* s/dell-wmi-smbios/dell-smbios/ in a few patch titles
* Add missing Suggested-by to a patch from v2 (Sorry Andy S!)
* Adjust cleanup order of wmi character device.
* Fix a remaining reference to /dev/wmi-$driver
* Use get_order to get size for pages
* Split up dell-smbios into 3 drivers:
dell-smbios
-> dell-smbios-smm
-> dell-smbios-wmi
If either of the two dispatcher drivers is unloaded but
the other still works, gracefully fall back to that driver
* Remove unneded open and release on file operations in WMI
driver
* Switch to misc character device in WMI bus.
* Query the size of the calling interface buffer from WMI
descriptor driver.
* Require userspace to use this information to build an
appropriately sized buffer.
* Verify the size of buffer passed from userspace matches
expectation of buffer processed in kernel.
* Add more documentation about the IOCTL API.
* Add in a recommendation from Andy S about outputing tokens
* Add a patch to test for non-functional SMM due to WSMT
* Define macros for IOCTL's through the WMI bus.
* Pass all IOCTL calls for WMI drivers through WMI bus
WMI bus will guarantee instance count matches and prevent
creating too many more IOCTLs.
changes between v2 and v3:
* Drop patches 1-7, they're in Darren's review tree now
* Add missing newline on new Documentation
* Add Reviewed by from Edward O'Callaghan
* Adjust path of character device from /dev/wmi-$driver to
/dev/wmi/$driver
* Store wmi_device pointer rather than using a boolean has_wmi
to indicate driver is running in WMI mode
* Don't guard free_page from freeing NULL (this is OK)
* New patch: add wmidev_evaluate_method to wmi bus as recommended
by Andy L
* Adjust ACPI-WMI interface for this patch change ^
* Add back in sysfs token patch, drop 2nd and 3rd ioctls per discussion
on mailing list.
* On sysfs interface: adjust the delimiter to be tabs
* Drop the rename dell-smbios -> dell-wmi-smbios patch
* Remove/move some unnecessary tests for CONFIG_DCDBAS
* Reword s/platform/SMM/ in the WMI-ACPI patch.
* Update Kconfig to recommend using CONFIG_DCDBAS on old machines.
* Allocate buffer to the same pointer regardless of the struct
assigned to it. Keep track of the buffer size for cleaning up.
* Explain policy around character device creation in commit message
changes between v1 and v2:
* Introduce another patch to sort the includes in wmi.c
* Introduce another patch to cleanup dell_wmi_check_descriptor_buffer
checks.
* Add a commit message to the pr_fmt commit
* Introduce includes to wmi.c in proper location
* Add Reviewed-by to relevant patches from Pali
* Make the WMI introduction patch fallback to legacy SMI
if compiled with CONFIG_DCDBAS
* Separate format of WMI and SMI buffers. WMI buffer supports more
arguments and data.
* Adjust the rename patch for changes to fallback
* Drop sysfs token creation patch
* Adjust WMI descriptor check patch for changes to fallback
* introduce another patch to remove needless includes in dell-smbios.c
* Add token ioctl interface to character device.
- Can query number of tokens
- Can query values in all tokens
* Expose format of all buffers and IOCTL commands to uapi header
* Drop the read interface from character device. It doesn't make
sense with multiple different ioctl methods.
* Default WMI interface to 32k (This would normally be queried via
MOF, but that's not possible yet)
* Create separate buffers for WMI and SMI. If WMI is available,
free the SMI buffer.
* Reorder patches so all fixups come first in the series.
Mario Limonciello (14):
platform/x86: wmi: Add new method wmidev_evaluate_method
platform/x86: dell-wmi: clean up wmi descriptor check
platform/x86: dell-wmi: allow 32k return size in the descriptor
platform/x86: dell-wmi: increase severity of some failures
platform/x86: dell-wmi-descriptor: split WMI descriptor into it's own
driver
platform/x86: wmi: Don't allow drivers to get each other's GUIDs
platform/x86: dell-smbios: only run if proper oem string is detected
platform/x86: dell-smbios: Add a sysfs interface for SMBIOS tokens
platform/x86: dell-smbios: Introduce dispatcher for SMM calls
platform/x86: dell-smbios-smm: test for WSMT
platform/x86: dell-smbios-wmi: Add new WMI dispatcher driver
platform/x86: wmi: create character devices when requested by drivers
platform/x86: dell-smbios-wmi: introduce userspace interface
platform/x86: Kconfig: Set default for dell-smbios to ACPI_WMI
Documentation/ABI/testing/dell-smbios-wmi | 43 +++
.../ABI/testing/sysfs-platform-dell-smbios | 16 ++
.../ABI/testing/sysfs-platform-dell-smbios-wmi | 10 +
MAINTAINERS | 26 ++
drivers/platform/x86/Kconfig | 44 ++-
drivers/platform/x86/Makefile | 3 +
drivers/platform/x86/dell-laptop.c | 191 +++++++------
drivers/platform/x86/dell-smbios-smm.c | 172 +++++++++++
drivers/platform/x86/dell-smbios-smm.h | 22 ++
drivers/platform/x86/dell-smbios-wmi.c | 313 +++++++++++++++++++++
drivers/platform/x86/dell-smbios-wmi.h | 18 ++
drivers/platform/x86/dell-smbios.c | 183 +++++++++---
drivers/platform/x86/dell-smbios.h | 13 +-
drivers/platform/x86/dell-wmi-descriptor.c | 162 +++++++++++
drivers/platform/x86/dell-wmi-descriptor.h | 18 ++
drivers/platform/x86/dell-wmi.c | 96 +------
drivers/platform/x86/wmi.c | 112 ++++++--
include/linux/wmi.h | 12 +-
include/uapi/linux/dell-smbios-wmi.h | 25 ++
include/uapi/linux/wmi.h | 10 +
20 files changed, 1233 insertions(+), 256 deletions(-)
create mode 100644 Documentation/ABI/testing/dell-smbios-wmi
create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-smbios
create mode 100644 Documentation/ABI/testing/sysfs-platform-dell-smbios-wmi
create mode 100644 drivers/platform/x86/dell-smbios-smm.c
create mode 100644 drivers/platform/x86/dell-smbios-smm.h
create mode 100644 drivers/platform/x86/dell-smbios-wmi.c
create mode 100644 drivers/platform/x86/dell-smbios-wmi.h
create mode 100644 drivers/platform/x86/dell-wmi-descriptor.c
create mode 100644 drivers/platform/x86/dell-wmi-descriptor.h
create mode 100644 include/uapi/linux/dell-smbios-wmi.h
create mode 100644 include/uapi/linux/wmi.h
--
2.14.1
^ permalink raw reply
* Re: [PATCH v3 0/7] new mlx4 datapath bypassing ibverbs
From: Thomas Monjalon @ 2017-10-04 22:46 UTC (permalink / raw)
To: Ferruh Yigit, Ophir Munk, Adrien Mazarguil; +Cc: dev, Olga Shern, Matan Azrad
In-Reply-To: <a860a747-415c-2b21-0b44-04568a1c1aba@intel.com>
05/10/2017 00:37, Ferruh Yigit:
> On 10/4/2017 10:48 PM, Ophir Munk wrote:
> > Moti Haimovsky (6):
> > net/mlx4: add simple Tx bypassing ibverbs
> > net/mlx4: get back Rx flow functionality
> > net/mlx4: support multi-segments Tx
> > net/mlx4: get back Tx checksum offloads
> > net/mlx4: get back Rx checksum offloads
> > net/mlx4: add loopback Tx from VF
> >
> > Vasily Philipov (1):
> > net/mlx4: support multi-segments Rx
>
> Adried looks like beat you with a few hours :)
>
> Can you please clarify which set is valid one, thanks.
I think we need a v4 merging both v3.
^ permalink raw reply
* [PATCH] cpufreq: intel_pstate: Clarify effective CPU frequency
From: Srinivas Pandruvada @ 2017-10-04 22:46 UTC (permalink / raw)
To: corbet, rjw; +Cc: linux-doc, linux-kernel, Srinivas Pandruvada
Add section for resultant frequency during GPU workloads.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
Documentation/admin-guide/pm/intel_pstate.rst | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst
index d2b6fda..a7c6308 100644
--- a/Documentation/admin-guide/pm/intel_pstate.rst
+++ b/Documentation/admin-guide/pm/intel_pstate.rst
@@ -482,6 +482,25 @@ on the following rules, regardless of the current operation mode of the driver:
3. The global and per-policy limits can be set independently.
+One important point to note is that all the above limit controls including
+``no_turbo``, only limits the P-state requests by the intel_pstate driver. The
+actual performance delivered by the processor is also influenced by GPU
+(Graphics processing unit) for GPU bound workloads. So, it is possible
+that, while GPU is requesting higher frequencies, the resultant P-state
+(CPU frequency) can be higher than the limits set via sysfs.
+Equivalent limits must, therefore, also be set for the GPU.
+
+An example where an Intel i915 GPU is used:
+
+#cat /sys/kernel/debug/dri/0/i915_ring_freq_table
+
+displays effective CPU frequency for a GPU frequency. To limit
+a CPU frequency, a corresponding GPU frequency limit must also be placed:
+
+/sys/class/drm/card0/gt_max_freq_mhz and
+
+/sys/class/drm/card0/gt_boost_freq_mhz
+
If the `HWP feature is enabled in the processor <Active Mode With HWP_>`_, the
resulting effective values are written into its registers whenever the limits
change in order to request its internal P-state selection logic to always set
--
2.7.5
^ permalink raw reply related
* Re: [PATCH v6 6/6] lib/dlock-list: Provide IRQ-safe APIs
From: Davidlohr Bueso @ 2017-10-04 22:46 UTC (permalink / raw)
To: Waiman Long
Cc: Alexander Viro, Jan Kara, Jeff Layton, J. Bruce Fields, Tejun Heo,
Christoph Lameter, linux-fsdevel, linux-kernel, Ingo Molnar,
Peter Zijlstra, Andi Kleen, Dave Chinner, Boqun Feng
In-Reply-To: <1507152007-28753-7-git-send-email-longman@redhat.com>
On Wed, 04 Oct 2017, Waiman Long wrote:
>To enable the use of dlock-list in an interrupt handler, the following
>new APIs are provided for a irqsafe dlock-list:
>
> - void dlock_list_unlock_irqsafe(struct dlock_list_iter *)
> - void dlock_list_relock_irqsafe(struct dlock_list_iter *)
> - void dlock_list_add_irqsafe(struct dlock_list_node *,
> struct dlock_list_head *);
> - void dlock_lists_add_irqsafe(struct dlock_list_node *,
> struct dlock_list_heads *)
> - void dlock_lists_del_irqsafe(struct dlock_list_node *)
>
>New macros for irqsafe dlock-list:
>
> - dlist_for_each_entry_irqsafe(pos, iter, member)
> - dlist_for_each_entry_safe_irqsafe(pos, n, iter, member)
Instead of adding more calls to the api, could we not just use the
irqsave/restore as part of the regular api?
Thanks,
Davidlohr
^ permalink raw reply
* Re: [PATCH 6/7] xen/tmem: Convert the file common/tmem_xen.c to use typesafe MFN
From: Andrew Cooper @ 2017-10-04 22:46 UTC (permalink / raw)
To: Julien Grall, xen-devel; +Cc: Konrad Rzeszutek Wilk
In-Reply-To: <20171004181526.9405-7-julien.grall@linaro.org>
On 04/10/2017 19:15, Julien Grall wrote:
> @@ -68,16 +72,16 @@ static inline void *cli_get_page(xen_pfn_t cmfn, unsigned long *pcli_mfn,
>
> *pcli_mfn = page_to_mfn(page);
> *pcli_pfp = page;
Newline.
> - return map_domain_page(_mfn(*pcli_mfn));
> + return map_domain_page(*pcli_mfn);
> }
>
> static inline void cli_put_page(void *cli_va, struct page_info *cli_pfp,
> - unsigned long cli_mfn, bool mark_dirty)
> + mfn_t cli_mfn, bool mark_dirty)
> {
> if ( mark_dirty )
> {
> put_page_and_type(cli_pfp);
> - paging_mark_dirty(current->domain, _mfn(cli_mfn));
> + paging_mark_dirty(current->domain, cli_mfn);
> }
> else
> put_page(cli_pfp);
> @@ -165,7 +169,7 @@ int tmem_copy_to_client(xen_pfn_t cmfn, struct page_info *pfp,
> return -EFAULT;
> }
> tmem_mfn = page_to_mfn(pfp);
> - tmem_va = map_domain_page(_mfn(tmem_mfn));
> + tmem_va = map_domain_page(tmem_mfn);
Newline.
Otherwise, Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
> if ( cli_va )
> {
> memcpy(cli_va, tmem_va, PAGE_SIZE);
>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
^ permalink raw reply
* RE: [PATCH 2/3 v2] net: phy: DP83822 initial driver submission
From: Woojung.Huh @ 2017-10-04 22:44 UTC (permalink / raw)
To: dmurphy, andrew, f.fainelli; +Cc: netdev, afd
In-Reply-To: <20171004182031.13794-2-dmurphy@ti.com>
> +static int dp83822_suspend(struct phy_device *phydev)
> +{
> + int value;
> +
> + mutex_lock(&phydev->lock);
> + value = phy_read_mmd(phydev, DP83822_DEVADDR,
> MII_DP83822_WOL_CFG);
> + mutex_unlock(&phydev->lock);
Would we need mutex to access phy_read_mmd()?
phy_read_mmd() has mdio_lock for indirect access.
> + if (!(value & DP83822_WOL_EN))
> + genphy_suspend(phydev);
> +
> + return 0;
> +}
> +
> +static int dp83822_resume(struct phy_device *phydev)
> +{
> + int value;
> +
> + genphy_resume(phydev);
> +
> + mutex_lock(&phydev->lock);
> + value = phy_read_mmd(phydev, DP83822_DEVADDR,
> MII_DP83822_WOL_CFG);
> +
> + phy_write_mmd(phydev, DP83822_DEVADDR,
> MII_DP83822_WOL_CFG, value |
> + DP83822_WOL_CLR_INDICATION);
> +
> + mutex_unlock(&phydev->lock);
Same here.
Woojung
^ permalink raw reply
* Re: [PATCH v7 0/8] net/i40e: GPT-C and GTP-U enabling
From: Ferruh Yigit @ 2017-10-04 22:43 UTC (permalink / raw)
To: Beilei Xing, jingjing.wu; +Cc: andrey.chilikin, dev
In-Reply-To: <1506700252-34949-1-git-send-email-beilei.xing@intel.com>
On 9/29/2017 4:50 PM, Beilei Xing wrote:
> This patch set enables RSS/FDIR/cloud filter for GPT-C and GTP-U.
> It depends on Kirill's patch:
> http://www.dpdk.org/dev/patchwork/patch/29325/
>
> v7 changes:
> - Distinguish GTP-C request and response message in mbuf description.
> - Clarify GTP-C response message is not supported.
> - Version_type 0x30 is invalid for GTP-C, replace with 0x32.
> - Refine metadata parsing function.
> - Rework for checking fdir programming status.
>
> v6 changes:
> - Reword description of GTP item and GTP structure, mainly support
> GTPv1, not include GTPv0 and GTPv2.
>
> v5 changes:
> - Fix code style.
> - Reword commit log.
>
> v4 changes:
> - Refine fdir related code.
> - Rework profile metadata parsing function.
> - Fix code style.
>
> v3 changes:
> - Rework implementation to support the new profile.
> - Add GTPC and GTPU tunnel type in software packet type parser.
> - Update ptype info when loading profile.
> - Fix bug of updating pctype info.
>
>
> v2 changes:
> - Enable RSS/FDIR/cloud filter dinamicly by checking profile
> - Add GTPC and GTPU items to distinguish rule for GTP-C or GTP-U
> - Rework FDIR/cloud filter enabling function
>
> Beilei Xing (8):
> mbuf: support GTP in software packet type parser
> net/i40e: update ptype and pctype info
> net/i40e: support RSS for new pctype
> ethdev: add GTP items to support flow API
> net/i40e: finish integration FDIR with generic flow API
> net/i40e: add FDIR support for GTP-C and GTP-U
> net/i40e: add cloud filter parsing function for GTP
> net/i40e: enable cloud filter for GTP-C and GTP-U
Series Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
I don't know about GTP internals , but set passes from my scripts.
But there was a merge conflict (on top of master tree) on patch 3, it is
easy to resolve but I wonder if there is a dependency not mentioned? If
not can you please double check the patch.
^ permalink raw reply
* Re: [PATCH 5/7] xen/xenoprof: Convert the file to use typesafe MFN
From: Andrew Cooper @ 2017-10-04 22:43 UTC (permalink / raw)
To: Julien Grall, xen-devel
Cc: Stefano Stabellini, Wei Liu, Konrad Rzeszutek Wilk, George Dunlap,
Ian Jackson, Tim Deegan, Jan Beulich
In-Reply-To: <20171004181526.9405-6-julien.grall@linaro.org>
On 04/10/2017 19:15, Julien Grall wrote:
> @@ -134,25 +140,26 @@ static void xenoprof_reset_buf(struct domain *d)
> }
>
> static int
> -share_xenoprof_page_with_guest(struct domain *d, unsigned long mfn, int npages)
> +share_xenoprof_page_with_guest(struct domain *d, mfn_t mfn, int npages)
> {
> int i;
>
> /* Check if previous page owner has released the page. */
> for ( i = 0; i < npages; i++ )
> {
> - struct page_info *page = mfn_to_page(mfn + i);
> + struct page_info *page = mfn_to_page(mfn_add(mfn, i));
A newline would be nice here...
> if ( (page->count_info & (PGC_allocated|PGC_count_mask)) != 0 )
> {
> printk(XENLOG_G_INFO "dom%d mfn %#lx page->count_info %#lx\n",
> - d->domain_id, mfn + i, page->count_info);
> + d->domain_id, mfn_x(mfn_add(mfn, i)), page->count_info);
> return -EBUSY;
> }
> page_set_owner(page, NULL);
> }
>
> for ( i = 0; i < npages; i++ )
> - share_xen_page_with_guest(mfn_to_page(mfn + i), d, XENSHARE_writable);
> + share_xen_page_with_guest(mfn_to_page(mfn_add(mfn, i)),
> + d, XENSHARE_writable);
>
> return 0;
> }
> @@ -161,11 +168,11 @@ static void
> unshare_xenoprof_page_with_guest(struct xenoprof *x)
> {
> int i, npages = x->npages;
> - unsigned long mfn = virt_to_mfn(x->rawbuf);
> + mfn_t mfn = virt_to_mfn(x->rawbuf);
>
> for ( i = 0; i < npages; i++ )
> {
> - struct page_info *page = mfn_to_page(mfn + i);
> + struct page_info *page = mfn_to_page(mfn_add(mfn, i));
... and here. This can easily be fixed on commit, so Reviewed-by:
Andrew Cooper <andrew.cooper3@citrix.com>
> BUG_ON(page_get_owner(page) != current->domain);
> if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
> put_page(page);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
^ permalink raw reply
* Re: Kernel 4.13 is broken on m68k
From: Andreas Schwab @ 2017-10-04 22:43 UTC (permalink / raw)
To: John Paul Adrian Glaubitz; +Cc: Linux/m68k, Debian m68k
In-Reply-To: <a10e5dde-b3a6-a525-d403-154c16e60b35@physik.fu-berlin.de>
On Okt 04 2017, John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> wrote:
> Ok, switching just the initrd to the one from 4.13 while keeping the 4.12 kernel
> image results in the same crash. Maybe the glibc is broken?
It's busybox. Try setting BUSYBOX=n in
/etc/initramfs-tools/initramfs.conf (or deinstall busybox).
$ busybox ash -c 'echo ${x}'
ash: syntax error: missing '}'
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply
* Re: [PATCH v2 0/2] ethdev: add support for raw flow type for flow director
From: Thomas Monjalon @ 2017-10-04 22:42 UTC (permalink / raw)
To: Ferruh Yigit, Kirill Rybalchenko
Cc: dev, andrey.chilikin, beilei.xing, jingjing.wu
In-Reply-To: <acd3ba0d-b813-9c8f-e957-81228abd4d12@intel.com>
04/10/2017 21:47, Ferruh Yigit:
> On 10/4/2017 6:56 PM, Thomas Monjalon wrote:
> > 04/10/2017 19:44, Ferruh Yigit:
> >> On 10/4/2017 5:57 PM, Thomas Monjalon wrote:
> >>> 03/10/2017 21:02, Ferruh Yigit:
> >>>> On 9/20/2017 9:42 AM, Kirill Rybalchenko wrote:
> >>>>> For complex packets use raw flow type with pre-constructed packet buffer
> >>>>> instead of creating a packet internally in PMD.
> >>>
> >>> Sorry for not catching this series before.
> >>>
> >>> As it has been said several times on this mailing list,
> >>> the flow director API is deprecated.
> >>> I think everybody here knows that it is going to be replaced
> >>> by rte_flow.
> >>>
> >>> That's why it does not make sense to extend flow director.
> >>> We are not going to update PMDs to support a new type
> >>> of legacy flow director.
> >>> Please focus your efforts on rte_flow.
> >>
> >> As far as I can see this is not to to extend flow director. But driver
> >> uses this struct and adding a new feature into driver requires update in
> >> this struct.
> >>
> >> I guess idea was for new filter functionalities PMD should use rte_flow,
> >> that is the new shiny method we have, I see the point here. But I don't
> >> see the point of making these structs under use completely immutable.
> >
> > I don't know what is RTE_ETH_FLOW_RAW.
> > Let's start by explaining it, how it is used by users,
> > and why this struct is needed.
>
> Let me answer as much as I get from patches, if something is missing or
> wrong Kirill needs to correct it.
>
> Driver (i40e) works with static pre-defined flow and pctypes. But new
> feature DDP lets loading custom profiles and work with custom and
> perhaps not yet defined flow and pctypes. So there are a few other
> patches to make driver more dynamic.
>
> For this case I40E_VALID_FLOW() fails with custom flow types, and
> RTE_ETH_FLOW_RAW used as kind of (void *) to be able to work with new
> dynamic types.
So the application could use a new flow type?
If an application has to be modified for this, it can use rte_flow.
Again, no new feature in legacy flow director.
^ permalink raw reply
* Re: [PATCH 4/7] xen/kimage: Remove defined but unused variables
From: Andrew Cooper @ 2017-10-04 22:42 UTC (permalink / raw)
To: Julien Grall, xen-devel
In-Reply-To: <20171004181526.9405-5-julien.grall@linaro.org>
On 04/10/2017 19:15, Julien Grall wrote:
> In the function kimage_alloc_normal_control_page, the variables mfn and
> emfn are defined but not used. Remove them.
>
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
Oops.
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
^ permalink raw reply
* Re: [PATCH 0/2] [v5] pinctrl: qcom: add support for sparse GPIOs
From: Timur Tabi @ 2017-10-04 22:41 UTC (permalink / raw)
To: Stephen Boyd
Cc: Linus Walleij, Andy Gross, David Brown, anjiandi, Bjorn Andersson,
linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-arm-msm@vger.kernel.org, thierry.reding@gmail.com,
Mika Westerberg, Andy Shevchenko
In-Reply-To: <20171004215023.GA457@codeaurora.org>
On 10/04/2017 04:50 PM, Stephen Boyd wrote:
>> At the time I wrote that patch, the ACPI tables exposed all of the
>> GPIOs, even the ones it didn't care about. The new ACPI tables list
>> only specific GPIOs, and so we no longer need to blindly read the
>> direction of all GPIOs.
>>
>
> Do you avoid this problem on new ACPI tables because only pins
> that are able to be read are exposed?
Yes. A recent firmware update enabled the "XPU" block which is being
programmed with a select subset of individual GPIOs. On our silicon,
each TLMM GPIO is in a separate 64k page, and so the XPU can block any
individual GPIO. Any attempt to touch those registers causes an XPU
violation which takes the whole system down.
>>> This is basically a revert of commit 72d320006177 ("gpio: set up
>>> initial state from .get_direction()").
>>
>> I would be in favor of either reverting that patch, or moving the
>> code into gpiochip_add_pin_range().
>
> If it's in gpiochip_add_pin_range() would we still read the
> hardware when creating the pin ranges?
I presume so. The idea is that pinctrl-qdf2xxx/pinctrl-msm only submit
pin ranges that are present in the ACPI tables.
> I don't want to have to> describe pin ranges of "valid" pins that
won't cause the system
> to blow up if we touch them, because those pins are never used by
> Linux so reading them is not useful.
Well, that's exactly what I'm trying to do with current patch set :-)
It seems the most logical approach to me. I don't understand the
dislike for it. What else are pin ranges for, other than to specify
ranges of pins that can be accessed?
Another alternative was to enumerate all of the GPIOs starting from 0.
So the first GPIO in ACPI would be gpio0, regardless of what gpio number
it actually was. E.g. GPIO 37 would appear as gpio0, GPIO 38 would
appear as gpio1, and so on. That also worked, but it meant that
customers would need to figure out which GPIO that "gpio0" actually
pointed to. That was not acceptable, so I dropped it.
I'm at a loss on how else to do it. I think a gpio_chip.available
callback is far less elegant than define pin ranges. There is no chance
that unavailable GPIOs can be accessed because the physical addresses
are not in the msm_pingroup array. That is, groups[0].ctrl_reg == 0,
not 0xFF02010000.
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH 0/2] [v5] pinctrl: qcom: add support for sparse GPIOs
From: Timur Tabi @ 2017-10-04 22:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171004215023.GA457@codeaurora.org>
On 10/04/2017 04:50 PM, Stephen Boyd wrote:
>> At the time I wrote that patch, the ACPI tables exposed all of the
>> GPIOs, even the ones it didn't care about. The new ACPI tables list
>> only specific GPIOs, and so we no longer need to blindly read the
>> direction of all GPIOs.
>>
>
> Do you avoid this problem on new ACPI tables because only pins
> that are able to be read are exposed?
Yes. A recent firmware update enabled the "XPU" block which is being
programmed with a select subset of individual GPIOs. On our silicon,
each TLMM GPIO is in a separate 64k page, and so the XPU can block any
individual GPIO. Any attempt to touch those registers causes an XPU
violation which takes the whole system down.
>>> This is basically a revert of commit 72d320006177 ("gpio: set up
>>> initial state from .get_direction()").
>>
>> I would be in favor of either reverting that patch, or moving the
>> code into gpiochip_add_pin_range().
>
> If it's in gpiochip_add_pin_range() would we still read the
> hardware when creating the pin ranges?
I presume so. The idea is that pinctrl-qdf2xxx/pinctrl-msm only submit
pin ranges that are present in the ACPI tables.
> I don't want to have to> describe pin ranges of "valid" pins that
won't cause the system
> to blow up if we touch them, because those pins are never used by
> Linux so reading them is not useful.
Well, that's exactly what I'm trying to do with current patch set :-)
It seems the most logical approach to me. I don't understand the
dislike for it. What else are pin ranges for, other than to specify
ranges of pins that can be accessed?
Another alternative was to enumerate all of the GPIOs starting from 0.
So the first GPIO in ACPI would be gpio0, regardless of what gpio number
it actually was. E.g. GPIO 37 would appear as gpio0, GPIO 38 would
appear as gpio1, and so on. That also worked, but it meant that
customers would need to figure out which GPIO that "gpio0" actually
pointed to. That was not acceptable, so I dropped it.
I'm at a loss on how else to do it. I think a gpio_chip.available
callback is far less elegant than define pin ranges. There is no chance
that unavailable GPIOs can be accessed because the physical addresses
are not in the msm_pingroup array. That is, groups[0].ctrl_reg == 0,
not 0xFF02010000.
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.