aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMario Limonciello <mario.limonciello@dell.com>2017-11-01 15:25:36 -0400
committerDarren Hart (VMware) <dvhart@infradead.org>2017-11-03 19:34:00 -0400
commitf2645fa317b8905b8934f06a0601d5b7fa66aba0 (patch)
tree67e5c5b646383869298a60dd0c6b1e44c345399e
parent44b6b7661132b1b0e5fd3147ded66f1e4a817ca9 (diff)
platform/x86: dell-smbios-wmi: introduce userspace interface
It's important for the driver to provide a R/W ioctl to ensure that two competing userspace processes don't race to provide or read each others data. This userspace character device will be used to perform SMBIOS calls from any applications. It provides an ioctl that will allow passing the WMI calling interface buffer between userspace and kernel space. This character device is intended to deprecate the dcdbas kernel module and the interface that it provides to userspace. To perform an SMBIOS IOCTL call using the character device userspace will perform a read() on the the character device. The WMI bus will provide a u64 variable containing the necessary size of the IOCTL buffer. The API for interacting with this interface is defined in documentation as well as the WMI uapi header provides the format of the structures. Not all userspace requests will be accepted. The dell-smbios filtering functionality will be used to prevent access to certain tokens and calls. All whitelisted commands and tokens are now shared out to userspace so applications don't need to define them in their own headers. 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--Documentation/ABI/testing/dell-smbios-wmi41
-rw-r--r--drivers/platform/x86/dell-smbios-wmi.c54
-rw-r--r--drivers/platform/x86/dell-smbios.h32
-rw-r--r--include/uapi/linux/wmi.h47
4 files changed, 133 insertions, 41 deletions
diff --git a/Documentation/ABI/testing/dell-smbios-wmi b/Documentation/ABI/testing/dell-smbios-wmi
new file mode 100644
index 000000000000..fc919ce16008
--- /dev/null
+++ b/Documentation/ABI/testing/dell-smbios-wmi
@@ -0,0 +1,41 @@
1What: /dev/wmi/dell-smbios
2Date: November 2017
3KernelVersion: 4.15
4Contact: "Mario Limonciello" <mario.limonciello@dell.com>
5Description:
6 Perform SMBIOS calls on supported Dell machines.
7 through the Dell ACPI-WMI interface.
8
9 IOCTL's and buffer formats are defined in:
10 <uapi/linux/wmi.h>
11
12 1) To perform an SMBIOS call from userspace, you'll need to
13 first determine the minimum size of the calling interface
14 buffer for your machine.
15 Platforms that contain larger buffers can return larger
16 objects from the system firmware.
17 Commonly this size is either 4k or 32k.
18
19 To determine the size of the buffer read() a u64 dword from
20 the WMI character device /dev/wmi/dell-smbios.
21
22 2) After you've determined the minimum size of the calling
23 interface buffer, you can allocate a structure that represents
24 the structure documented above.
25
26 3) In the 'length' object store the size of the buffer you
27 determined above and allocated.
28
29 4) In this buffer object, prepare as necessary for the SMBIOS
30 call you're interested in. Typically SMBIOS buffers have
31 "class", "select", and "input" defined to values that coincide
32 with the data you are interested in.
33 Documenting class/select/input values is outside of the scope
34 of this documentation. Check with the libsmbios project for
35 further documentation on these values.
36
37 6) Run the call by using ioctl() as described in the header.
38
39 7) The output will be returned in the buffer object.
40
41 8) Be sure to free up your allocated object.
diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c
index b31f457e58c3..35c13815b24c 100644
--- a/drivers/platform/x86/dell-smbios-wmi.c
+++ b/drivers/platform/x86/dell-smbios-wmi.c
@@ -30,17 +30,6 @@ struct misc_bios_flags_structure {
30 30
31#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" 31#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
32 32
33struct dell_wmi_extensions {
34 __u32 argattrib;
35 __u32 blength;
36 __u8 data[];
37} __packed;
38
39struct dell_wmi_smbios_buffer {
40 struct calling_interface_buffer std;
41 struct dell_wmi_extensions ext;
42} __packed;
43
44struct wmi_smbios_priv { 33struct wmi_smbios_priv {
45 struct dell_wmi_smbios_buffer *buf; 34 struct dell_wmi_smbios_buffer *buf;
46 struct list_head list; 35 struct list_head list;
@@ -117,6 +106,42 @@ int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
117 return ret; 106 return ret;
118} 107}
119 108
109static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
110 struct wmi_ioctl_buffer *arg)
111{
112 struct wmi_smbios_priv *priv;
113 int ret = 0;
114
115 switch (cmd) {
116 case DELL_WMI_SMBIOS_CMD:
117 mutex_lock(&call_mutex);
118 priv = dev_get_drvdata(&wdev->dev);
119 if (!priv) {
120 ret = -ENODEV;
121 goto fail_smbios_cmd;
122 }
123 memcpy(priv->buf, arg, priv->req_buf_size);
124 if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
125 dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
126 priv->buf->std.cmd_class,
127 priv->buf->std.cmd_select,
128 priv->buf->std.input[0]);
129 ret = -EFAULT;
130 goto fail_smbios_cmd;
131 }
132 ret = run_smbios_call(priv->wdev);
133 if (ret)
134 goto fail_smbios_cmd;
135 memcpy(arg, priv->buf, priv->req_buf_size);
136fail_smbios_cmd:
137 mutex_unlock(&call_mutex);
138 break;
139 default:
140 ret = -ENOIOCTLCMD;
141 }
142 return ret;
143}
144
120static int dell_smbios_wmi_probe(struct wmi_device *wdev) 145static int dell_smbios_wmi_probe(struct wmi_device *wdev)
121{ 146{
122 struct wmi_smbios_priv *priv; 147 struct wmi_smbios_priv *priv;
@@ -135,6 +160,12 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev)
135 if (!dell_wmi_get_size(&priv->req_buf_size)) 160 if (!dell_wmi_get_size(&priv->req_buf_size))
136 return -EPROBE_DEFER; 161 return -EPROBE_DEFER;
137 162
163 /* add in the length object we will use internally with ioctl */
164 priv->req_buf_size += sizeof(u64);
165 ret = set_required_buffer_size(wdev, priv->req_buf_size);
166 if (ret)
167 return ret;
168
138 count = get_order(priv->req_buf_size); 169 count = get_order(priv->req_buf_size);
139 priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); 170 priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
140 if (!priv->buf) 171 if (!priv->buf)
@@ -210,6 +241,7 @@ static struct wmi_driver dell_smbios_wmi_driver = {
210 .probe = dell_smbios_wmi_probe, 241 .probe = dell_smbios_wmi_probe,
211 .remove = dell_smbios_wmi_remove, 242 .remove = dell_smbios_wmi_remove,
212 .id_table = dell_smbios_wmi_id_table, 243 .id_table = dell_smbios_wmi_id_table,
244 .filter_callback = dell_smbios_wmi_filter,
213}; 245};
214 246
215static int __init init_dell_smbios_wmi(void) 247static int __init init_dell_smbios_wmi(void)
diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h
index 91e8004d48ba..138d478d9adc 100644
--- a/drivers/platform/x86/dell-smbios.h
+++ b/drivers/platform/x86/dell-smbios.h
@@ -17,23 +17,11 @@
17#define _DELL_SMBIOS_H_ 17#define _DELL_SMBIOS_H_
18 18
19#include <linux/device.h> 19#include <linux/device.h>
20#include <uapi/linux/wmi.h>
20 21
21/* Classes and selects used in kernel drivers */ 22/* Classes and selects used only in kernel drivers */
22#define CLASS_TOKEN_READ 0
23#define CLASS_TOKEN_WRITE 1
24#define SELECT_TOKEN_STD 0
25#define SELECT_TOKEN_BAT 1
26#define SELECT_TOKEN_AC 2
27#define CLASS_KBD_BACKLIGHT 4 23#define CLASS_KBD_BACKLIGHT 4
28#define SELECT_KBD_BACKLIGHT 11 24#define SELECT_KBD_BACKLIGHT 11
29#define CLASS_FLASH_INTERFACE 7
30#define SELECT_FLASH_INTERFACE 3
31#define CLASS_ADMIN_PROP 10
32#define SELECT_ADMIN_PROP 3
33#define CLASS_INFO 17
34#define SELECT_RFKILL 11
35#define SELECT_APP_REGISTRATION 3
36#define SELECT_DOCK 22
37 25
38/* Tokens used in kernel drivers, any of these 26/* Tokens used in kernel drivers, any of these
39 * should be filtered from userspace access 27 * should be filtered from userspace access
@@ -50,24 +38,8 @@
50#define GLOBAL_MIC_MUTE_ENABLE 0x0364 38#define GLOBAL_MIC_MUTE_ENABLE 0x0364
51#define GLOBAL_MIC_MUTE_DISABLE 0x0365 39#define GLOBAL_MIC_MUTE_DISABLE 0x0365
52 40
53/* tokens whitelisted to userspace use */
54#define CAPSULE_EN_TOKEN 0x0461
55#define CAPSULE_DIS_TOKEN 0x0462
56#define WSMT_EN_TOKEN 0x04EC
57#define WSMT_DIS_TOKEN 0x04ED
58
59struct notifier_block; 41struct notifier_block;
60 42
61/* This structure will be modified by the firmware when we enter
62 * system management mode, hence the volatiles */
63
64struct calling_interface_buffer {
65 u16 cmd_class;
66 u16 cmd_select;
67 volatile u32 input[4];
68 volatile u32 output[4];
69} __packed;
70
71struct calling_interface_token { 43struct calling_interface_token {
72 u16 tokenID; 44 u16 tokenID;
73 u16 location; 45 u16 location;
diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h
index 7e52350ac9b3..7a92e9e3d1c0 100644
--- a/include/uapi/linux/wmi.h
+++ b/include/uapi/linux/wmi.h
@@ -10,6 +10,7 @@
10#ifndef _UAPI_LINUX_WMI_H 10#ifndef _UAPI_LINUX_WMI_H
11#define _UAPI_LINUX_WMI_H 11#define _UAPI_LINUX_WMI_H
12 12
13#include <linux/ioctl.h>
13#include <linux/types.h> 14#include <linux/types.h>
14 15
15/* WMI bus will filter all WMI vendor driver requests through this IOC */ 16/* WMI bus will filter all WMI vendor driver requests through this IOC */
@@ -23,4 +24,50 @@ struct wmi_ioctl_buffer {
23 __u8 data[]; 24 __u8 data[];
24}; 25};
25 26
27/* This structure may be modified by the firmware when we enter
28 * system management mode through SMM, hence the volatiles
29 */
30struct calling_interface_buffer {
31 __u16 cmd_class;
32 __u16 cmd_select;
33 volatile __u32 input[4];
34 volatile __u32 output[4];
35} __packed;
36
37struct dell_wmi_extensions {
38 __u32 argattrib;
39 __u32 blength;
40 __u8 data[];
41} __packed;
42
43struct dell_wmi_smbios_buffer {
44 __u64 length;
45 struct calling_interface_buffer std;
46 struct dell_wmi_extensions ext;
47} __packed;
48
49/* Whitelisted smbios class/select commands */
50#define CLASS_TOKEN_READ 0
51#define CLASS_TOKEN_WRITE 1
52#define SELECT_TOKEN_STD 0
53#define SELECT_TOKEN_BAT 1
54#define SELECT_TOKEN_AC 2
55#define CLASS_FLASH_INTERFACE 7
56#define SELECT_FLASH_INTERFACE 3
57#define CLASS_ADMIN_PROP 10
58#define SELECT_ADMIN_PROP 3
59#define CLASS_INFO 17
60#define SELECT_RFKILL 11
61#define SELECT_APP_REGISTRATION 3
62#define SELECT_DOCK 22
63
64/* whitelisted tokens */
65#define CAPSULE_EN_TOKEN 0x0461
66#define CAPSULE_DIS_TOKEN 0x0462
67#define WSMT_EN_TOKEN 0x04EC
68#define WSMT_DIS_TOKEN 0x04ED
69
70/* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */
71#define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer)
72
26#endif 73#endif