From patchwork Tue Oct 17 18:21:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Limonciello, Mario" X-Patchwork-Id: 10012685 X-Patchwork-Delegate: dvhart@infradead.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D933060211 for ; Tue, 17 Oct 2017 18:23:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D03B7289CB for ; Tue, 17 Oct 2017 18:23:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C4A30289D3; Tue, 17 Oct 2017 18:23:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1271C289CB for ; Tue, 17 Oct 2017 18:23:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965492AbdJQSXD (ORCPT ); Tue, 17 Oct 2017 14:23:03 -0400 Received: from esa7.dell-outbound.iphmx.com ([68.232.153.96]:19818 "EHLO esa7.dell-outbound.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S937420AbdJQSWN (ORCPT ); Tue, 17 Oct 2017 14:22:13 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=dell.com; i=@dell.com; q=dns/txt; s=smtpout; t=1508264103; x=1539800103; h=from:to:cc:subject:date:message-id; bh=V2uyUT+oeD7rUufFAwj8MvLGg4NzOH0yZ9EbjjB8054=; b=sY3bi49kNiboQDxIQZgiUuZNqG6/6Dbxo4QzEs/K3BHUAPISMexjPQqG 3Q+LkweCPsUruWKOb1KdoSKyne/HS1WEXCtLuDstKp5xAuOx1SXK29xas EZYPLmJCogAV36GuilLT1n0/SBUrbk15HjWiGkHL24AHaai6d84F57DY4 E=; Received: from esa1.dell-outbound2.iphmx.com ([68.232.153.201]) by esa7.dell-outbound.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Oct 2017 13:14:57 -0500 Received: from ausxipps306.us.dell.com ([143.166.148.156]) by esa1.dell-outbound2.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 18 Oct 2017 00:21:16 +0600 X-LoopCount0: from 10.208.86.39 X-IronPort-AV: E=Sophos;i="5.43,391,1503378000"; d="scan'208";a="161471203" X-DLP: DLP_GlobalPCIDSS From: Mario Limonciello To: dvhart@infradead.org, Andy Shevchenko Cc: LKML , platform-driver-x86@vger.kernel.org, Andy Lutomirski , quasisec@google.com, pali.rohar@gmail.com, rjw@rjwysocki.net, mjg59@google.com, hch@lst.de, Greg KH , Alan Cox , Mario Limonciello Subject: [PATCH v9 15/17] platform/x86: dell-smbios-wmi: introduce userspace interface Date: Tue, 17 Oct 2017 13:21:59 -0500 Message-Id: <48fc8806415aba7c77bcec0466c9b62a1a9b9328.1508259916.git.mario.limonciello@dell.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: References: In-Reply-To: References: Sender: platform-driver-x86-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: platform-driver-x86@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 use the character device the buffer needed for the machine will also be needed. This information is exported to a sysfs attribute by the WMI bus in "required_buffer_size". 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. Use the dell-smbios filtering functionality to prevent access to certain tokens and calls. Signed-off-by: Mario Limonciello Reviewed-by: Edward O'Callaghan --- Documentation/ABI/testing/dell-smbios-wmi | 41 ++++++++++++++++ drivers/platform/x86/dell-smbios-wmi.c | 81 ++++++++++++++++++++++++++----- drivers/platform/x86/dell-smbios.h | 11 +---- include/uapi/linux/wmi.h | 26 ++++++++++ 4 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 Documentation/ABI/testing/dell-smbios-wmi diff --git a/Documentation/ABI/testing/dell-smbios-wmi b/Documentation/ABI/testing/dell-smbios-wmi new file mode 100644 index 000000000000..572c3eb53c5c --- /dev/null +++ b/Documentation/ABI/testing/dell-smbios-wmi @@ -0,0 +1,41 @@ +What: /dev/wmi/dell-smbios +Date: November 2017 +KernelVersion: 4.15 +Contact: "Mario Limonciello" +Description: + Perform SMBIOS calls on supported Dell machines. + through the Dell ACPI-WMI interface. + + IOCTL's and buffer formats are defined in: + + + 1) To perform a call from userspace, you'll need to first + determine the minimum size of the calling interface buffer + for your machine. + Platforms that contain larger buffers can return larger + objects from the system firmware. + Commonly this size is either 4k or 32k. + + To determine the size of the buffer, look in the device's + directory in sysfs for a "required_buffer_size" attribute. + + 2) After you've determined the minimum size of the calling + interface buffer, you can allocate a structure that represents + the structure documented above. + + 3) In the 'length' object store the size of the buffer you + determined above and allocated. + + 4) In this buffer object, prepare as necessary for the SMBIOS + call you're interested in. Typically SMBIOS buffers have + "class", "select", and "input" defined to values that coincide + with the data you are interested in. + Documenting class/select/input values is outside of the scope + of this documentation. Check with the libsmbios project for + further documentation on these values. + + 6) Run the call by using ioctl() as described in the header. + + 7) The output will be returned in the buffer object. + + 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 3d68dd100b02..8e189a0bc346 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 { #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" -struct dell_wmi_extensions { - __u32 argattrib; - __u32 blength; - __u8 data[]; -} __packed; - -struct dell_wmi_smbios_buffer { - struct calling_interface_buffer std; - struct dell_wmi_extensions ext; -} __packed; - struct wmi_smbios_priv { struct dell_wmi_smbios_buffer *buf; struct list_head list; @@ -117,6 +106,66 @@ int dell_smbios_wmi_call(struct calling_interface_buffer *buffer) return ret; } +static void _debug_ioctl(struct device *d, unsigned int expected, + unsigned int cmd) +{ + if (_IOC_DIR(expected) != _IOC_DIR(cmd)) + dev_dbg(d, "Invalid _IOC_DIR: %d\n", _IOC_DIR(cmd)); + if (_IOC_TYPE(expected) != _IOC_TYPE(cmd)) + dev_dbg(d, "Invalid _IOC_TYPE: %d\n", _IOC_TYPE(cmd)); + if (_IOC_NR(expected) != _IOC_NR(cmd)) + dev_dbg(d, "Invalid _IOC_NR: %d\n", _IOC_NR(cmd)); + if (_IOC_SIZE(expected) != _IOC_SIZE(cmd)) + dev_dbg(d, "Invalid _IOC_SIZE: %d\n", _IOC_SIZE(cmd)); +} + +static long dell_smbios_wmi_ioctl(struct wmi_device *wdev, unsigned int cmd, + unsigned long arg) +{ + void __user *input = (void __user *) arg; + struct wmi_smbios_priv *priv; + int ret = 0; + + switch (cmd) { + case DELL_WMI_SMBIOS_CMD: + priv = dev_get_drvdata(&wdev->dev); + if (!priv) + return -ENODEV; + mutex_lock(&call_mutex); + /* read the structure from userspace */ + if (copy_from_user(priv->buf, input, priv->req_buf_size)) { + dev_dbg(&wdev->dev, "Copy %d from user failed\n", + priv->req_buf_size); + ret = -EFAULT; + goto fail_smbios_cmd; + } + /* check for any calls we should avoid */ + if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { + dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", + priv->buf->std.class, priv->buf->std.select, + priv->buf->std.input[0]); + ret = -EFAULT; + goto fail_smbios_cmd; + } + ret = run_smbios_call(priv->wdev); + if (ret != 0) + goto fail_smbios_cmd; + /* return the result (only up to our internal buffer size) */ + if (copy_to_user(input, priv->buf, priv->req_buf_size)) { + dev_dbg(&wdev->dev, "Copy %d to user failed\n", + priv->req_buf_size); + ret = -EFAULT; + } +fail_smbios_cmd: + mutex_unlock(&call_mutex); + break; + default: + _debug_ioctl(&wdev->dev, DELL_WMI_SMBIOS_CMD, cmd); + ret = -ENOIOCTLCMD; + } + return ret; +} + static int dell_smbios_wmi_probe(struct wmi_device *wdev) { struct wmi_smbios_priv *priv; @@ -131,6 +180,12 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev) /* WMI buffer size will be either 4k or 32k depending on machine */ if (!dell_wmi_get_size(&priv->req_buf_size)) return -EPROBE_DEFER; + /* add in the length object we will use internally with ioctl */ + priv->req_buf_size += sizeof(u64); + + ret = set_required_buffer_size(wdev, 0, priv->req_buf_size); + if (ret) + return ret; count = get_order(priv->req_buf_size); priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); @@ -207,6 +262,10 @@ static struct wmi_driver dell_smbios_wmi_driver = { .probe = dell_smbios_wmi_probe, .remove = dell_smbios_wmi_remove, .id_table = dell_smbios_wmi_id_table, + .unlocked_ioctl = dell_smbios_wmi_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = dell_smbios_wmi_ioctl, +#endif }; static int __init init_dell_smbios_wmi(void) diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h index 91b9c615452d..edb9ece06451 100644 --- a/drivers/platform/x86/dell-smbios.h +++ b/drivers/platform/x86/dell-smbios.h @@ -17,6 +17,7 @@ #define _DELL_SMBIOS_H_ #include +#include /* Common classes and selects*/ #define CLASS_TOKEN_READ 0 @@ -58,16 +59,6 @@ struct notifier_block; -/* This structure will be modified by the firmware when we enter - * system management mode, hence the volatiles */ - -struct calling_interface_buffer { - u16 class; - u16 select; - volatile u32 input[4]; - volatile u32 output[4]; -} __packed; - struct calling_interface_token { u16 tokenID; u16 location; diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h index 7e52350ac9b3..622e4004bcfc 100644 --- a/include/uapi/linux/wmi.h +++ b/include/uapi/linux/wmi.h @@ -10,6 +10,7 @@ #ifndef _UAPI_LINUX_WMI_H #define _UAPI_LINUX_WMI_H +#include #include /* WMI bus will filter all WMI vendor driver requests through this IOC */ @@ -23,4 +24,29 @@ struct wmi_ioctl_buffer { __u8 data[]; }; +/* This structure may be modified by the firmware when we enter + * system management mode through SMM, hence the volatiles + */ +struct calling_interface_buffer { + __u16 class; + __u16 select; + volatile __u32 input[4]; + volatile __u32 output[4]; +} __packed; + +struct dell_wmi_extensions { + __u32 argattrib; + __u32 blength; + __u8 data[]; +} __packed; + +struct dell_wmi_smbios_buffer { + __u64 length; + struct calling_interface_buffer std; + struct dell_wmi_extensions ext; +} __packed; + +/* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */ +#define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer) + #endif