root/drivers/platform/x86/dell-smbios-wmi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_first_smbios_priv
  2. run_smbios_call
  3. dell_smbios_wmi_call
  4. dell_smbios_wmi_filter
  5. dell_smbios_wmi_probe
  6. dell_smbios_wmi_remove
  7. parse_b1_table
  8. find_b1
  9. init_dell_smbios_wmi
  10. exit_dell_smbios_wmi

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *  WMI methods for use with dell-smbios
   4  *
   5  *  Copyright (c) 2017 Dell Inc.
   6  */
   7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   8 
   9 #include <linux/dmi.h>
  10 #include <linux/list.h>
  11 #include <linux/module.h>
  12 #include <linux/mutex.h>
  13 #include <linux/uaccess.h>
  14 #include <linux/wmi.h>
  15 #include "dell-smbios.h"
  16 #include "dell-wmi-descriptor.h"
  17 
  18 static DEFINE_MUTEX(call_mutex);
  19 static DEFINE_MUTEX(list_mutex);
  20 static int wmi_supported;
  21 
  22 struct misc_bios_flags_structure {
  23         struct dmi_header header;
  24         u16 flags0;
  25 } __packed;
  26 #define FLAG_HAS_ACPI_WMI 0x02
  27 
  28 #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
  29 
  30 struct wmi_smbios_priv {
  31         struct dell_wmi_smbios_buffer *buf;
  32         struct list_head list;
  33         struct wmi_device *wdev;
  34         struct device *child;
  35         u32 req_buf_size;
  36 };
  37 static LIST_HEAD(wmi_list);
  38 
  39 static inline struct wmi_smbios_priv *get_first_smbios_priv(void)
  40 {
  41         return list_first_entry_or_null(&wmi_list,
  42                                         struct wmi_smbios_priv,
  43                                         list);
  44 }
  45 
  46 static int run_smbios_call(struct wmi_device *wdev)
  47 {
  48         struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
  49         struct wmi_smbios_priv *priv;
  50         struct acpi_buffer input;
  51         union acpi_object *obj;
  52         acpi_status status;
  53 
  54         priv = dev_get_drvdata(&wdev->dev);
  55         input.length = priv->req_buf_size - sizeof(u64);
  56         input.pointer = &priv->buf->std;
  57 
  58         dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n",
  59                 priv->buf->std.cmd_class, priv->buf->std.cmd_select,
  60                 priv->buf->std.input[0], priv->buf->std.input[1],
  61                 priv->buf->std.input[2], priv->buf->std.input[3]);
  62 
  63         status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
  64         if (ACPI_FAILURE(status))
  65                 return -EIO;
  66         obj = (union acpi_object *)output.pointer;
  67         if (obj->type != ACPI_TYPE_BUFFER) {
  68                 dev_dbg(&wdev->dev, "received type: %d\n", obj->type);
  69                 if (obj->type == ACPI_TYPE_INTEGER)
  70                         dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n",
  71                                 obj->integer.value);
  72                 return -EIO;
  73         }
  74         memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length);
  75         dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n",
  76                 priv->buf->std.output[0], priv->buf->std.output[1],
  77                 priv->buf->std.output[2], priv->buf->std.output[3]);
  78         kfree(output.pointer);
  79 
  80         return 0;
  81 }
  82 
  83 static int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
  84 {
  85         struct wmi_smbios_priv *priv;
  86         size_t difference;
  87         size_t size;
  88         int ret;
  89 
  90         mutex_lock(&call_mutex);
  91         priv = get_first_smbios_priv();
  92         if (!priv) {
  93                 ret = -ENODEV;
  94                 goto out_wmi_call;
  95         }
  96 
  97         size = sizeof(struct calling_interface_buffer);
  98         difference = priv->req_buf_size - sizeof(u64) - size;
  99 
 100         memset(&priv->buf->ext, 0, difference);
 101         memcpy(&priv->buf->std, buffer, size);
 102         ret = run_smbios_call(priv->wdev);
 103         memcpy(buffer, &priv->buf->std, size);
 104 out_wmi_call:
 105         mutex_unlock(&call_mutex);
 106 
 107         return ret;
 108 }
 109 
 110 static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
 111                                    struct wmi_ioctl_buffer *arg)
 112 {
 113         struct wmi_smbios_priv *priv;
 114         int ret = 0;
 115 
 116         switch (cmd) {
 117         case DELL_WMI_SMBIOS_CMD:
 118                 mutex_lock(&call_mutex);
 119                 priv = dev_get_drvdata(&wdev->dev);
 120                 if (!priv) {
 121                         ret = -ENODEV;
 122                         goto fail_smbios_cmd;
 123                 }
 124                 memcpy(priv->buf, arg, priv->req_buf_size);
 125                 if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
 126                         dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
 127                                 priv->buf->std.cmd_class,
 128                                 priv->buf->std.cmd_select,
 129                                 priv->buf->std.input[0]);
 130                         ret = -EFAULT;
 131                         goto fail_smbios_cmd;
 132                 }
 133                 ret = run_smbios_call(priv->wdev);
 134                 if (ret)
 135                         goto fail_smbios_cmd;
 136                 memcpy(arg, priv->buf, priv->req_buf_size);
 137 fail_smbios_cmd:
 138                 mutex_unlock(&call_mutex);
 139                 break;
 140         default:
 141                 ret = -ENOIOCTLCMD;
 142         }
 143         return ret;
 144 }
 145 
 146 static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
 147 {
 148         struct wmi_driver *wdriver =
 149                 container_of(wdev->dev.driver, struct wmi_driver, driver);
 150         struct wmi_smbios_priv *priv;
 151         u32 hotfix;
 152         int count;
 153         int ret;
 154 
 155         ret = dell_wmi_get_descriptor_valid();
 156         if (ret)
 157                 return ret;
 158 
 159         priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv),
 160                             GFP_KERNEL);
 161         if (!priv)
 162                 return -ENOMEM;
 163 
 164         /* WMI buffer size will be either 4k or 32k depending on machine */
 165         if (!dell_wmi_get_size(&priv->req_buf_size))
 166                 return -EPROBE_DEFER;
 167 
 168         /* some SMBIOS calls fail unless BIOS contains hotfix */
 169         if (!dell_wmi_get_hotfix(&hotfix))
 170                 return -EPROBE_DEFER;
 171         if (!hotfix) {
 172                 dev_warn(&wdev->dev,
 173                         "WMI SMBIOS userspace interface not supported(%u), try upgrading to a newer BIOS\n",
 174                         hotfix);
 175                 wdriver->filter_callback = NULL;
 176         }
 177 
 178         /* add in the length object we will use internally with ioctl */
 179         priv->req_buf_size += sizeof(u64);
 180         ret = set_required_buffer_size(wdev, priv->req_buf_size);
 181         if (ret)
 182                 return ret;
 183 
 184         count = get_order(priv->req_buf_size);
 185         priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
 186         if (!priv->buf)
 187                 return -ENOMEM;
 188 
 189         /* ID is used by dell-smbios to set priority of drivers */
 190         wdev->dev.id = 1;
 191         ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
 192         if (ret)
 193                 goto fail_register;
 194 
 195         priv->wdev = wdev;
 196         dev_set_drvdata(&wdev->dev, priv);
 197         mutex_lock(&list_mutex);
 198         list_add_tail(&priv->list, &wmi_list);
 199         mutex_unlock(&list_mutex);
 200 
 201         return 0;
 202 
 203 fail_register:
 204         free_pages((unsigned long)priv->buf, count);
 205         return ret;
 206 }
 207 
 208 static int dell_smbios_wmi_remove(struct wmi_device *wdev)
 209 {
 210         struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
 211         int count;
 212 
 213         mutex_lock(&call_mutex);
 214         mutex_lock(&list_mutex);
 215         list_del(&priv->list);
 216         mutex_unlock(&list_mutex);
 217         dell_smbios_unregister_device(&wdev->dev);
 218         count = get_order(priv->req_buf_size);
 219         free_pages((unsigned long)priv->buf, count);
 220         mutex_unlock(&call_mutex);
 221         return 0;
 222 }
 223 
 224 static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
 225         { .guid_string = DELL_WMI_SMBIOS_GUID },
 226         { },
 227 };
 228 
 229 static void parse_b1_table(const struct dmi_header *dm)
 230 {
 231         struct misc_bios_flags_structure *flags =
 232         container_of(dm, struct misc_bios_flags_structure, header);
 233 
 234         /* 4 bytes header, 8 bytes flags */
 235         if (dm->length < 12)
 236                 return;
 237         if (dm->handle != 0xb100)
 238                 return;
 239         if ((flags->flags0 & FLAG_HAS_ACPI_WMI))
 240                 wmi_supported = 1;
 241 }
 242 
 243 static void find_b1(const struct dmi_header *dm, void *dummy)
 244 {
 245         switch (dm->type) {
 246         case 0xb1: /* misc bios flags */
 247                 parse_b1_table(dm);
 248                 break;
 249         }
 250 }
 251 
 252 static struct wmi_driver dell_smbios_wmi_driver = {
 253         .driver = {
 254                 .name = "dell-smbios",
 255         },
 256         .probe = dell_smbios_wmi_probe,
 257         .remove = dell_smbios_wmi_remove,
 258         .id_table = dell_smbios_wmi_id_table,
 259         .filter_callback = dell_smbios_wmi_filter,
 260 };
 261 
 262 int init_dell_smbios_wmi(void)
 263 {
 264         dmi_walk(find_b1, NULL);
 265 
 266         if (!wmi_supported)
 267                 return -ENODEV;
 268 
 269         return wmi_driver_register(&dell_smbios_wmi_driver);
 270 }
 271 
 272 void exit_dell_smbios_wmi(void)
 273 {
 274         wmi_driver_unregister(&dell_smbios_wmi_driver);
 275 }
 276 
 277 MODULE_DEVICE_TABLE(wmi, dell_smbios_wmi_id_table);

/* [<][>][^][v][top][bottom][index][help] */