77089467fc
The dcdbas driver is used to call SMI handlers for both, dcdbas and dell-smbios-smm. Both drivers allocate a buffer for communicating with the SMI handler. The physical buffer address is then passed to the called SMI handler via %ebx. Unfortunately this doesn't work when running in Xen dom0, as the physical address obtained via virt_to_phys() is only a guest physical address, and not a machine physical address as needed by SMI. The problem in dcdbas is easy to correct, as dcdbas is using dma_alloc_coherent() for allocating the buffer, and the machine physical address is available via the DMA address returned in the DMA handle. In order to avoid duplicating the buffer allocation code in dell-smbios-smm, add a generic buffer allocation function to dcdbas and use it for both drivers. This is especially fine regarding driver dependencies, as dell-smbios-smm is already calling dcdbas to generate the SMI request. Signed-off-by: Juergen Gross <jgross@suse.com> Link: https://lore.kernel.org/r/20220318150950.16843-1-jgross@suse.com Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
155 lines
3.6 KiB
C
155 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* SMI methods for use with dell-smbios
|
|
*
|
|
* Copyright (c) Red Hat <mjg@redhat.com>
|
|
* Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
|
|
* Copyright (c) 2014 Pali Rohár <pali@kernel.org>
|
|
* Copyright (c) 2017 Dell Inc.
|
|
*/
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/dmi.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
#include "dcdbas.h"
|
|
#include "dell-smbios.h"
|
|
|
|
static int da_command_address;
|
|
static int da_command_code;
|
|
static struct smi_buffer smi_buf;
|
|
static struct calling_interface_buffer *buffer;
|
|
static struct platform_device *platform_device;
|
|
static DEFINE_MUTEX(smm_mutex);
|
|
|
|
static void parse_da_table(const struct dmi_header *dm)
|
|
{
|
|
struct calling_interface_structure *table =
|
|
container_of(dm, struct calling_interface_structure, header);
|
|
|
|
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
|
|
* 6 bytes of entry
|
|
*/
|
|
if (dm->length < 17)
|
|
return;
|
|
|
|
da_command_address = table->cmdIOAddress;
|
|
da_command_code = table->cmdIOCode;
|
|
}
|
|
|
|
static void find_cmd_address(const struct dmi_header *dm, void *dummy)
|
|
{
|
|
switch (dm->type) {
|
|
case 0xda: /* Calling interface */
|
|
parse_da_table(dm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int dell_smbios_smm_call(struct calling_interface_buffer *input)
|
|
{
|
|
struct smi_cmd command;
|
|
size_t size;
|
|
|
|
size = sizeof(struct calling_interface_buffer);
|
|
command.magic = SMI_CMD_MAGIC;
|
|
command.command_address = da_command_address;
|
|
command.command_code = da_command_code;
|
|
command.ebx = smi_buf.dma;
|
|
command.ecx = 0x42534931;
|
|
|
|
mutex_lock(&smm_mutex);
|
|
memcpy(buffer, input, size);
|
|
dcdbas_smi_request(&command);
|
|
memcpy(input, buffer, size);
|
|
mutex_unlock(&smm_mutex);
|
|
return 0;
|
|
}
|
|
|
|
/* When enabled this indicates that SMM won't work */
|
|
static bool test_wsmt_enabled(void)
|
|
{
|
|
struct calling_interface_token *wsmt;
|
|
|
|
/* if token doesn't exist, SMM will work */
|
|
wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
|
|
if (!wsmt)
|
|
return false;
|
|
|
|
/* If token exists, try to access over SMM but set a dummy return.
|
|
* - If WSMT disabled it will be overwritten by SMM
|
|
* - If WSMT enabled then dummy value will remain
|
|
*/
|
|
buffer->cmd_class = CLASS_TOKEN_READ;
|
|
buffer->cmd_select = SELECT_TOKEN_STD;
|
|
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
|
buffer->input[0] = wsmt->location;
|
|
buffer->output[0] = 99;
|
|
dell_smbios_smm_call(buffer);
|
|
if (buffer->output[0] == 99)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
int init_dell_smbios_smm(void)
|
|
{
|
|
int ret;
|
|
/*
|
|
* Allocate buffer below 4GB for SMI data--only 32-bit physical addr
|
|
* is passed to SMI handler.
|
|
*/
|
|
ret = dcdbas_smi_alloc(&smi_buf, PAGE_SIZE);
|
|
if (ret)
|
|
return ret;
|
|
buffer = (void *)smi_buf.virt;
|
|
|
|
dmi_walk(find_cmd_address, NULL);
|
|
|
|
if (test_wsmt_enabled()) {
|
|
pr_debug("Disabling due to WSMT enabled\n");
|
|
ret = -ENODEV;
|
|
goto fail_wsmt;
|
|
}
|
|
|
|
platform_device = platform_device_alloc("dell-smbios", 1);
|
|
if (!platform_device) {
|
|
ret = -ENOMEM;
|
|
goto fail_platform_device_alloc;
|
|
}
|
|
|
|
ret = platform_device_add(platform_device);
|
|
if (ret)
|
|
goto fail_platform_device_add;
|
|
|
|
ret = dell_smbios_register_device(&platform_device->dev,
|
|
&dell_smbios_smm_call);
|
|
if (ret)
|
|
goto fail_register;
|
|
|
|
return 0;
|
|
|
|
fail_register:
|
|
platform_device_del(platform_device);
|
|
|
|
fail_platform_device_add:
|
|
platform_device_put(platform_device);
|
|
|
|
fail_wsmt:
|
|
fail_platform_device_alloc:
|
|
dcdbas_smi_free(&smi_buf);
|
|
return ret;
|
|
}
|
|
|
|
void exit_dell_smbios_smm(void)
|
|
{
|
|
if (platform_device) {
|
|
dell_smbios_unregister_device(&platform_device->dev);
|
|
platform_device_unregister(platform_device);
|
|
dcdbas_smi_free(&smi_buf);
|
|
}
|
|
}
|