platform/x86: hp-wmi: Changing bios_args.data to be dynamically allocated
authorJorge Lopez <jorge.lopez2@hp.com>
Thu, 10 Mar 2022 21:08:53 +0000 (15:08 -0600)
committerHans de Goede <hdegoede@redhat.com>
Tue, 15 Mar 2022 09:49:37 +0000 (10:49 +0100)
The purpose of this patch is to remove 128 bytes buffer limitation
imposed in bios_args structure.

A limiting factor discovered during this investigation was the struct
bios_args.data size restriction.  The data member size limits all
possible WMI commands to those requiring buffer size of 128 bytes or
less. Several WMI commands and queries require a buffer size larger
than 128 bytes hence limiting current and feature supported by the
driver. It is for this reason, struct bios_args.data changed and is
dynamically allocated.  hp_wmi_perform_query function changed to
handle the memory allocation and release of any required buffer size.

All changes were validated on a HP ZBook Workstation notebook,
HP EliteBook x360, and HP EliteBook 850 G8.  Additional
validation was included in the test process to ensure no other
commands were incorrectly handled.

Signed-off-by: Jorge Lopez <jorge.lopez2@hp.com>
Link: https://lore.kernel.org/r/20220310210853.28367-5-jorge.lopez2@hp.com
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
drivers/platform/x86/hp-wmi.c

index 29ae35d15a41174cef08195759acf21c7f1377d1..1f9d6e1de5afee6d115509ebc16b9cdc09fa0778 100644 (file)
@@ -82,12 +82,17 @@ enum hp_wmi_event_ids {
        HPWMI_BATTERY_CHARGE_PERIOD     = 0x10,
 };
 
+/*
+ * struct bios_args buffer is dynamically allocated.  New WMI command types
+ * were introduced that exceeds 128-byte data size.  Changes to handle
+ * the data size allocation scheme were kept in hp_wmi_perform_qurey function.
+ */
 struct bios_args {
        u32 signature;
        u32 command;
        u32 commandtype;
        u32 datasize;
-       u8 data[128];
+       u8 data[];
 };
 
 enum hp_wmi_commandtype {
@@ -266,37 +271,43 @@ static inline int encode_outsize_for_pvsz(int outsize)
 static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
                                void *buffer, int insize, int outsize)
 {
-       int mid;
+       struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL };
        struct bios_return *bios_return;
-       int actual_outsize;
-       union acpi_object *obj;
-       struct bios_args args = {
-               .signature = 0x55434553,
-               .command = command,
-               .commandtype = query,
-               .datasize = insize,
-               .data = { 0 },
-       };
-       struct acpi_buffer input = { sizeof(struct bios_args), &args };
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-       int ret = 0;
+       union acpi_object *obj = NULL;
+       struct bios_args *args = NULL;
+       int mid, actual_outsize, ret;
+       size_t bios_args_size;
 
        mid = encode_outsize_for_pvsz(outsize);
        if (WARN_ON(mid < 0))
                return mid;
 
-       if (WARN_ON(insize > sizeof(args.data)))
-               return -EINVAL;
-       memcpy(&args.data[0], buffer, insize);
+       bios_args_size = struct_size(args, data, insize);
+       args = kmalloc(bios_args_size, GFP_KERNEL);
+       if (!args)
+               return -ENOMEM;
 
-       wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
+       input.length = bios_args_size;
+       input.pointer = args;
 
-       obj = output.pointer;
+       args->signature = 0x55434553;
+       args->command = command;
+       args->commandtype = query;
+       args->datasize = insize;
+       memcpy(args->data, buffer, flex_array_size(args, data, insize));
 
-       if (!obj)
-               return -EINVAL;
+       ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
+       if (ret)
+               goto out_free;
+
+       obj = output.pointer;
+       if (!obj) {
+               ret = -EINVAL;
+               goto out_free;
+       }
 
        if (obj->type != ACPI_TYPE_BUFFER) {
+               pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret);
                ret = -EINVAL;
                goto out_free;
        }
@@ -321,6 +332,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
 
 out_free:
        kfree(obj);
+       kfree(args);
        return ret;
 }