0001
0002
0003
0004
0005
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
0166 if (!dell_wmi_get_size(&priv->req_buf_size))
0167 return -EPROBE_DEFER;
0168
0169
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
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
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
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:
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);