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

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

DEFINITIONS

This source file includes following definitions.
  1. parse_da_table
  2. find_cmd_address
  3. dell_smbios_smm_call
  4. test_wsmt_enabled
  5. init_dell_smbios_smm
  6. exit_dell_smbios_smm

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

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