cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dell-smbios-smm.c (3693B)


      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@kernel.org>
      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
     21static int da_command_address;
     22static int da_command_code;
     23static struct smi_buffer smi_buf;
     24static struct calling_interface_buffer *buffer;
     25static struct platform_device *platform_device;
     26static DEFINE_MUTEX(smm_mutex);
     27
     28static void parse_da_table(const struct dmi_header *dm)
     29{
     30	struct calling_interface_structure *table =
     31		container_of(dm, struct calling_interface_structure, header);
     32
     33	/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
     34	 * 6 bytes of entry
     35	 */
     36	if (dm->length < 17)
     37		return;
     38
     39	da_command_address = table->cmdIOAddress;
     40	da_command_code = table->cmdIOCode;
     41}
     42
     43static void find_cmd_address(const struct dmi_header *dm, void *dummy)
     44{
     45	switch (dm->type) {
     46	case 0xda: /* Calling interface */
     47		parse_da_table(dm);
     48		break;
     49	}
     50}
     51
     52static int dell_smbios_smm_call(struct calling_interface_buffer *input)
     53{
     54	struct smi_cmd command;
     55	size_t size;
     56
     57	size = sizeof(struct calling_interface_buffer);
     58	command.magic = SMI_CMD_MAGIC;
     59	command.command_address = da_command_address;
     60	command.command_code = da_command_code;
     61	command.ebx = smi_buf.dma;
     62	command.ecx = 0x42534931;
     63
     64	mutex_lock(&smm_mutex);
     65	memcpy(buffer, input, size);
     66	dcdbas_smi_request(&command);
     67	memcpy(input, buffer, size);
     68	mutex_unlock(&smm_mutex);
     69	return 0;
     70}
     71
     72/* When enabled this indicates that SMM won't work */
     73static bool test_wsmt_enabled(void)
     74{
     75	struct calling_interface_token *wsmt;
     76
     77	/* if token doesn't exist, SMM will work */
     78	wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
     79	if (!wsmt)
     80		return false;
     81
     82	/* If token exists, try to access over SMM but set a dummy return.
     83	 * - If WSMT disabled it will be overwritten by SMM
     84	 * - If WSMT enabled then dummy value will remain
     85	 */
     86	buffer->cmd_class = CLASS_TOKEN_READ;
     87	buffer->cmd_select = SELECT_TOKEN_STD;
     88	memset(buffer, 0, sizeof(struct calling_interface_buffer));
     89	buffer->input[0] = wsmt->location;
     90	buffer->output[0] = 99;
     91	dell_smbios_smm_call(buffer);
     92	if (buffer->output[0] == 99)
     93		return true;
     94
     95	return false;
     96}
     97
     98int init_dell_smbios_smm(void)
     99{
    100	int ret;
    101	/*
    102	 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
    103	 * is passed to SMI handler.
    104	 */
    105	ret = dcdbas_smi_alloc(&smi_buf, PAGE_SIZE);
    106	if (ret)
    107		return ret;
    108	buffer = (void *)smi_buf.virt;
    109
    110	dmi_walk(find_cmd_address, NULL);
    111
    112	if (test_wsmt_enabled()) {
    113		pr_debug("Disabling due to WSMT enabled\n");
    114		ret = -ENODEV;
    115		goto fail_wsmt;
    116	}
    117
    118	platform_device = platform_device_alloc("dell-smbios", 1);
    119	if (!platform_device) {
    120		ret = -ENOMEM;
    121		goto fail_platform_device_alloc;
    122	}
    123
    124	ret = platform_device_add(platform_device);
    125	if (ret)
    126		goto fail_platform_device_add;
    127
    128	ret = dell_smbios_register_device(&platform_device->dev,
    129					  &dell_smbios_smm_call);
    130	if (ret)
    131		goto fail_register;
    132
    133	return 0;
    134
    135fail_register:
    136	platform_device_del(platform_device);
    137
    138fail_platform_device_add:
    139	platform_device_put(platform_device);
    140
    141fail_wsmt:
    142fail_platform_device_alloc:
    143	dcdbas_smi_free(&smi_buf);
    144	return ret;
    145}
    146
    147void exit_dell_smbios_smm(void)
    148{
    149	if (platform_device) {
    150		dell_smbios_unregister_device(&platform_device->dev);
    151		platform_device_unregister(platform_device);
    152		dcdbas_smi_free(&smi_buf);
    153	}
    154}