Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  WMI methods for use with dell-smbios
0004  *
0005  *  Copyright (c) 2017 Dell Inc.
0006  */
0007 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0008 
0009 #include <linux/dmi.h>
0010 #include <linux/list.h>
0011 #include <linux/module.h>
0012 #include <linux/mutex.h>
0013 #include <linux/uaccess.h>
0014 #include <linux/wmi.h>
0015 #include "dell-smbios.h"
0016 #include "dell-wmi-descriptor.h"
0017 
0018 static DEFINE_MUTEX(call_mutex);
0019 static DEFINE_MUTEX(list_mutex);
0020 static int wmi_supported;
0021 
0022 struct misc_bios_flags_structure {
0023     struct dmi_header header;
0024     u16 flags0;
0025 } __packed;
0026 #define FLAG_HAS_ACPI_WMI 0x02
0027 
0028 #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
0029 
0030 struct wmi_smbios_priv {
0031     struct dell_wmi_smbios_buffer *buf;
0032     struct list_head list;
0033     struct wmi_device *wdev;
0034     struct device *child;
0035     u32 req_buf_size;
0036 };
0037 static LIST_HEAD(wmi_list);
0038 
0039 static inline struct wmi_smbios_priv *get_first_smbios_priv(void)
0040 {
0041     return list_first_entry_or_null(&wmi_list,
0042                     struct wmi_smbios_priv,
0043                     list);
0044 }
0045 
0046 static int run_smbios_call(struct wmi_device *wdev)
0047 {
0048     struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
0049     struct wmi_smbios_priv *priv;
0050     struct acpi_buffer input;
0051     union acpi_object *obj;
0052     acpi_status status;
0053 
0054     priv = dev_get_drvdata(&wdev->dev);
0055     input.length = priv->req_buf_size - sizeof(u64);
0056     input.pointer = &priv->buf->std;
0057 
0058     dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n",
0059         priv->buf->std.cmd_class, priv->buf->std.cmd_select,
0060         priv->buf->std.input[0], priv->buf->std.input[1],
0061         priv->buf->std.input[2], priv->buf->std.input[3]);
0062 
0063     status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
0064     if (ACPI_FAILURE(status))
0065         return -EIO;
0066     obj = (union acpi_object *)output.pointer;
0067     if (obj->type != ACPI_TYPE_BUFFER) {
0068         dev_dbg(&wdev->dev, "received type: %d\n", obj->type);
0069         if (obj->type == ACPI_TYPE_INTEGER)
0070             dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n",
0071                 obj->integer.value);
0072         kfree(output.pointer);
0073         return -EIO;
0074     }
0075     memcpy(input.pointer, obj->buffer.pointer, obj->buffer.length);
0076     dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n",
0077         priv->buf->std.output[0], priv->buf->std.output[1],
0078         priv->buf->std.output[2], priv->buf->std.output[3]);
0079     kfree(output.pointer);
0080 
0081     return 0;
0082 }
0083 
0084 static int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
0085 {
0086     struct wmi_smbios_priv *priv;
0087     size_t difference;
0088     size_t size;
0089     int ret;
0090 
0091     mutex_lock(&call_mutex);
0092     priv = get_first_smbios_priv();
0093     if (!priv) {
0094         ret = -ENODEV;
0095         goto out_wmi_call;
0096     }
0097 
0098     size = sizeof(struct calling_interface_buffer);
0099     difference = priv->req_buf_size - sizeof(u64) - size;
0100 
0101     memset(&priv->buf->ext, 0, difference);
0102     memcpy(&priv->buf->std, buffer, size);
0103     ret = run_smbios_call(priv->wdev);
0104     memcpy(buffer, &priv->buf->std, size);
0105 out_wmi_call:
0106     mutex_unlock(&call_mutex);
0107 
0108     return ret;
0109 }
0110 
0111 static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
0112                    struct wmi_ioctl_buffer *arg)
0113 {
0114     struct wmi_smbios_priv *priv;
0115     int ret = 0;
0116 
0117     switch (cmd) {
0118     case DELL_WMI_SMBIOS_CMD:
0119         mutex_lock(&call_mutex);
0120         priv = dev_get_drvdata(&wdev->dev);
0121         if (!priv) {
0122             ret = -ENODEV;
0123             goto fail_smbios_cmd;
0124         }
0125         memcpy(priv->buf, arg, priv->req_buf_size);
0126         if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
0127             dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
0128                 priv->buf->std.cmd_class,
0129                 priv->buf->std.cmd_select,
0130                 priv->buf->std.input[0]);
0131             ret = -EFAULT;
0132             goto fail_smbios_cmd;
0133         }
0134         ret = run_smbios_call(priv->wdev);
0135         if (ret)
0136             goto fail_smbios_cmd;
0137         memcpy(arg, priv->buf, priv->req_buf_size);
0138 fail_smbios_cmd:
0139         mutex_unlock(&call_mutex);
0140         break;
0141     default:
0142         ret = -ENOIOCTLCMD;
0143     }
0144     return ret;
0145 }
0146 
0147 static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
0148 {
0149     struct wmi_driver *wdriver =
0150         container_of(wdev->dev.driver, struct wmi_driver, driver);
0151     struct wmi_smbios_priv *priv;
0152     u32 hotfix;
0153     int count;
0154     int ret;
0155 
0156     ret = dell_wmi_get_descriptor_valid();
0157     if (ret)
0158         return ret;
0159 
0160     priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv),
0161                 GFP_KERNEL);
0162     if (!priv)
0163         return -ENOMEM;
0164 
0165     /* WMI buffer size will be either 4k or 32k depending on machine */
0166     if (!dell_wmi_get_size(&priv->req_buf_size))
0167         return -EPROBE_DEFER;
0168 
0169     /* some SMBIOS calls fail unless BIOS contains hotfix */
0170     if (!dell_wmi_get_hotfix(&hotfix))
0171         return -EPROBE_DEFER;
0172     if (!hotfix) {
0173         dev_warn(&wdev->dev,
0174             "WMI SMBIOS userspace interface not supported(%u), try upgrading to a newer BIOS\n",
0175             hotfix);
0176         wdriver->filter_callback = NULL;
0177     }
0178 
0179     /* add in the length object we will use internally with ioctl */
0180     priv->req_buf_size += sizeof(u64);
0181     ret = set_required_buffer_size(wdev, priv->req_buf_size);
0182     if (ret)
0183         return ret;
0184 
0185     count = get_order(priv->req_buf_size);
0186     priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
0187     if (!priv->buf)
0188         return -ENOMEM;
0189 
0190     /* ID is used by dell-smbios to set priority of drivers */
0191     wdev->dev.id = 1;
0192     ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
0193     if (ret)
0194         goto fail_register;
0195 
0196     priv->wdev = wdev;
0197     dev_set_drvdata(&wdev->dev, priv);
0198     mutex_lock(&list_mutex);
0199     list_add_tail(&priv->list, &wmi_list);
0200     mutex_unlock(&list_mutex);
0201 
0202     return 0;
0203 
0204 fail_register:
0205     free_pages((unsigned long)priv->buf, count);
0206     return ret;
0207 }
0208 
0209 static void dell_smbios_wmi_remove(struct wmi_device *wdev)
0210 {
0211     struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
0212     int count;
0213 
0214     mutex_lock(&call_mutex);
0215     mutex_lock(&list_mutex);
0216     list_del(&priv->list);
0217     mutex_unlock(&list_mutex);
0218     dell_smbios_unregister_device(&wdev->dev);
0219     count = get_order(priv->req_buf_size);
0220     free_pages((unsigned long)priv->buf, count);
0221     mutex_unlock(&call_mutex);
0222 }
0223 
0224 static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
0225     { .guid_string = DELL_WMI_SMBIOS_GUID },
0226     { },
0227 };
0228 
0229 static void parse_b1_table(const struct dmi_header *dm)
0230 {
0231     struct misc_bios_flags_structure *flags =
0232     container_of(dm, struct misc_bios_flags_structure, header);
0233 
0234     /* 4 bytes header, 8 bytes flags */
0235     if (dm->length < 12)
0236         return;
0237     if (dm->handle != 0xb100)
0238         return;
0239     if ((flags->flags0 & FLAG_HAS_ACPI_WMI))
0240         wmi_supported = 1;
0241 }
0242 
0243 static void find_b1(const struct dmi_header *dm, void *dummy)
0244 {
0245     switch (dm->type) {
0246     case 0xb1: /* misc bios flags */
0247         parse_b1_table(dm);
0248         break;
0249     }
0250 }
0251 
0252 static struct wmi_driver dell_smbios_wmi_driver = {
0253     .driver = {
0254         .name = "dell-smbios",
0255     },
0256     .probe = dell_smbios_wmi_probe,
0257     .remove = dell_smbios_wmi_remove,
0258     .id_table = dell_smbios_wmi_id_table,
0259     .filter_callback = dell_smbios_wmi_filter,
0260 };
0261 
0262 int init_dell_smbios_wmi(void)
0263 {
0264     dmi_walk(find_b1, NULL);
0265 
0266     if (!wmi_supported)
0267         return -ENODEV;
0268 
0269     return wmi_driver_register(&dell_smbios_wmi_driver);
0270 }
0271 
0272 void exit_dell_smbios_wmi(void)
0273 {
0274     if (wmi_supported)
0275         wmi_driver_unregister(&dell_smbios_wmi_driver);
0276 }
0277 
0278 MODULE_DEVICE_TABLE(wmi, dell_smbios_wmi_id_table);