Hi, On 11/9/22 21:00, Jorge Lopez wrote: > Hi Hans, > > On Wed, Nov 9, 2022 at 12:10 PM Hans de Goede <hdegoede@xxxxxxxxxx> wrote: >> >> Hi, >> >> On 11/9/22 18:24, Jorge Lopez wrote: >>> HI Hans, >>> >>> Please see questions and comments below. >>> >>> On Tue, Nov 8, 2022 at 8:51 AM Hans de Goede <hdegoede@xxxxxxxxxx> wrote: >>>> >>>> Hi Jorge, >>>> >>>> Review comments inline. >>>> >>>> On 10/20/22 22:10, Jorge Lopez wrote: >>>>> The purpose for this patch is submit HP BIOSCFG driver to be list of >>>>> HP Linux kernel drivers. The driver include a total of 12 files >>>>> broken in several patches. This is set 1 of 4. >>>>> >>>>> HP BIOS Configuration driver purpose is to provide a driver supporting >>>>> the latest sysfs class firmware attributes framework allowing the user >>>>> to change BIOS settings and security solutions on HP Inc.’s commercial >>>>> notebooks. >>>>> >>>>> Many features of HP Commercial PC’s can be managed using Windows >>>>> Management Instrumentation (WMI). WMI is an implementation of Web-Based >>>>> Enterprise Management (WBEM) that provides a standards-based interface >>>>> for changing and monitoring system settings. HP BISOCFG driver provides >>>>> a native Linux solution and the exposed features facilitates the >>>>> migration to Linux environments. >>>>> >>>>> The Linux security features to be provided in hp-bioscfg driver enables >>>>> managing the BIOS settings and security solutions via sysfs, a virtual >>>>> filesystem that can be used by user-mode applications. The new >>>>> documentation cover features such Secure Platform Management, Sure >>>>> Admin, and Sure Start. Each section provides security feature >>>>> description and identifies sysfs directories and files exposed by >>>>> the driver. >>>>> >>>>> Many HP Commercial PC’s include a feature called Secure Platform >>>>> Management (SPM), which replaces older password-based BIOS settings >>>>> management with public key cryptography. PC secure product management >>>>> begins when a target system is provisioned with cryptographic keys >>>>> that are used to ensure the integrity of communications between system >>>>> management utilities and the BIOS. >>>>> >>>>> HP Commercial PC’s have several BIOS settings that control its behaviour >>>>> and capabilities, many of which are related to security. To prevent >>>>> unauthorized changes to these settings, the system can be configured >>>>> to use a Sure Admin cryptographic signature-based authorization string >>>>> that the BIOS will use to verify authorization to modify the setting. >>>>> >>>>> Signed-off-by: Jorge Lopez <jorge.lopez2@xxxxxx> >>>>> >>>>> --- >>>>> Based on the latest platform-drivers-x86.git/for-next >>>>> --- >>>>> .../x86/hp/hp-bioscfg/biosattr-interface.c | 285 ++++++++ >>>>> drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 671 ++++++++++++++++++ >>>>> .../x86/hp/hp-bioscfg/enum-attributes.c | 521 ++++++++++++++ >>>>> 3 files changed, 1477 insertions(+) >>>>> create mode 100644 drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c >>>>> create mode 100644 drivers/platform/x86/hp/hp-bioscfg/bioscfg.h >>>>> create mode 100644 drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c >>>>> >>>>> diff --git a/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c b/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c >>>>> new file mode 100644 >>>>> index 000000000000..f0c919bf3ab0 >>>>> --- /dev/null >>>>> +++ b/drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c >>>>> @@ -0,0 +1,285 @@ >>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>> +/* >>>>> + * Functions corresponding to methods under BIOS interface GUID >>>>> + * for use with hp-bioscfg driver. >>>>> + * >>>>> + * Copyright (c) 2022 Hewlett-Packard Inc. >>>>> + */ >>>>> + >>>>> +#include <linux/wmi.h> >>>>> +#include "bioscfg.h" >>>>> + >>>>> +#define SET_DEFAULT_VALUES_METHOD_ID 0x02 >>>>> +#define SET_BIOS_DEFAULTS_METHOD_ID 0x03 >>>>> +#define SET_ATTRIBUTE_METHOD_ID 0x04 >>>>> + >>>>> +/* >>>>> + * set_attribute() - Update an attribute value >>>>> + * @a_name: The attribute name >>>>> + * @a_value: The attribute value >>>>> + * >>>>> + * Sets an attribute to new value >>>>> + */ >>>>> +int hp_set_attribute(const char *a_name, const char *a_value) >>>>> +{ >>>>> + size_t security_area_size; >>>>> + size_t a_name_size, a_value_size; >>>>> + u16 *buffer = NULL; >>>>> + u16 *start = NULL; >>>>> + int buffer_size; >>>> >>>> You have 2 spaces between int and buffer_size here, please drop >>>> one. >>>> >>>> >>>>> + int ret; >>>>> + int instance; >>>>> + char *auth_empty_value = " "; >>>>> + >>>>> + mutex_lock(&bioscfg_drv.mutex); >>>>> + if (!bioscfg_drv.bios_attr_wdev) { >>>>> + ret = -ENODEV; >>>>> + goto out_set_attribute; >>>>> + } >>>>> + >>>>> + instance = get_password_instance_for_type(SETUP_PASSWD); >>>>> + if (instance < 0) >>>>> + goto out_set_attribute; >>>>> + >>>>> + if (strlen(bioscfg_drv.password_data[instance].current_password) == 0) >>>>> + strncpy(bioscfg_drv.password_data[instance].current_password, >>>>> + auth_empty_value, >>>>> + sizeof(bioscfg_drv.password_data[instance].current_password)); >>>> >>>> strncpy does not guarantee 0 termination of the destination buffer, >>>> please use strscpy. >>>> >>>>> + >>>>> + a_name_size = calculate_string_buffer(a_name); >>>>> + a_value_size = calculate_string_buffer(a_value); >>>>> + security_area_size = calculate_security_buffer(bioscfg_drv.password_data[instance].current_password); >>>>> + buffer_size = a_name_size + a_value_size + security_area_size; >>>>> + >>>>> + buffer = kzalloc(buffer_size, GFP_KERNEL); >>>>> + if (!buffer) { >>>>> + ret = -ENOMEM; >>>>> + goto out_set_attribute; >>>>> + } >>>>> + >>>>> + /* build variables to set */ >>>>> + start = buffer; >>>>> + start = ascii_to_utf16_unicode(start, a_name); >>>>> + if (!start) >>>>> + goto out_set_attribute; >>>>> + >>>>> + start = ascii_to_utf16_unicode(start, a_value); >>>>> + if (!start) >>>>> + goto out_set_attribute; >>>>> + >>>>> + populate_security_buffer(start, bioscfg_drv.password_data[instance].current_password); >>>>> + ret = hp_wmi_set_bios_setting(buffer, buffer_size); >>>>> + >>>>> + >>>>> +out_set_attribute: >>>>> + kfree(buffer); >>>>> + mutex_unlock(&bioscfg_drv.mutex); >>>>> + return ret; >>>>> +} >>>>> + >>>>> +/* >>>>> + * hp_wmi_perform_query >>>>> + * >>>>> + * query: The commandtype (enum hp_wmi_commandtype) >>>>> + * write: The command (enum hp_wmi_command) >>>>> + * buffer: Buffer used as input and/or output >>>>> + * insize: Size of input buffer >>>>> + * outsize: Size of output buffer >>>>> + * >>>>> + * returns zero on success >>>>> + * an HP WMI query specific error code (which is positive) >>>>> + * -EINVAL if the query was not successful at all >>>>> + * -EINVAL if the output buffer size exceeds buffersize >>>>> + * >>>>> + * Note: The buffersize must at least be the maximum of the input and output >>>>> + * size. E.g. Battery info query is defined to have 1 byte input >>>>> + * and 128 byte output. The caller would do: >>>>> + * buffer = kzalloc(128, GFP_KERNEL); >>>>> + * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128) >>>>> + */ >>>>> +int hp_wmi_perform_query(int query, enum hp_wmi_command command, void *buffer, int insize, int outsize) >>>>> +{ >>>>> + struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; >>>>> + struct bios_return *bios_return; >>>>> + union acpi_object *obj = NULL; >>>>> + struct bios_args *args = NULL; >>>>> + int mid, actual_insize, actual_outsize; >>>>> + size_t bios_args_size; >>>>> + int ret; >>>>> + >>>>> + mid = encode_outsize_for_pvsz(outsize); >>>>> + if (WARN_ON(mid < 0)) >>>>> + return mid; >>>>> + >>>>> + actual_insize = insize; >>>>> + bios_args_size = struct_size(args, data, insize); >>>>> + args = kmalloc(bios_args_size, GFP_KERNEL); >>>>> + if (!args) >>>>> + return -ENOMEM; >>>>> + >>>>> + input.length = bios_args_size; >>>>> + input.pointer = args; >>>>> + >>>>> + args->signature = 0x55434553; >>>>> + args->command = command; >>>>> + args->commandtype = query; >>>>> + args->datasize = insize; >>>>> + memcpy(args->data, buffer, flex_array_size(args, data, insize)); >>>>> + >>>>> + ret = wmi_evaluate_method(HP_WMI_BIOS_GUID, 0, mid, &input, &output); >>>>> + bioscfg_drv.last_wmi_status = ret; >>>>> + if (ret) >>>>> + goto out_free; >>>>> + >>>>> + obj = output.pointer; >>>>> + if (!obj) { >>>>> + ret = -EINVAL; >>>>> + goto out_free; >>>>> + } >>>>> + >>>> >>>> You need to check the type of obj here before dereferencing >>>> obj as if it is a buffer. >>>> >>>>> + bios_return = (struct bios_return *)obj->buffer.pointer; >>>>> + ret = bios_return->return_code; >>>>> + bioscfg_drv.last_wmi_status = ret; >>>>> + if (ret) { >>>>> + if (ret != HPWMI_RET_UNKNOWN_COMMAND && >>>>> + ret != HPWMI_RET_UNKNOWN_CMDTYPE) >>>>> + pr_warn("query 0x%x returned error 0x%x\n", query, ret); >>>>> + goto out_free; >>>>> + } >>>>> + >>>>> + /* Ignore output data of zero size */ >>>>> + if (!outsize) >>>>> + goto out_free; >>>>> + >>>>> + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); >>>>> + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); >>>>> + memset(buffer + actual_outsize, 0, outsize - actual_outsize); >>>>> + >>>>> +out_free: >>>>> + kfree(obj); >>>>> + kfree(args); >>>>> + return ret; >>>>> +} >>>>> + >>>>> +/* >>>>> + * ascii_to_utf16_unicode - Convert ascii string to UTF-16 unicode >>>>> + * >>>>> + * @p: Unicode buffer address >>>>> + * @str: string to convert to unicode >>>>> + * >>>>> + * Returns a void pointer to the buffer containing unicode string >>>>> + */ >>>>> +void *ascii_to_utf16_unicode(u16 *p, const u8 *str) >>>>> +{ >>>>> + int len = strlen(str); >>>>> + int ret; >>>>> + >>>>> + /* >>>>> + * Add null character when reading an empty string >>>>> + */ >>>>> + if (len == 0) { >>>>> + *p++ = 2; >>>>> + *p++ = (u8)0x00; >>>>> + return p; >>>> >>>> This does not match with calculate_string_buffer() which will >>>> return 2 for a 0 length string while you are using 4 bytes here. >>>> >>>> I guess this may also be why you need to use " " for >>>> auth_empty_value above, so as to avoid this bug. >>>> >>> HP BIOS expects 2 characters when an empty string is being converted >>> to u16 hence the reason for returning 2 instead of zero. This is an >>> intended behavior and needed when allocating a buffer and writing to >>> BIOS. >> >> Right I understand that, it wants 2 characters for the 16 bit length >> word, but why not write 0 to that 16 bit length word. Why actually >> say the string-buffer length is 2 bytes long / and then write >> a 16-bit word with value 0? >> >> What you are doing now creates a 4 byte buffer like this: >> >> u8 buf[4] = { 0x02, 0x00, 0x00, 0x00 } >> >> Why not just create a 2 byte buffer like this: >> >> u8 buf[2] = { 0x00, 0x00 } >> >> ? >> >> >> Also I'm wondering why the empty auth string is " " and >> not "" ? >> >> > > The string returned for an empty string is 4 bytes. The returned > string includes two bytes for the string size in bytes and the > remaining bytes are the string. > Size = 0x02, 0x00 > String = 0x00, 0x00 > > All strings return include the string size in bytes followed by the u16 string Right I understand that, but why is the "String = 0x00, 0x00" there ? All the non-0-length strings are not 0 terminated, why does the zero length string needs to be specified as length 2 (1 u16) and then have that u16 be a 0 terminator ? Have you tried just using Size[2] = 0x00, 0x00 and String[0] for an empty string? ### Also you did not answer this separate question: I'm wondering why the empty auth string is " " and not "" ? Regards, Hans