^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Common functions for kernel modules using Dell SMBIOS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) Red Hat <mjg@redhat.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (c) 2014 Pali Rohár <pali@kernel.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Based on documentation in the libsmbios package:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Copyright (C) 2005-2014 Dell Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/capability.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include "dell-smbios.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static u32 da_supported_commands;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static int da_num_tokens;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static struct platform_device *platform_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static struct calling_interface_token *da_tokens;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static struct device_attribute *token_location_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static struct device_attribute *token_value_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static struct attribute **token_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) static DEFINE_MUTEX(smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) struct smbios_device {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct list_head list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct device *device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) int (*call_fn)(struct calling_interface_buffer *arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct smbios_call {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) u32 need_capability;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) int cmd_class;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) int cmd_select;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) /* calls that are whitelisted for given capabilities */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) static struct smbios_call call_whitelist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) /* generally tokens are allowed, but may be further filtered or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * restricted by token blacklist or whitelist
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) /* used by userspace: fwupdate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) /* used by userspace: fwupd */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) /* calls that are explicitly blacklisted */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static struct smbios_call call_blacklist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {0x0000, 1, 7}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) {0x0000, 6, 5}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {0x0000, 11, 3}, /* write once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) {0x0000, 11, 7}, /* write once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {0x0000, 11, 11}, /* write once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) {0x0000, 19, -1}, /* diagnostics */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) /* handled by kernel: dell-laptop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) {0x0000, CLASS_INFO, SELECT_RFKILL},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) struct token_range {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u32 need_capability;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) u16 min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) u16 max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) /* tokens that are whitelisted for given capabilities */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static struct token_range token_whitelist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) /* used by userspace: fwupdate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) /* can indicate to userspace that WMI is needed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* tokens that are explicitly blacklisted */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) static struct token_range token_blacklist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) {0x0000, 0x0058, 0x0059}, /* ME use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) {0x0000, 0x0175, 0x0176}, /* write once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) {0x0000, 0x0195, 0x0197}, /* diagnostics */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) {0x0000, 0x027D, 0x0284}, /* diagnostics */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {0x0000, 0x0300, 0x0302}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {0x0000, 0x0325, 0x0326}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {0x0000, 0x0332, 0x0335}, /* fan control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) {0x0000, 0x0350, 0x0350}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {0x0000, 0x0363, 0x0363}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {0x0000, 0x0368, 0x0368}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {0x0000, 0x049E, 0x049F}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) {0x0000, 0x04A0, 0x04A3}, /* disagnostics */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {0x0000, 0x9000, 0x9001}, /* internal BIOS use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) {0x0000, 0xA000, 0xBFFF}, /* write only */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /* handled by kernel: dell-laptop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static LIST_HEAD(smbios_device_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) int dell_smbios_error(int value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) switch (value) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) case 0: /* Completed successfully */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) case -1: /* Completed with error */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) case -2: /* Function not supported */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) default: /* Unknown error */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) EXPORT_SYMBOL_GPL(dell_smbios_error);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) int dell_smbios_register_device(struct device *d, void *call_fn)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) struct smbios_device *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) get_device(d);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) priv->device = d;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) priv->call_fn = call_fn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) mutex_lock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) list_add_tail(&priv->list, &smbios_device_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) mutex_unlock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) dev_dbg(d, "Added device: %s\n", d->driver->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) EXPORT_SYMBOL_GPL(dell_smbios_register_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) void dell_smbios_unregister_device(struct device *d)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) struct smbios_device *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) mutex_lock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) list_for_each_entry(priv, &smbios_device_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (priv->device == d) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) list_del(&priv->list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) put_device(d);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) mutex_unlock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) dev_dbg(d, "Remove device: %s\n", d->driver->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) int dell_smbios_call_filter(struct device *d,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) struct calling_interface_buffer *buffer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) u16 t = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) /* can't make calls over 30 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if (buffer->cmd_class > 30) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) dev_dbg(d, "class too big: %u\n", buffer->cmd_class);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) /* supported calls on the particular system */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) if (!(da_supported_commands & (1 << buffer->cmd_class))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) da_supported_commands);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) /* match against call blacklist */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) if (buffer->cmd_class != call_blacklist[i].cmd_class)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) if (buffer->cmd_select != call_blacklist[i].cmd_select &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) call_blacklist[i].cmd_select != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) dev_dbg(d, "blacklisted command: %u/%u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) buffer->cmd_class, buffer->cmd_select);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) /* if a token call, find token ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) if ((buffer->cmd_class == CLASS_TOKEN_READ ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) buffer->cmd_class == CLASS_TOKEN_WRITE) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) buffer->cmd_select < 3) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) /* tokens enabled ? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) if (!da_tokens) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) dev_dbg(d, "no token support on this system\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) /* find the matching token ID */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) for (i = 0; i < da_num_tokens; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) if (da_tokens[i].location != buffer->input[0])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) t = da_tokens[i].tokenID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) /* token call; but token didn't exist */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (!t) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) dev_dbg(d, "token at location %04x doesn't exist\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) buffer->input[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) /* match against token blacklist */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) if (!token_blacklist[i].min || !token_blacklist[i].max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) if (t >= token_blacklist[i].min &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) t <= token_blacklist[i].max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) /* match against token whitelist */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (!token_whitelist[i].min || !token_whitelist[i].max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (t < token_whitelist[i].min ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) t > token_whitelist[i].max)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) if (!token_whitelist[i].need_capability ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) capable(token_whitelist[i].need_capability)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) dev_dbg(d, "whitelisted token: %x\n", t);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) /* match against call whitelist */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (buffer->cmd_class != call_whitelist[i].cmd_class)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) if (buffer->cmd_select != call_whitelist[i].cmd_select)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (!call_whitelist[i].need_capability ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) capable(call_whitelist[i].need_capability)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) dev_dbg(d, "whitelisted capable command: %u/%u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) buffer->cmd_class, buffer->cmd_select);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) dev_dbg(d, "missing capability %d for %u/%u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) call_whitelist[i].need_capability,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) buffer->cmd_class, buffer->cmd_select);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) /* not in a whitelist, only allow processes with capabilities */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) if (capable(CAP_SYS_RAWIO)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) buffer->cmd_class, buffer->cmd_select);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) return -EACCES;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) EXPORT_SYMBOL_GPL(dell_smbios_call_filter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) int dell_smbios_call(struct calling_interface_buffer *buffer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) int (*call_fn)(struct calling_interface_buffer *) = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) struct device *selected_dev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) struct smbios_device *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) mutex_lock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) list_for_each_entry(priv, &smbios_device_list, list) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) if (!selected_dev || priv->device->id >= selected_dev->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) dev_dbg(priv->device, "Trying device ID: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) priv->device->id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) call_fn = priv->call_fn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) selected_dev = priv->device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) if (!selected_dev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) pr_err("No dell-smbios drivers are loaded\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) goto out_smbios_call;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) ret = call_fn(buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) out_smbios_call:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) mutex_unlock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) EXPORT_SYMBOL_GPL(dell_smbios_call);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) struct calling_interface_token *dell_smbios_find_token(int tokenid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) if (!da_tokens)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) for (i = 0; i < da_num_tokens; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (da_tokens[i].tokenID == tokenid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) return &da_tokens[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) EXPORT_SYMBOL_GPL(dell_smbios_find_token);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) int dell_laptop_register_notifier(struct notifier_block *nb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) return blocking_notifier_chain_register(&dell_laptop_chain_head, nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) EXPORT_SYMBOL_GPL(dell_laptop_register_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) int dell_laptop_unregister_notifier(struct notifier_block *nb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) return blocking_notifier_chain_unregister(&dell_laptop_chain_head, nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) void dell_laptop_call_notifier(unsigned long action, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) blocking_notifier_call_chain(&dell_laptop_chain_head, action, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) EXPORT_SYMBOL_GPL(dell_laptop_call_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static void __init parse_da_table(const struct dmi_header *dm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) /* Final token is a terminator, so we don't want to copy it */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) struct calling_interface_token *new_da_tokens;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) struct calling_interface_structure *table =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) container_of(dm, struct calling_interface_structure, header);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) * 4 bytes of table header, plus 7 bytes of Dell header
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) * plus at least 6 bytes of entry
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) if (dm->length < 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) da_supported_commands = table->supportedCmds;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) sizeof(struct calling_interface_token),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (!new_da_tokens)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) da_tokens = new_da_tokens;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) memcpy(da_tokens+da_num_tokens, table->tokens,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) sizeof(struct calling_interface_token) * tokens);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) da_num_tokens += tokens;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) static void zero_duplicates(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) int i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) for (i = 0; i < da_num_tokens; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) if (da_tokens[i].tokenID == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) for (j = i+1; j < da_num_tokens; j++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) if (da_tokens[j].tokenID == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) if (da_tokens[i].tokenID == da_tokens[j].tokenID) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) da_tokens[j].tokenID,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) da_tokens[j].location,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) da_tokens[j].value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) da_tokens[j].tokenID = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) static void __init find_tokens(const struct dmi_header *dm, void *dummy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) switch (dm->type) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) case 0xd4: /* Indexed IO */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) case 0xd5: /* Protected Area Type 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) case 0xd6: /* Protected Area Type 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) case 0xda: /* Calling interface */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) parse_da_table(dm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) static int match_attribute(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) struct device_attribute *attr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) for (i = 0; i < da_num_tokens * 2; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if (!token_attrs[i])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) if (strcmp(token_attrs[i]->name, attr->attr.name) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) return i/2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) dev_dbg(dev, "couldn't match: %s\n", attr->attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) static ssize_t location_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) if (!capable(CAP_SYS_ADMIN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) i = match_attribute(dev, attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) if (i > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) static ssize_t value_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) if (!capable(CAP_SYS_ADMIN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) i = match_attribute(dev, attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) if (i > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) static struct attribute_group smbios_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) .name = "tokens"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) static struct platform_driver platform_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) .name = "dell-smbios",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) static int build_tokens_sysfs(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) char *location_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) char *value_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) size_t size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) int i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) /* (number of tokens + 1 for null terminated */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) size = sizeof(struct device_attribute) * (da_num_tokens + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) token_location_attrs = kzalloc(size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) if (!token_location_attrs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) token_value_attrs = kzalloc(size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) if (!token_value_attrs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) goto out_allocate_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) /* need to store both location and value + terminator*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) token_attrs = kzalloc(size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) if (!token_attrs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) goto out_allocate_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) for (i = 0, j = 0; i < da_num_tokens; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) /* skip empty */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) if (da_tokens[i].tokenID == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) /* add location */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) location_name = kasprintf(GFP_KERNEL, "%04x_location",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) da_tokens[i].tokenID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) if (location_name == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) goto out_unwind_strings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) sysfs_attr_init(&token_location_attrs[i].attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) token_location_attrs[i].attr.name = location_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) token_location_attrs[i].attr.mode = 0444;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) token_location_attrs[i].show = location_show;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) token_attrs[j++] = &token_location_attrs[i].attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) /* add value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) value_name = kasprintf(GFP_KERNEL, "%04x_value",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) da_tokens[i].tokenID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) if (value_name == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) goto loop_fail_create_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) sysfs_attr_init(&token_value_attrs[i].attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) token_value_attrs[i].attr.name = value_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) token_value_attrs[i].attr.mode = 0444;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) token_value_attrs[i].show = value_show;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) token_attrs[j++] = &token_value_attrs[i].attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) loop_fail_create_value:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) kfree(location_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) goto out_unwind_strings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) smbios_attribute_group.attrs = token_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) goto out_unwind_strings;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) out_unwind_strings:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) while (i--) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) kfree(token_location_attrs[i].attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) kfree(token_value_attrs[i].attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) kfree(token_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) out_allocate_attrs:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) kfree(token_value_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) out_allocate_value:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) kfree(token_location_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) static void free_group(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) sysfs_remove_group(&pdev->dev.kobj,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) &smbios_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) for (i = 0; i < da_num_tokens; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) kfree(token_location_attrs[i].attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) kfree(token_value_attrs[i].attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) kfree(token_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) kfree(token_value_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) kfree(token_location_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) static int __init dell_smbios_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) int ret, wmi, smm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) pr_err("Unable to run on non-Dell system\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) dmi_walk(find_tokens, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) ret = platform_driver_register(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) goto fail_platform_driver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) platform_device = platform_device_alloc("dell-smbios", 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) if (!platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) goto fail_platform_device_alloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) ret = platform_device_add(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) goto fail_platform_device_add;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) /* register backends */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) wmi = init_dell_smbios_wmi();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) if (wmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) pr_debug("Failed to initialize WMI backend: %d\n", wmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) smm = init_dell_smbios_smm();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) if (smm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) pr_debug("Failed to initialize SMM backend: %d\n", smm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) if (wmi && smm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) wmi, smm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) goto fail_create_group;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) if (da_tokens) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) /* duplicate tokens will cause problems building sysfs files */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) zero_duplicates(&platform_device->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) ret = build_tokens_sysfs(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) goto fail_sysfs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) fail_sysfs:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) free_group(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) fail_create_group:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) platform_device_del(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) fail_platform_device_add:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) platform_device_put(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) fail_platform_device_alloc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) platform_driver_unregister(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) fail_platform_driver:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) kfree(da_tokens);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) static void __exit dell_smbios_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) exit_dell_smbios_wmi();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) exit_dell_smbios_smm();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) mutex_lock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) if (platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) if (da_tokens)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) free_group(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) platform_device_unregister(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) platform_driver_unregister(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) kfree(da_tokens);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) mutex_unlock(&smbios_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) module_init(dell_smbios_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) module_exit(dell_smbios_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) MODULE_LICENSE("GPL");