RE: [PATCH v1 5/6] Sure Admin Security Feature

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

 



[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




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux