Hi, On 3/18/22 16:09, Juergen Gross wrote: > 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. > > Cc: stable@xxxxxxxxxxxxxxx > Signed-off-by: Juergen Gross <jgross@xxxxxxxx> Thank you for your patch, I've applied this patch to my review-hans branch: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/log/?h=review-hans Note it will show up in my review-hans branch once I've pushed my local branch there, which might take a while. Once I've run some tests on this branch the patches there will be added to the platform-drivers-x86/for-next branch and eventually will be included in the pdx86 pull-request to Linus for the next merge-window. Regards, Hans > --- > drivers/platform/x86/dell/dcdbas.c | 127 +++++++++++--------- > drivers/platform/x86/dell/dcdbas.h | 9 ++ > drivers/platform/x86/dell/dell-smbios-smm.c | 14 ++- > 3 files changed, 87 insertions(+), 63 deletions(-) > > diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c > index 5e63d6225048..02bcac619018 100644 > --- a/drivers/platform/x86/dell/dcdbas.c > +++ b/drivers/platform/x86/dell/dcdbas.c > @@ -40,13 +40,10 @@ > > 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 *bios_buffer; > +static struct smi_buffer smi_buf; > > static unsigned int host_control_action; > static unsigned int host_control_smi_type; > @@ -54,23 +51,49 @@ static unsigned int host_control_on_shutdown; > > static bool wsmt_enabled; > > +int dcdbas_smi_alloc(struct smi_buffer *smi_buffer, unsigned long size) > +{ > + smi_buffer->virt = dma_alloc_coherent(&dcdbas_pdev->dev, size, > + &smi_buffer->dma, GFP_KERNEL); > + if (!smi_buffer->virt) { > + dev_dbg(&dcdbas_pdev->dev, > + "%s: failed to allocate memory size %lu\n", > + __func__, size); > + return -ENOMEM; > + } > + smi_buffer->size = size; > + > + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", > + __func__, (u32)smi_buffer->dma, smi_buffer->size); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(dcdbas_smi_alloc); > + > +void dcdbas_smi_free(struct smi_buffer *smi_buffer) > +{ > + if (!smi_buffer->virt) > + return; > + > + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", > + __func__, (u32)smi_buffer->dma, smi_buffer->size); > + dma_free_coherent(&dcdbas_pdev->dev, smi_buffer->size, > + smi_buffer->virt, smi_buffer->dma); > + smi_buffer->virt = NULL; > + smi_buffer->dma = 0; > + smi_buffer->size = 0; > +} > +EXPORT_SYMBOL_GPL(dcdbas_smi_free); > + > /** > * smi_data_buf_free: free SMI data buffer > */ > static void smi_data_buf_free(void) > { > - if (!smi_data_buf || wsmt_enabled) > + if (!smi_buf.virt || wsmt_enabled) > return; > > - dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", > - __func__, smi_data_buf_phys_addr, smi_data_buf_size); > - > - dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, > - smi_data_buf_handle); > - smi_data_buf = NULL; > - smi_data_buf_handle = 0; > - smi_data_buf_phys_addr = 0; > - smi_data_buf_size = 0; > + dcdbas_smi_free(&smi_buf); > } > > /** > @@ -78,39 +101,29 @@ static void smi_data_buf_free(void) > */ > static int smi_data_buf_realloc(unsigned long size) > { > - void *buf; > - dma_addr_t handle; > + struct smi_buffer tmp; > + int ret; > > - if (smi_data_buf_size >= size) > + if (smi_buf.size >= size) > return 0; > > if (size > max_smi_data_buf_size) > return -EINVAL; > > /* new buffer is needed */ > - buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL); > - if (!buf) { > - dev_dbg(&dcdbas_pdev->dev, > - "%s: failed to allocate memory size %lu\n", > - __func__, size); > - return -ENOMEM; > - } > - /* memory zeroed by dma_alloc_coherent */ > + ret = dcdbas_smi_alloc(&tmp, size); > + if (ret) > + return ret; > > - if (smi_data_buf) > - memcpy(buf, smi_data_buf, smi_data_buf_size); > + /* memory zeroed by dma_alloc_coherent */ > + if (smi_buf.virt) > + memcpy(tmp.virt, smi_buf.virt, smi_buf.size); > > /* free any existing buffer */ > smi_data_buf_free(); > > /* set up new buffer for use */ > - smi_data_buf = buf; > - smi_data_buf_handle = handle; > - smi_data_buf_phys_addr = (u32) virt_to_phys(buf); > - smi_data_buf_size = size; > - > - dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", > - __func__, smi_data_buf_phys_addr, smi_data_buf_size); > + smi_buf = tmp; > > return 0; > } > @@ -119,14 +132,14 @@ static ssize_t smi_data_buf_phys_addr_show(struct device *dev, > struct device_attribute *attr, > char *buf) > { > - return sprintf(buf, "%x\n", smi_data_buf_phys_addr); > + return sprintf(buf, "%x\n", (u32)smi_buf.dma); > } > > static ssize_t smi_data_buf_size_show(struct device *dev, > struct device_attribute *attr, > char *buf) > { > - return sprintf(buf, "%lu\n", smi_data_buf_size); > + return sprintf(buf, "%lu\n", smi_buf.size); > } > > static ssize_t smi_data_buf_size_store(struct device *dev, > @@ -155,8 +168,8 @@ static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, > ssize_t ret; > > mutex_lock(&smi_data_lock); > - ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, > - smi_data_buf_size); > + ret = memory_read_from_buffer(buf, count, &pos, smi_buf.virt, > + smi_buf.size); > mutex_unlock(&smi_data_lock); > return ret; > } > @@ -176,7 +189,7 @@ static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, > if (ret) > goto out; > > - memcpy(smi_data_buf + pos, buf, count); > + memcpy(smi_buf.virt + pos, buf, count); > ret = count; > out: > mutex_unlock(&smi_data_lock); > @@ -306,11 +319,11 @@ static ssize_t smi_request_store(struct device *dev, > > mutex_lock(&smi_data_lock); > > - if (smi_data_buf_size < sizeof(struct smi_cmd)) { > + if (smi_buf.size < sizeof(struct smi_cmd)) { > ret = -ENODEV; > goto out; > } > - smi_cmd = (struct smi_cmd *)smi_data_buf; > + smi_cmd = (struct smi_cmd *)smi_buf.virt; > > switch (val) { > case 2: > @@ -326,20 +339,20 @@ static ssize_t smi_request_store(struct device *dev, > * 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 > + * Because the address that smi_cmd (smi_buf.virt) 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 + > + smi_cmd->ebx = (u32)smi_buf.dma + > offsetof(struct smi_cmd, command_buffer); > ret = dcdbas_smi_request(smi_cmd); > if (!ret) > ret = count; > break; > case 0: > - memset(smi_data_buf, 0, smi_data_buf_size); > + memset(smi_buf.virt, 0, smi_buf.size); > ret = count; > break; > default: > @@ -356,7 +369,7 @@ EXPORT_SYMBOL(dcdbas_smi_request); > /** > * host_control_smi: generate host control SMI > * > - * Caller must set up the host control command in smi_data_buf. > + * Caller must set up the host control command in smi_buf.virt. > */ > static int host_control_smi(void) > { > @@ -367,14 +380,14 @@ static int host_control_smi(void) > s8 cmd_status; > u8 index; > > - apm_cmd = (struct apm_cmd *)smi_data_buf; > + apm_cmd = (struct apm_cmd *)smi_buf.virt; > apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; > > switch (host_control_smi_type) { > case HC_SMITYPE_TYPE1: > spin_lock_irqsave(&rtc_lock, flags); > /* write SMI data buffer physical address */ > - data = (u8 *)&smi_data_buf_phys_addr; > + data = (u8 *)&smi_buf.dma; > for (index = PE1300_CMOS_CMD_STRUCT_PTR; > index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); > index++, data++) { > @@ -405,7 +418,7 @@ static int host_control_smi(void) > case HC_SMITYPE_TYPE3: > spin_lock_irqsave(&rtc_lock, flags); > /* write SMI data buffer physical address */ > - data = (u8 *)&smi_data_buf_phys_addr; > + data = (u8 *)&smi_buf.dma; > for (index = PE1400_CMOS_CMD_STRUCT_PTR; > index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); > index++, data++) { > @@ -450,7 +463,7 @@ static int host_control_smi(void) > * This function is called by the driver after the system has > * finished shutting down if the user application specified a > * host control action to perform on shutdown. It is safe to > - * use smi_data_buf at this point because the system has finished > + * use smi_buf.virt at this point because the system has finished > * shutting down and no userspace apps are running. > */ > static void dcdbas_host_control(void) > @@ -464,18 +477,18 @@ static void dcdbas_host_control(void) > action = host_control_action; > host_control_action = HC_ACTION_NONE; > > - if (!smi_data_buf) { > + if (!smi_buf.virt) { > dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); > return; > } > > - if (smi_data_buf_size < sizeof(struct apm_cmd)) { > + if (smi_buf.size < sizeof(struct apm_cmd)) { > dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", > __func__); > return; > } > > - apm_cmd = (struct apm_cmd *)smi_data_buf; > + apm_cmd = (struct apm_cmd *)smi_buf.virt; > > /* power off takes precedence */ > if (action & HC_ACTION_HOST_CONTROL_POWEROFF) { > @@ -583,11 +596,11 @@ static int dcdbas_check_wsmt(void) > return -ENOMEM; > } > > - /* First 8 bytes is for a semaphore, not part of the smi_data_buf */ > - smi_data_buf_phys_addr = bios_buf_paddr + 8; > - smi_data_buf = bios_buffer + 8; > - smi_data_buf_size = remap_size - 8; > - max_smi_data_buf_size = smi_data_buf_size; > + /* First 8 bytes is for a semaphore, not part of the smi_buf.virt */ > + smi_buf.dma = bios_buf_paddr + 8; > + smi_buf.virt = bios_buffer + 8; > + smi_buf.size = remap_size - 8; > + max_smi_data_buf_size = smi_buf.size; > wsmt_enabled = true; > dev_info(&dcdbas_pdev->dev, > "WSMT found, using firmware-provided SMI buffer.\n"); > diff --git a/drivers/platform/x86/dell/dcdbas.h b/drivers/platform/x86/dell/dcdbas.h > index c3cca5433525..942a23ddded0 100644 > --- a/drivers/platform/x86/dell/dcdbas.h > +++ b/drivers/platform/x86/dell/dcdbas.h > @@ -105,5 +105,14 @@ struct smm_eps_table { > u64 num_of_4k_pages; > } __packed; > > +struct smi_buffer { > + u8 *virt; > + unsigned long size; > + dma_addr_t dma; > +}; > + > +int dcdbas_smi_alloc(struct smi_buffer *smi_buffer, unsigned long size); > +void dcdbas_smi_free(struct smi_buffer *smi_buffer); > + > #endif /* _DCDBAS_H_ */ > > diff --git a/drivers/platform/x86/dell/dell-smbios-smm.c b/drivers/platform/x86/dell/dell-smbios-smm.c > index 320c032418ac..4d375985c85f 100644 > --- a/drivers/platform/x86/dell/dell-smbios-smm.c > +++ b/drivers/platform/x86/dell/dell-smbios-smm.c > @@ -20,6 +20,7 @@ > > 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); > @@ -57,7 +58,7 @@ static int dell_smbios_smm_call(struct calling_interface_buffer *input) > command.magic = SMI_CMD_MAGIC; > command.command_address = da_command_address; > command.command_code = da_command_code; > - command.ebx = virt_to_phys(buffer); > + command.ebx = smi_buf.dma; > command.ecx = 0x42534931; > > mutex_lock(&smm_mutex); > @@ -101,9 +102,10 @@ int init_dell_smbios_smm(void) > * Allocate buffer below 4GB for SMI data--only 32-bit physical addr > * is passed to SMI handler. > */ > - buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); > - if (!buffer) > - return -ENOMEM; > + ret = dcdbas_smi_alloc(&smi_buf, PAGE_SIZE); > + if (ret) > + return ret; > + buffer = (void *)smi_buf.virt; > > dmi_walk(find_cmd_address, NULL); > > @@ -138,7 +140,7 @@ int init_dell_smbios_smm(void) > > fail_wsmt: > fail_platform_device_alloc: > - free_page((unsigned long)buffer); > + dcdbas_smi_free(&smi_buf); > return ret; > } > > @@ -147,6 +149,6 @@ void exit_dell_smbios_smm(void) > if (platform_device) { > dell_smbios_unregister_device(&platform_device->dev); > platform_device_unregister(platform_device); > - free_page((unsigned long)buffer); > + dcdbas_smi_free(&smi_buf); > } > }