Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  SMI methods for use with dell-smbios
0004  *
0005  *  Copyright (c) Red Hat <mjg@redhat.com>
0006  *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
0007  *  Copyright (c) 2014 Pali Rohár <pali@kernel.org>
0008  *  Copyright (c) 2017 Dell Inc.
0009  */
0010 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0011 
0012 #include <linux/dmi.h>
0013 #include <linux/gfp.h>
0014 #include <linux/io.h>
0015 #include <linux/module.h>
0016 #include <linux/mutex.h>
0017 #include <linux/platform_device.h>
0018 #include "dcdbas.h"
0019 #include "dell-smbios.h"
0020 
0021 static int da_command_address;
0022 static int da_command_code;
0023 static struct smi_buffer smi_buf;
0024 static struct calling_interface_buffer *buffer;
0025 static struct platform_device *platform_device;
0026 static DEFINE_MUTEX(smm_mutex);
0027 
0028 static void parse_da_table(const struct dmi_header *dm)
0029 {
0030     struct calling_interface_structure *table =
0031         container_of(dm, struct calling_interface_structure, header);
0032 
0033     /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
0034      * 6 bytes of entry
0035      */
0036     if (dm->length < 17)
0037         return;
0038 
0039     da_command_address = table->cmdIOAddress;
0040     da_command_code = table->cmdIOCode;
0041 }
0042 
0043 static void find_cmd_address(const struct dmi_header *dm, void *dummy)
0044 {
0045     switch (dm->type) {
0046     case 0xda: /* Calling interface */
0047         parse_da_table(dm);
0048         break;
0049     }
0050 }
0051 
0052 static int dell_smbios_smm_call(struct calling_interface_buffer *input)
0053 {
0054     struct smi_cmd command;
0055     size_t size;
0056 
0057     size = sizeof(struct calling_interface_buffer);
0058     command.magic = SMI_CMD_MAGIC;
0059     command.command_address = da_command_address;
0060     command.command_code = da_command_code;
0061     command.ebx = smi_buf.dma;
0062     command.ecx = 0x42534931;
0063 
0064     mutex_lock(&smm_mutex);
0065     memcpy(buffer, input, size);
0066     dcdbas_smi_request(&command);
0067     memcpy(input, buffer, size);
0068     mutex_unlock(&smm_mutex);
0069     return 0;
0070 }
0071 
0072 /* When enabled this indicates that SMM won't work */
0073 static bool test_wsmt_enabled(void)
0074 {
0075     struct calling_interface_token *wsmt;
0076 
0077     /* if token doesn't exist, SMM will work */
0078     wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
0079     if (!wsmt)
0080         return false;
0081 
0082     /* If token exists, try to access over SMM but set a dummy return.
0083      * - If WSMT disabled it will be overwritten by SMM
0084      * - If WSMT enabled then dummy value will remain
0085      */
0086     buffer->cmd_class = CLASS_TOKEN_READ;
0087     buffer->cmd_select = SELECT_TOKEN_STD;
0088     memset(buffer, 0, sizeof(struct calling_interface_buffer));
0089     buffer->input[0] = wsmt->location;
0090     buffer->output[0] = 99;
0091     dell_smbios_smm_call(buffer);
0092     if (buffer->output[0] == 99)
0093         return true;
0094 
0095     return false;
0096 }
0097 
0098 int init_dell_smbios_smm(void)
0099 {
0100     int ret;
0101     /*
0102      * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
0103      * is passed to SMI handler.
0104      */
0105     ret = dcdbas_smi_alloc(&smi_buf, PAGE_SIZE);
0106     if (ret)
0107         return ret;
0108     buffer = (void *)smi_buf.virt;
0109 
0110     dmi_walk(find_cmd_address, NULL);
0111 
0112     if (test_wsmt_enabled()) {
0113         pr_debug("Disabling due to WSMT enabled\n");
0114         ret = -ENODEV;
0115         goto fail_wsmt;
0116     }
0117 
0118     platform_device = platform_device_alloc("dell-smbios", 1);
0119     if (!platform_device) {
0120         ret = -ENOMEM;
0121         goto fail_platform_device_alloc;
0122     }
0123 
0124     ret = platform_device_add(platform_device);
0125     if (ret)
0126         goto fail_platform_device_add;
0127 
0128     ret = dell_smbios_register_device(&platform_device->dev,
0129                       &dell_smbios_smm_call);
0130     if (ret)
0131         goto fail_register;
0132 
0133     return 0;
0134 
0135 fail_register:
0136     platform_device_del(platform_device);
0137 
0138 fail_platform_device_add:
0139     platform_device_put(platform_device);
0140 
0141 fail_wsmt:
0142 fail_platform_device_alloc:
0143     dcdbas_smi_free(&smi_buf);
0144     return ret;
0145 }
0146 
0147 void exit_dell_smbios_smm(void)
0148 {
0149     if (platform_device) {
0150         dell_smbios_unregister_device(&platform_device->dev);
0151         platform_device_unregister(platform_device);
0152         dcdbas_smi_free(&smi_buf);
0153     }
0154 }