[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] platform/x86/dell: add buffer allocation/free functions for SMI calls



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);
>       }
>  }




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.