[Public] > -----Original Message----- > From: Jorge Lopez <jorgealtxwork@xxxxxxxxx> > Sent: Monday, April 4, 2022 15:36 > To: platform-driver-x86@xxxxxxxxxxxxxxx > Subject: [PATCH v1 5/6] Sure Admin Security Feature > > 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. Behind the scenes, Sure Admin uses Secure Platform > Management (SPM) and WMI > > 'settings' is a file associated with Sure Admin. BIOS settings can be > read or written through the Sure Admin settings file in sysfs > > /sys/devices/platform/hp-wmi/sure_admin/settings > > Expected data format to update BIOS setting > > [BIOS setting],[new value],[auth token] > > Sample settings reported data > > { > "Class": "HPBIOS_BIOSEnumeration", > "Name": "USB Storage Boot", > "Path": "\\Advanced\\Boot Options", > "IsReadOnly": 0, > ... > "Value": "Enable", > "Size": 2, > "PossibleValues": [ > "Disable", > "Enable" > ] > } > This sounds like it has re-invented /sys/class/firmware-attributes. Shouldn't you adopt that API? > This feature requires "Update hp_wmi_group to simplify feature > addition" patch. > > All changes were validated on a HP ZBook Workstation, > HP EliteBook x360, and HP EliteBook 850 G8 notebooks. > > Signed-off-by: Jorge Lopez <jorge.lopez2@xxxxxx> > > --- > Based on the latest platform-drivers-x86.git/for-next > --- > drivers/platform/x86/hp-wmi.c | 977 > ++++++++++++++++++++++++++++++++++ > 1 file changed, 977 insertions(+) > > diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c > index 918e3eaf1b67..b72ca18b77a6 100644 > --- a/drivers/platform/x86/hp-wmi.c > +++ b/drivers/platform/x86/hp-wmi.c > @@ -27,6 +27,7 @@ > #include <linux/rfkill.h> > #include <linux/string.h> > #include <linux/dmi.h> > +#include <linux/nls.h> > > MODULE_AUTHOR("Matthew Garrett <mjg59@xxxxxxxxxxxxx>"); > MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); > @@ -37,8 +38,16 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91- > 3D44E2C707E4"); > > #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" > #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" > + > #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 > > +#define HPWMI_STRING_GUID "988D08E3-68F4-4c35-AF3E-6A1B8106F83C" > +#define HPWMI_INTEGER_GUID "8232DE3D-663D-4327-A8F4- > E293ADB9BF05" > +#define HPWMI_ENUMERATION_GUID "2D114B49-2DFB-4130-B8FE- > 4A3C09E75133" > +#define HPWMI_ORDEREDLIST_GUID "14EA9746-CE1F-4098-A0E0- > 7045CB4DA745" > +#define HPWMI_PASSWORD_GUID "322F2028-0F84-4901-988E- > 015176049E2D" > +#define HPWMI_SETBIOSSETTING_GUID "1F4C91EB-DC5C-460b-951D- > C7CB9B4B8D5E" > + > /* DMI board names of devices that should use the omen specific path for > * thermal profiles. > * This was obtained by taking a look in the windows omen command center > @@ -1025,6 +1034,973 @@ static const struct attribute_group > sure_start_group = { > .attrs = sure_start_attrs, > }; > > + > +static int convert_hexstr_to_str(char **hex, int input_len, char **str, int > *len) > +{ > + int ret = 0; > + int new_len = 0; > + char tmp[] = "0x00"; > + char *input = *hex; > + char *new_str = NULL; > + int ch; > + int i; > + > + if (input_len <= 0 || hex == NULL || str == NULL || len == NULL) > + return -EINVAL; > + > + *len = 0; > + *str = NULL; > + > + new_str = kmalloc(input_len, GFP_KERNEL); > + if (!new_str) > + return -ENOMEM; > + > + for (i = 0; i < input_len; i += 5) { > + strncpy(tmp, input + i, strlen(tmp)); > + ret = kstrtoint(tmp, 16, &ch); > + if (ret) { > + new_len = 0; > + break; > + } > + > + if (ch == '\\') > + new_str[new_len++] = '\\'; > + > + new_str[new_len++] = ch; > + if (ch == '\0') > + break; > + } > + > + if (new_len) { > + new_str[new_len] = '\0'; > + *str = krealloc(new_str, (new_len + 1) * sizeof(char), > GFP_KERNEL); > + if (*str) > + *len = new_len; > + else > + ret = -ENOMEM; > + } > + > + if (ret) > + kfree(new_str); > + return ret; > +} > + > +/* > + * hp_wmi_get_setting_object() - Get an ACPI object by GUID and instance > + * > + * @guid: GUID associated with the ACPI list of managed objects > + * @instance: Instance index to query on the ACPI list > + * @obj: The output ACPI object of type ACPI_TYPE_PACKAGE > + * or ACPI_TYPE_BUFFER (freed by the callee) > + * > + * Returns zero on success. Otherwise,an error inherited from > + * wmi_query_block(). It returns a obj by parameter if > + * the query returned object of type buffer or package, > + * otherwise, a null obj is returned. > + * > + * Note: obj should be freed by the callee once it is finished working with it > + */ > +static int hp_wmi_get_setting_object(char *guid, int instance, > + union acpi_object **obj) > +{ > + struct acpi_buffer output = { ACPI_ALLOCATE_LOCAL_BUFFER, NULL > }; > + union acpi_object *tmp = NULL; > + int ret; > + > + ret = wmi_query_block(guid, instance, &output); > + if (ACPI_SUCCESS(ret) && output.pointer != NULL) { > + tmp = output.pointer; > + if (tmp->type == ACPI_TYPE_BUFFER || tmp->type == > ACPI_TYPE_PACKAGE) > + *obj = output.pointer; > + else { > + kfree(tmp); > + *obj = NULL; > + } > + } > + > + return ret; > +} > + > + > +static int get_string_from_buffer(u16 **buffer, char **str) > +{ > + u16 *ptr = *buffer; > + u16 ptrlen; > + > + u16 size; > + int i; > + char *output = NULL; > + int escape = 0; > + > + ptrlen = *(ptr++); > + size = ptrlen / 2; > + > + if (size == 0) > + goto cleanup_exit; > + > + for (i = 0; i < size; i++) > + if (ptr[i] == '\\') > + escape++; > + > + size += escape; > + *str = kcalloc(size + 1, sizeof(char), GFP_KERNEL); > + if (!*str) > + return -ENOMEM; > + > + output = *str; > + > + /* > + * convert from UTF-16 unicode to ASCII > + */ > + utf16s_to_utf8s(ptr, ptrlen, UTF16_HOST_ENDIAN, output, size); > + > + if (escape == 0) { > + ptr += (ptrlen / 2); > + goto cleanup_exit; > + } > + /* > + * Convert escape characters only when found > + */ > + for (i = 0; i < size; i++) { > + if (*ptr == '\\') > + output[i++] = '\\'; > + output[i] = *ptr; > + ptr++; > + } > + > +cleanup_exit: > + *buffer = ptr; > + return 0; > +} > + > +static int get_integer_from_buffer(int **buffer, int *integer) > +{ > + int *ptr = PTR_ALIGN(*buffer, 4); > + *integer = *(ptr++); > + *buffer = ptr; > + return 0; > +} > + > + > +// Sure Admin functions > +enum hp_wmi_data_type { > + HPWMI_STRING_TYPE, > + HPWMI_INTEGER_TYPE, > + HPWMI_ENUMERATION_TYPE, > + HPWMI_ORDEREDLIST_TYPE, > + HPWMI_PASSWORD_TYPE, > +}; > + > +#define HP_WMI_COMMON_ELEMENTS \ > + "Name", \ > + "Value", \ > + "Path", \ > + "IsReadOnly", \ > + "DisplayInUI", \ > + "RequiresPhysicalPresence", \ > + "Sequence", \ > + "PrerequisiteSize", \ > + "SecurityLevel" > + > +const char *hp_wmi_string_elements[] = { > + HP_WMI_COMMON_ELEMENTS, > + "MinLength", > + "MaxLength" > +}; > + > +const char *hp_wmi_integer_elements[] = { > + HP_WMI_COMMON_ELEMENTS, > + "LowerBound", > + "UpperBound", > + "IntValue" > +}; > + > +const char *hp_wmi_enumeration_elements[] = { > + HP_WMI_COMMON_ELEMENTS, > + "CurrentValue", > + "Size" > +}; > + > +const char *hp_wmi_orderedlist_elements[] = { > + HP_WMI_COMMON_ELEMENTS, > + "Size" > +}; > + > +const char *hp_wmi_password_elements[] = { > + HP_WMI_COMMON_ELEMENTS, > + "MinLength", > + "MaxLength", > + "Size", > + "SupportedEncoding", > + "IsSet" > +}; > + > +const char **hp_wmi_elements[] = { > + hp_wmi_string_elements, > + hp_wmi_integer_elements, > + hp_wmi_enumeration_elements, > + hp_wmi_orderedlist_elements, > + hp_wmi_password_elements > +}; > + > +const int hp_wmi_elements_count[] = { > + ARRAY_SIZE(hp_wmi_string_elements), > + ARRAY_SIZE(hp_wmi_integer_elements), > + ARRAY_SIZE(hp_wmi_enumeration_elements), > + ARRAY_SIZE(hp_wmi_orderedlist_elements), > + ARRAY_SIZE(hp_wmi_password_elements) > +}; > + > +const char *hp_wmi_classes[] = { > + "HPBIOS_BIOSString", > + "HPBIOS_BIOSInteger", > + "HPBIOS_BIOSEnumeration", > + "HPBIOS_BIOSOrderedList", > + "HPBIOS_BIOSPassword" > +}; > + > +static DEFINE_MUTEX(buf_mutex); > +static int settings_buffer_size; > +static int buf_alloc_size; > +static char *hp_bios_settings_buffer; > + > + > +static int append_package_elements_to_buffer(union acpi_object *obj, > + char *buf, int alloc_size, enum > hp_wmi_data_type type) > +{ > + int i; > + union acpi_object *pobj = NULL; > + char *value = NULL; > + int value_len; > + char *tmpstr = NULL; > + char *part_tmp = NULL; > + int tmp_len = 0; > + char *part = NULL; > + int status = 0; > + int size = 0; > + int buf_size; > + > + if (type >= ARRAY_SIZE(hp_wmi_classes) || !buf || !obj) > + return -EINVAL; > + > + if (obj->type != ACPI_TYPE_PACKAGE) > + return -EINVAL; > + > + buf_size = snprintf(buf, alloc_size, "%s{\n", buf); > + buf_size = snprintf(buf, alloc_size, "%s\t\"Class\": \"%s\",\n", buf, > hp_wmi_classes[type]); > + > + for (i = 0; i < 3; i++) { > + pobj = &(obj->package.elements[i]); > + if (pobj->type == ACPI_TYPE_STRING) { > + status = convert_hexstr_to_str(&pobj- > >string.pointer, > + pobj->string.length, > &value, &value_len); > + if (ACPI_FAILURE(status)) > + continue; > + /* > + * Skip 'Value' (HP_WMI_COMMON_ELEMENTS) > since > + * 'CurrentValue' is reported. > + */ > + if (type != HPWMI_ENUMERATION_TYPE || i != 1) > + buf_size = snprintf(buf, alloc_size, > + "%s\t\"%s\": \"%s\",\n", > + buf, > + > hp_wmi_elements[type][i], value); > + > + } > + kfree(value); > + value = NULL; > + } > + > + for (i = 3; i < hp_wmi_elements_count[type]; i++) { > + pobj = &(obj->package.elements[i]); > + > + if (type == HPWMI_ENUMERATION_TYPE && > + i == 9 && > + pobj->type == ACPI_TYPE_STRING) { > + /* > + * Report "CurrentValue" as "Value" > + */ > + status = convert_hexstr_to_str(&pobj- > >string.pointer, > + pobj->string.length, > + &value, &value_len); > + if (ACPI_FAILURE(status)) > + continue; > + > + buf_size = snprintf(buf, alloc_size, > + "%s\t\"Value\": \"%s\",\n", > + buf, value); > + kfree(value); > + value = NULL; > + > + } else if (type == HPWMI_PASSWORD_TYPE && > + i == 12 && > + pobj->type == ACPI_TYPE_STRING) { > + /* > + * Report list of "SupportEncoding" > + * > + * "SupportedEncoding": [ > + * "utf-16" > + * ], > + * > + */ > + > + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": [\n", > + buf, hp_wmi_elements[type][i]); > + while (size--) { > + pobj = &(obj->package.elements[i]); > + status = convert_hexstr_to_str(&pobj- > >string.pointer, > + pobj- > >string.length, > + &value, > &value_len); > + if (ACPI_FAILURE(status)) > + continue; > + > + if (size) { > + buf_size = snprintf(buf, alloc_size, > + "%s\t\t\"%s\",\n", > buf, value); > + i++; > + } else > + buf_size = snprintf(buf, alloc_size, > + "%s\t\t\"%s\"\n", > buf, value); > + > + kfree(value); > + value = NULL; > + > + } > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + continue; > + > + } else if (pobj->type == ACPI_TYPE_INTEGER) { > + /* > + * Report "PrerequisiteSize" and "Size" values > + * ... > + * "PrerequisiteSize": 1, > + * ... > + * "Size": 2, > + * ... > + */ > + if (i == 7) > + size = pobj->integer.value; > + else if (type == HPWMI_ORDEREDLIST_TYPE && i == > 9) > + size = pobj->integer.value; > + else if (type == HPWMI_ENUMERATION_TYPE && i > == 10) > + size = pobj->integer.value; > + else if (type == HPWMI_PASSWORD_TYPE && i == > 11) > + size = pobj->integer.value; > + > + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": > %lld,\n", buf, > + hp_wmi_elements[type][i], pobj- > >integer.value); > + } > + } > + > + if (type == HPWMI_ENUMERATION_TYPE) { > + buf_size = snprintf(buf, alloc_size, "%s\t\"PossibleValues\": > [\n", buf); > + for (i = 0; i < size; i++) { > + pobj = &(obj->package.elements[i + > hp_wmi_elements_count[type]]); > + > + status = convert_hexstr_to_str(&pobj- > >string.pointer, > + pobj->string.length, > + &value, &value_len); > + if (ACPI_FAILURE(status)) > + break; > + > + /* > + * Report list of "PossibleValues" of size > + * "Size" > + * ... > + * "Size": 2, > + * "PossibleValues": [ > + * "Disable", > + * "Enable"] > + */ > + if (i == (size - 1)) > + buf_size = snprintf(buf, alloc_size, > + "%s\t\t\"%s\"\n", buf, > value); > + else > + buf_size = snprintf(buf, alloc_size, > + "%s\t\t\"%s\",\n", buf, > value); > + kfree(value); > + value = NULL; > + } > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + } > + > + if (type == HPWMI_ORDEREDLIST_TYPE) { > + buf_size = snprintf(buf, alloc_size, "%s\t\"Elements\": [\n", > buf); > + if (size <= 0) > + goto finish_ordered_list; > + > + pobj = &(obj- > >package.elements[hp_wmi_elements_count[type]]); > + status = convert_hexstr_to_str(&pobj->string.pointer, > + pobj->string.length, &value, > &value_len); > + if (ACPI_FAILURE(status)) > + goto finish_ordered_list; > + > + /* > + * Ordered list data is stored in hex and comma separated > format > + * Convert the data and split it to show each element > + */ > + status = convert_hexstr_to_str(&value, value_len, &tmpstr, > &tmp_len); > + if (ACPI_FAILURE(status)) > + goto finish_ordered_list; > + > + part_tmp = tmpstr; > + part = strsep(&part_tmp, ","); > + while (part) { > + buf_size = snprintf(buf, alloc_size, "%s\t\t\"%s\"", > buf, part); > + part = strsep(&part_tmp, ","); > + if (part) > + buf_size = snprintf(buf, alloc_size, "%s,\n", > buf); > + else > + buf_size = snprintf(buf, alloc_size, "%s\n", > buf); > + } > + } > + > +finish_ordered_list: > + if (type == HPWMI_ORDEREDLIST_TYPE) > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + > + /* > + * remove trailing comma > + */ > + if (buf_size > 3) > + buf[buf_size - 2] = ' '; > + > + kfree(tmpstr); > + kfree(value); > + return snprintf(buf, alloc_size, "%s},\n", buf); > +} > + > +static int append_buffer_elements_to_buffer(union acpi_object *obj, > + char *buf, int alloc_size, enum > hp_wmi_data_type type) > +{ > + int buf_size; > + int status; > + char *str = NULL; > + int i; > + int j; > + int integer; > + int size = 0; > + > + if (type >= ARRAY_SIZE(hp_wmi_classes) || !buf || !obj) > + return -EINVAL; > + > + if (obj->type != ACPI_TYPE_BUFFER) > + return -EINVAL; > + > + buf_size = snprintf(buf, alloc_size, "%s{\n", buf); > + buf_size = snprintf(buf, alloc_size, "%s\t\"Class\": \"%s\",\n", buf, > hp_wmi_classes[type]); > + > + for (i = 0; i < 3; i++) { > + status = get_string_from_buffer((u16 **)&obj- > >buffer.pointer, &str); > + if (ACPI_SUCCESS(status)) { > + /* > + * Skip 'Value' (HP_WMI_COMMON_ELEMENTS) > since > + * 'CurrentValue' is reported. > + */ > + if (type != HPWMI_ENUMERATION_TYPE || i != 1) > + buf_size = snprintf(buf, alloc_size, > + "%s\t\"%s\": \"%s\",\n", > + buf, > + > hp_wmi_elements[type][i], str); > + } > + kfree(str); > + str = NULL; > + > + } > + > + for (i = 3; i < hp_wmi_elements_count[type]; i++) { > + if (type == HPWMI_ENUMERATION_TYPE && i == 9) { > + status = get_string_from_buffer((u16 **)&obj- > >buffer.pointer, &str); > + if (ACPI_SUCCESS(status)) { > + /* > + * Report "CurrentValue" as "Value" > + */ > + buf_size = snprintf(buf, alloc_size, > + "%s\t\"Value\": \"%s\",\n", > buf, str); > + } > + kfree(str); > + str = NULL; > + continue; > + > + } else if (type == HPWMI_PASSWORD_TYPE && i == 12) { > + /* > + * Report list of "SupportEncoding" > + * > + * "SupportedEncoding": [ > + * "utf-16" > + * ], > + * > + */ > + > + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": [\n", > + buf, hp_wmi_elements[type][i]); > + for (j = 0; j < size; j++) { > + status = get_string_from_buffer((u16 > **)&obj->buffer.pointer, &str); > + if (ACPI_SUCCESS(status)) { > + if (j == size - 1) > + buf_size = snprintf(buf, > alloc_size, > + > "%s\t\t\"%s\"\n", buf, str); > + else > + buf_size = snprintf(buf, > alloc_size, > + > "%s\t\t\"%s\",\n", buf, str); > + } > + kfree(str); > + str = NULL; > + } > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + continue; > + } > + > + size = 0; > + status = get_integer_from_buffer((int **)&obj- > >buffer.pointer, &integer); > + if (ACPI_SUCCESS(status)) { > + /* > + * Report "PrerequisiteSize" and "Size" values > + * ... > + * "PrerequisiteSize": 1, > + * ... > + * "Size": 2, > + * ... > + */ > + if (i == 7) > + size = integer; > + else if (type == HPWMI_ENUMERATION_TYPE && i > == 10) > + size = integer; > + else if (type == HPWMI_ORDEREDLIST_TYPE && i == > 9) > + size = integer; > + else if (type == HPWMI_PASSWORD_TYPE && i == > 11) > + size = integer; > + > + buf_size = snprintf(buf, alloc_size, "%s\t\"%s\": > %d,\n", buf, > + hp_wmi_elements[type][i], > integer); > + } > + > + if (size > 20) > + pr_warn("%s exceeded the maximum number of > elements supported or data may be malformed\n", > + hp_wmi_elements[type][i]); > + > + if (ACPI_SUCCESS(status) && i == 7) { > + buf_size = snprintf(buf, alloc_size, > "%s\t\"Prerequisites\": [\n", buf); > + for (j = 0; j < size; j++) { > + status = get_string_from_buffer((u16 > **)&obj->buffer.pointer, &str); > + if (ACPI_SUCCESS(status)) { > + buf_size = snprintf(buf, alloc_size, > "%s\t\t\"%s\"", buf, str); > + > + if (j == size - 1) > + buf_size = snprintf(buf, > alloc_size, "%s\n", buf); > + else > + buf_size = snprintf(buf, > alloc_size, "%s,\n", buf); > + > + } > + kfree(str); > + str = NULL; > + } > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + } > + } > + > + if (type == HPWMI_ENUMERATION_TYPE || type == > HPWMI_ORDEREDLIST_TYPE) { > + if (type == HPWMI_ENUMERATION_TYPE) > + buf_size = snprintf(buf, alloc_size, > "%s\t\"PossibleValues\": [\n", buf); > + else > + buf_size = snprintf(buf, alloc_size, > "%s\t\"Elements\": [\n", buf); > + > + for (i = 0; i < size; i++) { > + status = get_string_from_buffer((u16 **)&obj- > >buffer.pointer, &str); > + if (ACPI_SUCCESS(status)) { > + buf_size = snprintf(buf, alloc_size, > "%s\t\t\"%s\"", buf, str); > + > + if (i == size - 1) > + buf_size = snprintf(buf, alloc_size, > "%s\n", buf); > + else > + buf_size = snprintf(buf, alloc_size, > "%s,\n", buf); > + > + } > + kfree(str); > + str = NULL; > + } > + buf_size = snprintf(buf, alloc_size, "%s\t],\n", buf); > + } > + > + /* > + * remove trailing comma > + */ > + if (buf_size > 3) > + buf[buf_size - 2] = ' '; > + > + return snprintf(buf, alloc_size, "%s},\n", buf); > +} > + > +static int hp_bios_settings_free_buffer(void) > +{ > + mutex_lock(&buf_mutex); > + kfree(hp_bios_settings_buffer); > + settings_buffer_size = 0; > + buf_alloc_size = 0; > + mutex_unlock(&buf_mutex); > + > + return 0; > +} > + > +static int hp_bios_settings_realloc_buffer(char **buf, int *buf_size, > + int *alloc_size, > + struct mutex *buf_mutex) > +{ > + int new_buffer_size; > + char *new_buf = NULL; > + int ret = 0; > + > + if (*buf_size + PAGE_SIZE >= *alloc_size) { > + new_buffer_size = buf_alloc_size + 2 * PAGE_SIZE; > + > + mutex_lock(buf_mutex); > + new_buf = krealloc(*buf, new_buffer_size, GFP_KERNEL); > + mutex_unlock(buf_mutex); > + if (new_buf) { > + mutex_lock(buf_mutex); > + *buf = new_buf; > + *alloc_size = ksize(new_buf); > + mutex_unlock(buf_mutex); > + } else { > + hp_bios_settings_free_buffer(); > + ret = -ENOMEM; > + } > + } > + > + return ret; > +} > + > +static int append_settings_to_buffer(char *guid, int type, char **buf, > + int *buf_size, int *alloc_size, > + struct mutex *buf_mutex) > +{ > + union acpi_object *obj = NULL; > + int ret = 0; > + int status = 0; > + int instance = 0; > + > + /* > + * Query all the instances until to receive a AE_BAD_PARAMETER > + */ > + do { > + ret = hp_wmi_get_setting_object(guid, instance++, &obj); > + if (ACPI_SUCCESS(ret) && obj != NULL) { > + status = 0; > + if (obj->type == ACPI_TYPE_PACKAGE) { > + mutex_lock(buf_mutex); > + status = > append_package_elements_to_buffer(obj, > + *buf, *alloc_size, > type); > + if (status > 0) > + *buf_size = status; > + mutex_unlock(buf_mutex); > + > + } else if (obj->type == ACPI_TYPE_BUFFER) { > + mutex_lock(buf_mutex); > + status = > append_buffer_elements_to_buffer(obj, > + *buf, *alloc_size, > type); > + if (status > 0) > + *buf_size = status; > + mutex_unlock(buf_mutex); > + > + } else > + pr_warn("The retrieved object type(%d) is > not supported yet\n", > + obj->type); > + > + ret = hp_bios_settings_realloc_buffer(buf, buf_size, > alloc_size, buf_mutex); > + } > + > + kfree(obj); > + obj = NULL; > + > + } while (ACPI_SUCCESS(ret)); > + > + /* > + * AE_BAD_PARAMETER means the loop ended by exhaustion > + */ > + if (ret == AE_BAD_PARAMETER) > + ret = 0; > + > + return ret; > +} > + > +static int hp_bios_settings_fill_buffer(void) > +{ > + int status = 0; > + int initial_buffer_size = 20 * PAGE_SIZE; > + > + mutex_lock(&buf_mutex); > + hp_bios_settings_buffer = kmalloc(initial_buffer_size, GFP_KERNEL); > + mutex_unlock(&buf_mutex); > + if (!hp_bios_settings_buffer) > + return -ENOMEM; > + > + mutex_lock(&buf_mutex); > + buf_alloc_size = ksize(hp_bios_settings_buffer); > + settings_buffer_size = snprintf(hp_bios_settings_buffer, > + buf_alloc_size, "[\n"); > + mutex_unlock(&buf_mutex); > + > + status = append_settings_to_buffer(HPWMI_STRING_GUID, > + HPWMI_STRING_TYPE, &hp_bios_settings_buffer, > + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > + if (ACPI_FAILURE(status)) > + pr_err("error 0x%x occurred retrieving string instances\n", > status); > + > + status = append_settings_to_buffer(HPWMI_INTEGER_GUID, > + HPWMI_INTEGER_TYPE, &hp_bios_settings_buffer, > + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > + if (ACPI_FAILURE(status)) > + pr_err("error 0x%x occurred retrieving integer instances\n", > status); > + > + status = append_settings_to_buffer(HPWMI_ENUMERATION_GUID, > + HPWMI_ENUMERATION_TYPE, &hp_bios_settings_buffer, > + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > + if (ACPI_FAILURE(status)) > + pr_err("error 0x%x occurred retrieving enumeration > instances\n", status); > + > + status = append_settings_to_buffer(HPWMI_ORDEREDLIST_GUID, > + HPWMI_ORDEREDLIST_TYPE, &hp_bios_settings_buffer, > + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > + if (ACPI_FAILURE(status)) > + pr_err("error 0x%x occurred retrieving ordered list > instances\n", status); > + > + status = append_settings_to_buffer(HPWMI_PASSWORD_GUID, > + HPWMI_PASSWORD_TYPE, &hp_bios_settings_buffer, > + &settings_buffer_size, &buf_alloc_size, &buf_mutex); > + if (ACPI_FAILURE(status)) > + pr_err("error 0x%x occurred retrieving password list > instances\n", status); > + > + mutex_lock(&buf_mutex); > + /* > + * remove trailing comma > + */ > + if (settings_buffer_size >= 3) { > + if (hp_bios_settings_buffer[settings_buffer_size - 2] == ',') > + hp_bios_settings_buffer[settings_buffer_size - 2] = ' > '; > + } > + settings_buffer_size = snprintf(hp_bios_settings_buffer, > + buf_alloc_size, "%s]\n", > + hp_bios_settings_buffer); > + mutex_unlock(&buf_mutex); > + > + return settings_buffer_size; > +} > + > +/* > + * sure_admin_settings_read - Return a formatted file with settings > + * and possible options read from BIOS > + * > + * @filp: Pointer to file of settings read from BIOS > + * @kobj: Pointer to a kernel object of things that show up as directory in > the sysfs filesystem. > + * @attr: Pointer to list of read attributes > + * @buf: Pointer to buffer > + * @off: File current offset > + * @count: Buffer size > + * > + * Returns the count of unicode chars read if successful, otherwise > + * -ENOMEM unable to allocate memory > + * -EINVAL buffer not allocated or too small > + * > + */ > +static ssize_t sure_admin_settings_read(struct file *filp, struct kobject > *kobj, > + struct bin_attribute *attr, char *buf, > loff_t off, size_t count) > +{ > + ssize_t ret; > + > + /* clear the buffer when offset is pointing to the last position */ > + if (off >= settings_buffer_size && settings_buffer_size > 0) { > + hp_bios_settings_free_buffer(); > + return 0; > + } > + > + /* clear the buffer whenever the read starts from the first position > */ > + if (off == 0 && settings_buffer_size > 0) > + hp_bios_settings_free_buffer(); > + > + if (settings_buffer_size == 0) > + hp_bios_settings_fill_buffer(); > + > + mutex_lock(&buf_mutex); > + ret = memory_read_from_buffer(buf, count, &off, > hp_bios_settings_buffer, > + settings_buffer_size); > + mutex_unlock(&buf_mutex); > + > + 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 > + */ > +static void *ascii_to_utf16_unicode(u16 *p, const u8 *str) > +{ > + int len = strlen(str); > + > + /* > + * Add null character when reading an empty string > + */ > + if (len == 0) { > + *p++ = 2; > + *p++ = (u8)0x00; > + return p; > + } > + *p++ = len * 2; > + utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, p, len); > + p += len; > + > + return p; > +} > + > +/* > + * hp_wmi_set_bios_setting - Set setting's value in BIOS > + * > + * @input_buffer: Input buffer address > + * @input_size: Input buffer size > + * > + * Returns: Count of unicode characters written to BIOS if successful, > otherwise > + * -ENOMEM unable to allocate memory > + * -EINVAL buffer not allocated or too small > + */ > +static int hp_wmi_set_bios_setting(u16 *input_buffer, u32 input_size) > +{ > + union acpi_object *obj; > + struct acpi_buffer input = {input_size, input_buffer}; > + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; > + int ret = 0; > + > + ret = wmi_evaluate_method(HPWMI_SETBIOSSETTING_GUID, 0, 1, > &input, &output); > + > + obj = output.pointer; > + if (!obj) > + return -EINVAL; > + > + if (obj->type != ACPI_TYPE_INTEGER) > + ret = -EINVAL; > + > + ret = obj->integer.value; > + kfree(obj); > + return ret; > +} > + > +/* Sure Admin Functions */ > + > +#define UTF_PREFIX ((unsigned char *)"<utf-16/>") > +#define BEAM_PREFIX ((unsigned char > *)"<BEAM/>") > + > +/* > + * sure_admin_settings_write - Write the contents of a formatted file > + * with settings and performs the logic > + * to change any settings in BIOS. > + * > + * @filp: Pointer to file of settings to be written to BIOS > + * @kobj: Pointer to a kernel object of things that show up as directory in > the sysfs filesystem. > + * @attr: Pointer to list of attributes for the write operation > + * @buf: Pointer to buffer > + * @off: File current offset > + * @count: Buffer size > + * > + * > + * Returns the count of unicode characters written to BIOS if successful, > otherwise > + * -ENOMEM unable to allocate memory > + * -EINVAL buffer not allocated or too small > + * > + */ > +static ssize_t sure_admin_settings_write(struct file *filp, struct kobject > *kobj, > + struct bin_attribute *attr, char *buf, loff_t off, size_t > count) > +{ > + int status = 0; > + char *part = NULL; > + int part_len = 0; > + unsigned short *buffer = NULL; > + unsigned short *tmpstr = NULL; > + int buffer_size = (count + strlen(UTF_PREFIX)) * sizeof(unsigned > short); > + > + buffer = kmalloc(buffer_size, GFP_KERNEL); > + if (!buffer) > + return -ENOMEM; > + > + tmpstr = buffer; > + part = strsep(&buf, ","); > + if (!part) { > + status = -EINVAL; > + goto out_free; > + } > + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > + part = strsep(&buf, ","); > + if (!part) { > + status = -EINVAL; > + goto out_free; > + } > + > + /* Add extra buffer space when encountering an empty string */ > + if (!strlen(part)) > + buffer_size += sizeof(unsigned short); > + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > + part = strsep(&buf, ","); > + if (!part) { > + status = -EINVAL; > + goto out_free; > + } > + part_len = strlen(part) - 1; > + part[part_len] = '\0'; > + > + if (strncmp(part, BEAM_PREFIX, strlen(BEAM_PREFIX)) == 0) { > + /* > + * BEAM_PREFIX is append to buffer when a signature > + * is provided and Sure Admin is enabled in BIOS > + */ > + // BEAM_PREFIX found, convert part to unicode > + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > + // decrease buffer size allocated initially for UTF_PREFIX > + buffer_size -= strlen(UTF_PREFIX) * sizeof(unsigned short); > + } else { > + /* > + * UTF-16 prefix is append to the * buffer when a BIOS > + * admin password is configured in BIOS > + */ > + > + // append UTF_PREFIX to part and then convert it to unicode > + part = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX, part); > + if (!part) > + goto out_free; > + > + tmpstr = ascii_to_utf16_unicode(tmpstr, part); > + kfree(part); > + } > + > + part = strsep(&buf, ","); > + if (part) { > + status = -EINVAL; > + goto out_free; > + } > + status = hp_wmi_set_bios_setting(buffer, buffer_size); > + if (ACPI_FAILURE(status)) > + status = -EINVAL; > + > +out_free: > + kfree(buffer); > + if (ACPI_SUCCESS(status)) > + return count; > + return status; > +} > + > +HPWMI_BINATTR_RW(sure_admin, settings, 0); > + > +static struct bin_attribute *sure_admin_binattrs[] = { > + &sure_admin_settings, > + NULL, > +}; > + > +static const struct attribute_group sure_admin_group = { > + .name = "sure_admin", > + .bin_attrs = sure_admin_binattrs, > +}; > + > static DEVICE_ATTR_RO(display); > static DEVICE_ATTR_RO(hddtemp); > static DEVICE_ATTR_RW(als); > @@ -1050,6 +2026,7 @@ static const struct attribute_group > *hp_wmi_groups[] = { > &hp_wmi_group, > &spm_group, > &sure_start_group, > + &sure_admin_group, > NULL, > }; > > -- > 2.25.1