summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMario Limonciello <mario.limonciello@dell.com>2017-11-01 15:25:35 -0400
committerDarren Hart (VMware) <dvhart@infradead.org>2017-11-03 19:34:00 -0400
commit44b6b7661132b1b0e5fd3147ded66f1e4a817ca9 (patch)
treeaa54778be562e5b60b8cde0034fb1bfc276870e3
parent1f8543a5d602b816b9b64a62cafd6caae2af4ca6 (diff)
platform/x86: wmi: create userspace interface for drivers
For WMI operations that are only Set or Query readable and writable sysfs attributes created by WMI vendor drivers or the bus driver makes 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 a callback method in the wmi_driver the WMI bus driver will create a character device that maps to that function. This callback method will be responsible for filtering invalid requests and performing the actual call. That character device will correspond to this path: /dev/wmi/$driver Performing read() on this character device will provide the size of the buffer that the character device needs to perform calls. This buffer size can be set by vendor drivers through a new symbol or when MOF parsing is available by the MOF. Performing ioctl() on this character device will be interpretd by the WMI bus driver. It will perform sanity tests for size of data, test them for a valid instance, copy the data from userspace and pass iton to the vendor driver to further process and run. This creates an implicit policy that each driver will only be allowed a single character device. If a module matches multiple GUID's, the wmi_devices will need to be all handled by the same wmi_driver. The WMI vendor drivers will be responsible for managing inappropriate access to this character device and proper locking on data used by it. When a WMI vendor driver is unloaded the WMI bus driver will clean up the character device and any memory allocated for the call. Signed-off-by: Mario Limonciello <mario.limonciello@dell.com> Reviewed-by: Edward O'Callaghan <quasisec@google.com> Signed-off-by: Darren Hart (VMware) <dvhart@infradead.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/platform/x86/wmi.c189
-rw-r--r--include/linux/wmi.h5
-rw-r--r--include/uapi/linux/wmi.h26
4 files changed, 219 insertions, 2 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index aede236d10f1..3af07502220a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -384,6 +384,7 @@ ACPI WMI DRIVER
384L: platform-driver-x86@vger.kernel.org 384L: platform-driver-x86@vger.kernel.org
385S: Orphan 385S: Orphan
386F: drivers/platform/x86/wmi.c 386F: drivers/platform/x86/wmi.c
387F: include/uapi/linux/wmi.h
387 388
388AD1889 ALSA SOUND DRIVER 389AD1889 ALSA SOUND DRIVER
389M: Thibaut Varene <T-Bone@parisc-linux.org> 390M: Thibaut Varene <T-Bone@parisc-linux.org>
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index bcb41c1c7f52..8c31ed4f0e1b 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -38,12 +38,15 @@
38#include <linux/init.h> 38#include <linux/init.h>
39#include <linux/kernel.h> 39#include <linux/kernel.h>
40#include <linux/list.h> 40#include <linux/list.h>
41#include <linux/miscdevice.h>
41#include <linux/module.h> 42#include <linux/module.h>
42#include <linux/platform_device.h> 43#include <linux/platform_device.h>
43#include <linux/slab.h> 44#include <linux/slab.h>
44#include <linux/types.h> 45#include <linux/types.h>
46#include <linux/uaccess.h>
45#include <linux/uuid.h> 47#include <linux/uuid.h>
46#include <linux/wmi.h> 48#include <linux/wmi.h>
49#include <uapi/linux/wmi.h>
47 50
48ACPI_MODULE_NAME("wmi"); 51ACPI_MODULE_NAME("wmi");
49MODULE_AUTHOR("Carlos Corbacho"); 52MODULE_AUTHOR("Carlos Corbacho");
@@ -69,9 +72,12 @@ struct wmi_block {
69 struct wmi_device dev; 72 struct wmi_device dev;
70 struct list_head list; 73 struct list_head list;
71 struct guid_block gblock; 74 struct guid_block gblock;
75 struct miscdevice char_dev;
76 struct mutex char_mutex;
72 struct acpi_device *acpi_device; 77 struct acpi_device *acpi_device;
73 wmi_notify_handler handler; 78 wmi_notify_handler handler;
74 void *handler_data; 79 void *handler_data;
80 u64 req_buf_size;
75 81
76 bool read_takes_no_args; 82 bool read_takes_no_args;
77}; 83};
@@ -188,6 +194,25 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
188/* 194/*
189 * Exported WMI functions 195 * Exported WMI functions
190 */ 196 */
197
198/**
199 * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
200 * @wdev: A wmi bus device from a driver
201 * @instance: Instance index
202 *
203 * Allocates memory needed for buffer, stores the buffer size in that memory
204 */
205int set_required_buffer_size(struct wmi_device *wdev, u64 length)
206{
207 struct wmi_block *wblock;
208
209 wblock = container_of(wdev, struct wmi_block, dev);
210 wblock->req_buf_size = length;
211
212 return 0;
213}
214EXPORT_SYMBOL_GPL(set_required_buffer_size);
215
191/** 216/**
192 * wmi_evaluate_method - Evaluate a WMI method 217 * wmi_evaluate_method - Evaluate a WMI method
193 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 218 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -764,6 +789,111 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
764 789
765 return 0; 790 return 0;
766} 791}
792static int wmi_char_open(struct inode *inode, struct file *filp)
793{
794 const char *driver_name = filp->f_path.dentry->d_iname;
795 struct wmi_block *wblock = NULL;
796 struct wmi_block *next = NULL;
797
798 list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
799 if (!wblock->dev.dev.driver)
800 continue;
801 if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) {
802 filp->private_data = wblock;
803 break;
804 }
805 }
806
807 if (!filp->private_data)
808 return -ENODEV;
809
810 return nonseekable_open(inode, filp);
811}
812
813static ssize_t wmi_char_read(struct file *filp, char __user *buffer,
814 size_t length, loff_t *offset)
815{
816 struct wmi_block *wblock = filp->private_data;
817
818 return simple_read_from_buffer(buffer, length, offset,
819 &wblock->req_buf_size,
820 sizeof(wblock->req_buf_size));
821}
822
823static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
824{
825 struct wmi_ioctl_buffer __user *input =
826 (struct wmi_ioctl_buffer __user *) arg;
827 struct wmi_block *wblock = filp->private_data;
828 struct wmi_ioctl_buffer *buf = NULL;
829 struct wmi_driver *wdriver = NULL;
830 int ret;
831
832 if (_IOC_TYPE(cmd) != WMI_IOC)
833 return -ENOTTY;
834
835 /* make sure we're not calling a higher instance than exists*/
836 if (_IOC_NR(cmd) >= wblock->gblock.instance_count)
837 return -EINVAL;
838
839 mutex_lock(&wblock->char_mutex);
840 buf = wblock->handler_data;
841 if (get_user(buf->length, &input->length)) {
842 dev_dbg(&wblock->dev.dev, "Read length from user failed\n");
843 ret = -EFAULT;
844 goto out_ioctl;
845 }
846 /* if it's too small, abort */
847 if (buf->length < wblock->req_buf_size) {
848 dev_err(&wblock->dev.dev,
849 "Buffer %lld too small, need at least %lld\n",
850 buf->length, wblock->req_buf_size);
851 ret = -EINVAL;
852 goto out_ioctl;
853 }
854 /* if it's too big, warn, driver will only use what is needed */
855 if (buf->length > wblock->req_buf_size)
856 dev_warn(&wblock->dev.dev,
857 "Buffer %lld is bigger than required %lld\n",
858 buf->length, wblock->req_buf_size);
859
860 /* copy the structure from userspace */
861 if (copy_from_user(buf, input, wblock->req_buf_size)) {
862 dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n",
863 wblock->req_buf_size);
864 ret = -EFAULT;
865 goto out_ioctl;
866 }
867
868 /* let the driver do any filtering and do the call */
869 wdriver = container_of(wblock->dev.dev.driver,
870 struct wmi_driver, driver);
871 if (!try_module_get(wdriver->driver.owner))
872 return -EBUSY;
873 ret = wdriver->filter_callback(&wblock->dev, cmd, buf);
874 module_put(wdriver->driver.owner);
875 if (ret)
876 goto out_ioctl;
877
878 /* return the result (only up to our internal buffer size) */
879 if (copy_to_user(input, buf, wblock->req_buf_size)) {
880 dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n",
881 wblock->req_buf_size);
882 ret = -EFAULT;
883 }
884
885out_ioctl:
886 mutex_unlock(&wblock->char_mutex);
887 return ret;
888}
889
890static const struct file_operations wmi_fops = {
891 .owner = THIS_MODULE,
892 .read = wmi_char_read,
893 .open = wmi_char_open,
894 .unlocked_ioctl = wmi_ioctl,
895 .compat_ioctl = wmi_ioctl,
896};
767 897
768static int wmi_dev_probe(struct device *dev) 898static int wmi_dev_probe(struct device *dev)
769{ 899{
@@ -771,16 +901,63 @@ static int wmi_dev_probe(struct device *dev)
771 struct wmi_driver *wdriver = 901 struct wmi_driver *wdriver =
772 container_of(dev->driver, struct wmi_driver, driver); 902 container_of(dev->driver, struct wmi_driver, driver);
773 int ret = 0; 903 int ret = 0;
904 int count;
905 char *buf;
774 906
775 if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 907 if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
776 dev_warn(dev, "failed to enable device -- probing anyway\n"); 908 dev_warn(dev, "failed to enable device -- probing anyway\n");
777 909
778 if (wdriver->probe) { 910 if (wdriver->probe) {
779 ret = wdriver->probe(dev_to_wdev(dev)); 911 ret = wdriver->probe(dev_to_wdev(dev));
780 if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) 912 if (ret != 0)
781 dev_warn(dev, "failed to disable device\n"); 913 goto probe_failure;
782 } 914 }
783 915
916 /* driver wants a character device made */
917 if (wdriver->filter_callback) {
918 /* check that required buffer size declared by driver or MOF */
919 if (!wblock->req_buf_size) {
920 dev_err(&wblock->dev.dev,
921 "Required buffer size not set\n");
922 ret = -EINVAL;
923 goto probe_failure;
924 }
925
926 count = get_order(wblock->req_buf_size);
927 wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL,
928 count);
929 if (!wblock->handler_data) {
930 ret = -ENOMEM;
931 goto probe_failure;
932 }
933
934 buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL);
935 if (!buf) {
936 ret = -ENOMEM;
937 goto probe_string_failure;
938 }
939 sprintf(buf, "wmi/%s", wdriver->driver.name);
940 wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
941 wblock->char_dev.name = buf;
942 wblock->char_dev.fops = &wmi_fops;
943 wblock->char_dev.mode = 0444;
944 ret = misc_register(&wblock->char_dev);
945 if (ret) {
946 dev_warn(dev, "failed to register char dev: %d", ret);
947 ret = -ENOMEM;
948 goto probe_misc_failure;
949 }
950 }
951
952 return 0;
953
954probe_misc_failure:
955 kfree(buf);
956probe_string_failure:
957 kfree(wblock->handler_data);
958probe_failure:
959 if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
960 dev_warn(dev, "failed to disable device\n");
784 return ret; 961 return ret;
785} 962}
786 963
@@ -791,6 +968,13 @@ static int wmi_dev_remove(struct device *dev)
791 container_of(dev->driver, struct wmi_driver, driver); 968 container_of(dev->driver, struct wmi_driver, driver);
792 int ret = 0; 969 int ret = 0;
793 970
971 if (wdriver->filter_callback) {
972 misc_deregister(&wblock->char_dev);
973 kfree(wblock->char_dev.name);
974 free_pages((unsigned long)wblock->handler_data,
975 get_order(wblock->req_buf_size));
976 }
977
794 if (wdriver->remove) 978 if (wdriver->remove)
795 ret = wdriver->remove(dev_to_wdev(dev)); 979 ret = wdriver->remove(dev_to_wdev(dev));
796 980
@@ -847,6 +1031,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
847 1031
848 if (gblock->flags & ACPI_WMI_METHOD) { 1032 if (gblock->flags & ACPI_WMI_METHOD) {
849 wblock->dev.dev.type = &wmi_type_method; 1033 wblock->dev.dev.type = &wmi_type_method;
1034 mutex_init(&wblock->char_mutex);
850 goto out_init; 1035 goto out_init;
851 } 1036 }
852 1037
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index ddee427e0721..4757cb5077e5 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -18,6 +18,7 @@
18 18
19#include <linux/device.h> 19#include <linux/device.h>
20#include <linux/acpi.h> 20#include <linux/acpi.h>
21#include <uapi/linux/wmi.h>
21 22
22struct wmi_device { 23struct wmi_device {
23 struct device dev; 24 struct device dev;
@@ -36,6 +37,8 @@ extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
36extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, 37extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
37 u8 instance); 38 u8 instance);
38 39
40extern int set_required_buffer_size(struct wmi_device *wdev, u64 length);
41
39struct wmi_device_id { 42struct wmi_device_id {
40 const char *guid_string; 43 const char *guid_string;
41}; 44};
@@ -47,6 +50,8 @@ struct wmi_driver {
47 int (*probe)(struct wmi_device *wdev); 50 int (*probe)(struct wmi_device *wdev);
48 int (*remove)(struct wmi_device *wdev); 51 int (*remove)(struct wmi_device *wdev);
49 void (*notify)(struct wmi_device *device, union acpi_object *data); 52 void (*notify)(struct wmi_device *device, union acpi_object *data);
53 long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,
54 struct wmi_ioctl_buffer *arg);
50}; 55};
51 56
52extern int __must_check __wmi_driver_register(struct wmi_driver *driver, 57extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h
new file mode 100644
index 000000000000..7e52350ac9b3
--- /dev/null
+++ b/include/uapi/linux/wmi.h
@@ -0,0 +1,26 @@
1/*
2 * User API methods for ACPI-WMI mapping driver
3 *
4 * Copyright (C) 2017 Dell, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#ifndef _UAPI_LINUX_WMI_H
11#define _UAPI_LINUX_WMI_H
12
13#include <linux/types.h>
14
15/* WMI bus will filter all WMI vendor driver requests through this IOC */
16#define WMI_IOC 'W'
17
18/* All ioctl requests through WMI should declare their size followed by
19 * relevant data objects
20 */
21struct wmi_ioctl_buffer {
22 __u64 length;
23 __u8 data[];
24};
25
26#endif