^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) * SMI methods for use with 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) * Copyright (c) 2017 Dell Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/gfp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/io.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/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "dcdbas.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include "dell-smbios.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) static int da_command_address;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) static int da_command_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) static struct calling_interface_buffer *buffer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static struct platform_device *platform_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static DEFINE_MUTEX(smm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static const struct dmi_system_id dell_device_table[] __initconst = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) .ident = "Dell laptop",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) .ident = "Dell Computer Corporation",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) MODULE_DEVICE_TABLE(dmi, dell_device_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static void parse_da_table(const struct dmi_header *dm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct calling_interface_structure *table =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) container_of(dm, struct calling_interface_structure, header);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * 6 bytes of entry
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (dm->length < 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) da_command_address = table->cmdIOAddress;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) da_command_code = table->cmdIOCode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static void find_cmd_address(const struct dmi_header *dm, void *dummy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) switch (dm->type) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) case 0xda: /* Calling interface */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) parse_da_table(dm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) }
^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) static int dell_smbios_smm_call(struct calling_interface_buffer *input)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct smi_cmd command;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) size_t size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) size = sizeof(struct calling_interface_buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) command.magic = SMI_CMD_MAGIC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) command.command_address = da_command_address;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) command.command_code = da_command_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) command.ebx = virt_to_phys(buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) command.ecx = 0x42534931;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) mutex_lock(&smm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) memcpy(buffer, input, size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) dcdbas_smi_request(&command);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) memcpy(input, buffer, size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) mutex_unlock(&smm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) /* When enabled this indicates that SMM won't work */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) static bool test_wsmt_enabled(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) struct calling_interface_token *wsmt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) /* if token doesn't exist, SMM will work */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (!wsmt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) /* If token exists, try to access over SMM but set a dummy return.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) * - If WSMT disabled it will be overwritten by SMM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) * - If WSMT enabled then dummy value will remain
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) buffer->cmd_class = CLASS_TOKEN_READ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) buffer->cmd_select = SELECT_TOKEN_STD;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) memset(buffer, 0, sizeof(struct calling_interface_buffer));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) buffer->input[0] = wsmt->location;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) buffer->output[0] = 99;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) dell_smbios_smm_call(buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (buffer->output[0] == 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) int init_dell_smbios_smm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) * is passed to SMI handler.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (!buffer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) dmi_walk(find_cmd_address, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) if (test_wsmt_enabled()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) pr_debug("Disabling due to WSMT enabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) goto fail_wsmt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) platform_device = platform_device_alloc("dell-smbios", 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (!platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) goto fail_platform_device_alloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) ret = platform_device_add(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) goto fail_platform_device_add;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) ret = dell_smbios_register_device(&platform_device->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) &dell_smbios_smm_call);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) goto fail_register;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) fail_register:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) platform_device_del(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) fail_platform_device_add:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) platform_device_put(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) fail_wsmt:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) fail_platform_device_alloc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) free_page((unsigned long)buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) void exit_dell_smbios_smm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if (platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) dell_smbios_unregister_device(&platform_device->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) platform_device_unregister(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) free_page((unsigned long)buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }