[PATCH 2/5] firmware: dcdbas: Add support for WSMT ACPI table
From: Stuart Hayes If the WSMT ACPI table is present and indicates that a fixed communication buffer should be used, use the firmware-specified buffer instead of allocating a buffer in memory for communications between the dcdbas driver and firmare. Signed-off-by: Stuart Hayes --- This patch has been discussed previously, see history at https://lore.kernel.org/patchwork/patch/958893/ drivers/firmware/dcdbas.c | 123 -- drivers/firmware/dcdbas.h | 10 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 0bdea60c65dd..ae28e48ff7dc 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -41,7 +42,7 @@ #include "dcdbas.h" #define DRIVER_NAME"dcdbas" -#define DRIVER_VERSION "5.6.0-3.2" +#define DRIVER_VERSION "5.6.0-3.3" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" static struct platform_device *dcdbas_pdev; @@ -49,19 +50,23 @@ static struct platform_device *dcdbas_pdev; static u8 *smi_data_buf; static dma_addr_t smi_data_buf_handle; static unsigned long smi_data_buf_size; +static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; static u32 smi_data_buf_phys_addr; static DEFINE_MUTEX(smi_data_lock); +static u8 *eps_buffer; static unsigned int host_control_action; static unsigned int host_control_smi_type; static unsigned int host_control_on_shutdown; +static bool wsmt_enabled; + /** * smi_data_buf_free: free SMI data buffer */ static void smi_data_buf_free(void) { - if (!smi_data_buf) + if (!smi_data_buf || wsmt_enabled) return; dev_dbg(_pdev->dev, "%s: phys: %x size: %lu\n", @@ -86,7 +91,7 @@ static int smi_data_buf_realloc(unsigned long size) if (smi_data_buf_size >= size) return 0; - if (size > MAX_SMI_DATA_BUF_SIZE) + if (size > max_smi_data_buf_size) return -EINVAL; /* new buffer is needed */ @@ -169,7 +174,7 @@ static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, { ssize_t ret; - if ((pos + count) > MAX_SMI_DATA_BUF_SIZE) + if ((pos + count) > max_smi_data_buf_size) return -EINVAL; mutex_lock(_data_lock); @@ -322,8 +327,20 @@ static ssize_t smi_request_store(struct device *dev, ret = count; break; case 1: - /* Calling Interface SMI */ - smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); + /* +* Calling Interface SMI +* +* Provide physical address of command buffer field within +* the struct smi_cmd to BIOS. +* +* Because the address that smi_cmd (smi_data_buf) points to +* will be from memremap() of a non-memory address if WSMT +* is present, we can't use virt_to_phys() on smi_cmd, so +* we have to use the physical address that was saved when +* the virtual address for smi_cmd was received. +*/ + smi_cmd->ebx = smi_data_buf_phys_addr + + offsetof(struct smi_cmd, command_buffer); ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; @@ -482,6 +499,93 @@ static void dcdbas_host_control(void) } } +/* WSMT */ + +static u8 checksum(u8 *buffer, u8 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum += *buffer++; + return sum; +} + +static inline struct smm_eps_table *check_eps_table(u8 *addr) +{ + struct smm_eps_table *eps = (struct smm_eps_table *)addr; + + if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) + return NULL; + + if (checksum(addr, eps->length) != 0) + return NULL; + + return eps; +} + +static int dcdbas_check_wsmt(void) +{ + struct acpi_table_wsmt *wsmt = NULL; + struct smm_eps_table *eps = NULL; + u64 remap_size; + u8 *addr; + + acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)); + if (!wsmt) + return 0; + + /* Check if WSMT ACPI table shows that protection is enabled */ + if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || + !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) + return 0; + + /* Scan for EPS (entry point structure) */ + for (addr = (u8 *)__va(0xf); +addr < (u8 *)__va(0x10 - sizeof(struct smm_eps_table)); +addr += 16) { + eps = check_eps_table(addr); + if (eps) + break; +
[PATCH 2/5] firmware: dcdbas: Add support for WSMT ACPI table
From: Stuart Hayes If the WSMT ACPI table is present and indicates that a fixed communication buffer should be used, use the firmware-specified buffer instead of allocating a buffer in memory for communications between the dcdbas driver and firmare. Signed-off-by: Stuart Hayes --- This patch has been discussed previously, see history at https://lore.kernel.org/patchwork/patch/958893/ drivers/firmware/dcdbas.c | 123 -- drivers/firmware/dcdbas.h | 10 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 0bdea60c65dd..ae28e48ff7dc 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -41,7 +42,7 @@ #include "dcdbas.h" #define DRIVER_NAME"dcdbas" -#define DRIVER_VERSION "5.6.0-3.2" +#define DRIVER_VERSION "5.6.0-3.3" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" static struct platform_device *dcdbas_pdev; @@ -49,19 +50,23 @@ static struct platform_device *dcdbas_pdev; static u8 *smi_data_buf; static dma_addr_t smi_data_buf_handle; static unsigned long smi_data_buf_size; +static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; static u32 smi_data_buf_phys_addr; static DEFINE_MUTEX(smi_data_lock); +static u8 *eps_buffer; static unsigned int host_control_action; static unsigned int host_control_smi_type; static unsigned int host_control_on_shutdown; +static bool wsmt_enabled; + /** * smi_data_buf_free: free SMI data buffer */ static void smi_data_buf_free(void) { - if (!smi_data_buf) + if (!smi_data_buf || wsmt_enabled) return; dev_dbg(_pdev->dev, "%s: phys: %x size: %lu\n", @@ -86,7 +91,7 @@ static int smi_data_buf_realloc(unsigned long size) if (smi_data_buf_size >= size) return 0; - if (size > MAX_SMI_DATA_BUF_SIZE) + if (size > max_smi_data_buf_size) return -EINVAL; /* new buffer is needed */ @@ -169,7 +174,7 @@ static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, { ssize_t ret; - if ((pos + count) > MAX_SMI_DATA_BUF_SIZE) + if ((pos + count) > max_smi_data_buf_size) return -EINVAL; mutex_lock(_data_lock); @@ -322,8 +327,20 @@ static ssize_t smi_request_store(struct device *dev, ret = count; break; case 1: - /* Calling Interface SMI */ - smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); + /* +* Calling Interface SMI +* +* Provide physical address of command buffer field within +* the struct smi_cmd to BIOS. +* +* Because the address that smi_cmd (smi_data_buf) points to +* will be from memremap() of a non-memory address if WSMT +* is present, we can't use virt_to_phys() on smi_cmd, so +* we have to use the physical address that was saved when +* the virtual address for smi_cmd was received. +*/ + smi_cmd->ebx = smi_data_buf_phys_addr + + offsetof(struct smi_cmd, command_buffer); ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; @@ -482,6 +499,93 @@ static void dcdbas_host_control(void) } } +/* WSMT */ + +static u8 checksum(u8 *buffer, u8 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum += *buffer++; + return sum; +} + +static inline struct smm_eps_table *check_eps_table(u8 *addr) +{ + struct smm_eps_table *eps = (struct smm_eps_table *)addr; + + if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) + return NULL; + + if (checksum(addr, eps->length) != 0) + return NULL; + + return eps; +} + +static int dcdbas_check_wsmt(void) +{ + struct acpi_table_wsmt *wsmt = NULL; + struct smm_eps_table *eps = NULL; + u64 remap_size; + u8 *addr; + + acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)); + if (!wsmt) + return 0; + + /* Check if WSMT ACPI table shows that protection is enabled */ + if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || + !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) + return 0; + + /* Scan for EPS (entry point structure) */ + for (addr = (u8 *)__va(0xf); +addr < (u8 *)__va(0x10 - sizeof(struct smm_eps_table)); +addr += 16) { + eps = check_eps_table(addr); + if (eps) + break; +