162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common methods for use with hp-bioscfg driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2022 HP Development Company, L.P.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/wmi.h>
1462306a36Sopenharmony_ci#include "bioscfg.h"
1562306a36Sopenharmony_ci#include "../../firmware_attributes_class.h"
1662306a36Sopenharmony_ci#include <linux/nls.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciMODULE_AUTHOR("Jorge Lopez <jorge.lopez2@hp.com>");
2062306a36Sopenharmony_ciMODULE_DESCRIPTION("HP BIOS Configuration Driver");
2162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct bioscfg_priv bioscfg_drv = {
2462306a36Sopenharmony_ci	.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic struct class *fw_attr_class;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cissize_t display_name_language_code_show(struct kobject *kobj,
3062306a36Sopenharmony_ci					struct kobj_attribute *attr,
3162306a36Sopenharmony_ci					char *buf)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", LANG_CODE_STR);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct kobj_attribute common_display_langcode =
3762306a36Sopenharmony_ci	__ATTR_RO(display_name_language_code);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciint hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	int *ptr = PTR_ALIGN((int *)*buffer, sizeof(int));
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Ensure there is enough space remaining to read the integer */
4462306a36Sopenharmony_ci	if (*buffer_size < sizeof(int))
4562306a36Sopenharmony_ci		return -EINVAL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	*integer = *(ptr++);
4862306a36Sopenharmony_ci	*buffer = (u8 *)ptr;
4962306a36Sopenharmony_ci	*buffer_size -= sizeof(int);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciint hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	u16 *src = (u16 *)*buffer;
5762306a36Sopenharmony_ci	u16 src_size;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	u16 size;
6062306a36Sopenharmony_ci	int i;
6162306a36Sopenharmony_ci	int conv_dst_size;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (*buffer_size < sizeof(u16))
6462306a36Sopenharmony_ci		return -EINVAL;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	src_size = *(src++);
6762306a36Sopenharmony_ci	/* size value in u16 chars */
6862306a36Sopenharmony_ci	size = src_size / sizeof(u16);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Ensure there is enough space remaining to read and convert
7162306a36Sopenharmony_ci	 * the string
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	if (*buffer_size < src_size)
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	for (i = 0; i < size; i++)
7762306a36Sopenharmony_ci		if (src[i] == '\\' ||
7862306a36Sopenharmony_ci		    src[i] == '\r' ||
7962306a36Sopenharmony_ci		    src[i] == '\n' ||
8062306a36Sopenharmony_ci		    src[i] == '\t')
8162306a36Sopenharmony_ci			size++;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/*
8462306a36Sopenharmony_ci	 * Conversion is limited to destination string max number of
8562306a36Sopenharmony_ci	 * bytes.
8662306a36Sopenharmony_ci	 */
8762306a36Sopenharmony_ci	conv_dst_size = size;
8862306a36Sopenharmony_ci	if (size > dst_size)
8962306a36Sopenharmony_ci		conv_dst_size = dst_size - 1;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * convert from UTF-16 unicode to ASCII
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci	utf16s_to_utf8s(src, src_size, UTF16_HOST_ENDIAN, dst, conv_dst_size);
9562306a36Sopenharmony_ci	dst[conv_dst_size] = 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for (i = 0; i < conv_dst_size; i++) {
9862306a36Sopenharmony_ci		if (*src == '\\' ||
9962306a36Sopenharmony_ci		    *src == '\r' ||
10062306a36Sopenharmony_ci		    *src == '\n' ||
10162306a36Sopenharmony_ci		    *src == '\t') {
10262306a36Sopenharmony_ci			dst[i++] = '\\';
10362306a36Sopenharmony_ci			if (i == conv_dst_size)
10462306a36Sopenharmony_ci				break;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		if (*src == '\r')
10862306a36Sopenharmony_ci			dst[i] = 'r';
10962306a36Sopenharmony_ci		else if (*src == '\n')
11062306a36Sopenharmony_ci			dst[i] = 'n';
11162306a36Sopenharmony_ci		else if (*src == '\t')
11262306a36Sopenharmony_ci			dst[i] = 't';
11362306a36Sopenharmony_ci		else if (*src == '"')
11462306a36Sopenharmony_ci			dst[i] = '\'';
11562306a36Sopenharmony_ci		else
11662306a36Sopenharmony_ci			dst[i] = *src;
11762306a36Sopenharmony_ci		src++;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	*buffer = (u8 *)src;
12162306a36Sopenharmony_ci	*buffer_size -= size * sizeof(u16);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return size;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size,
12762306a36Sopenharmony_ci				   struct common_data *common_data)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	int ret = 0;
13062306a36Sopenharmony_ci	int reqs;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	// PATH:
13362306a36Sopenharmony_ci	ret = hp_get_string_from_buffer(buffer_ptr, buffer_size, common_data->path,
13462306a36Sopenharmony_ci					sizeof(common_data->path));
13562306a36Sopenharmony_ci	if (ret < 0)
13662306a36Sopenharmony_ci		goto common_exit;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	// IS_READONLY:
13962306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
14062306a36Sopenharmony_ci					 &common_data->is_readonly);
14162306a36Sopenharmony_ci	if (ret < 0)
14262306a36Sopenharmony_ci		goto common_exit;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	//DISPLAY_IN_UI:
14562306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
14662306a36Sopenharmony_ci					 &common_data->display_in_ui);
14762306a36Sopenharmony_ci	if (ret < 0)
14862306a36Sopenharmony_ci		goto common_exit;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	// REQUIRES_PHYSICAL_PRESENCE:
15162306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
15262306a36Sopenharmony_ci					 &common_data->requires_physical_presence);
15362306a36Sopenharmony_ci	if (ret < 0)
15462306a36Sopenharmony_ci		goto common_exit;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	// SEQUENCE:
15762306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
15862306a36Sopenharmony_ci					 &common_data->sequence);
15962306a36Sopenharmony_ci	if (ret < 0)
16062306a36Sopenharmony_ci		goto common_exit;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	// PREREQUISITES_SIZE:
16362306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
16462306a36Sopenharmony_ci					 &common_data->prerequisites_size);
16562306a36Sopenharmony_ci	if (ret < 0)
16662306a36Sopenharmony_ci		goto common_exit;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (common_data->prerequisites_size > MAX_PREREQUISITES_SIZE) {
16962306a36Sopenharmony_ci		/* Report a message and limit prerequisite size to maximum value */
17062306a36Sopenharmony_ci		pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
17162306a36Sopenharmony_ci		common_data->prerequisites_size = MAX_PREREQUISITES_SIZE;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	// PREREQUISITES:
17562306a36Sopenharmony_ci	for (reqs = 0; reqs < common_data->prerequisites_size; reqs++) {
17662306a36Sopenharmony_ci		ret = hp_get_string_from_buffer(buffer_ptr, buffer_size,
17762306a36Sopenharmony_ci						common_data->prerequisites[reqs],
17862306a36Sopenharmony_ci						sizeof(common_data->prerequisites[reqs]));
17962306a36Sopenharmony_ci		if (ret < 0)
18062306a36Sopenharmony_ci			break;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	// SECURITY_LEVEL:
18462306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
18562306a36Sopenharmony_ci					 &common_data->security_level);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cicommon_exit:
18862306a36Sopenharmony_ci	return ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciint hp_enforce_single_line_input(char *buf, size_t count)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	char *p;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	p = memchr(buf, '\n', count);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (p == buf + count - 1)
19862306a36Sopenharmony_ci		*p = '\0'; /* strip trailing newline */
19962306a36Sopenharmony_ci	else if (p)
20062306a36Sopenharmony_ci		return -EINVAL;  /* enforce single line input */
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/* Set pending reboot value and generate KOBJ_NAME event */
20662306a36Sopenharmony_civoid hp_set_reboot_and_signal_event(void)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	bioscfg_drv.pending_reboot = true;
20962306a36Sopenharmony_ci	kobject_uevent(&bioscfg_drv.class_dev->kobj, KOBJ_CHANGE);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/**
21362306a36Sopenharmony_ci * hp_calculate_string_buffer() - determines size of string buffer for
21462306a36Sopenharmony_ci * use with BIOS communication
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci * @str: the string to calculate based upon
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cisize_t hp_calculate_string_buffer(const char *str)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	size_t length = strlen(str);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* BIOS expects 4 bytes when an empty string is found */
22362306a36Sopenharmony_ci	if (length == 0)
22462306a36Sopenharmony_ci		return 4;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* u16 length field + one UTF16 char for each input char */
22762306a36Sopenharmony_ci	return sizeof(u16) + strlen(str) * sizeof(u16);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciint hp_wmi_error_and_message(int error_code)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	char *error_msg = NULL;
23362306a36Sopenharmony_ci	int ret;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	switch (error_code) {
23662306a36Sopenharmony_ci	case SUCCESS:
23762306a36Sopenharmony_ci		error_msg = "Success";
23862306a36Sopenharmony_ci		ret = 0;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case CMD_FAILED:
24162306a36Sopenharmony_ci		error_msg = "Command failed";
24262306a36Sopenharmony_ci		ret = -EINVAL;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case INVALID_SIGN:
24562306a36Sopenharmony_ci		error_msg = "Invalid signature";
24662306a36Sopenharmony_ci		ret = -EINVAL;
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	case INVALID_CMD_VALUE:
24962306a36Sopenharmony_ci		error_msg = "Invalid command value/Feature not supported";
25062306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	case INVALID_CMD_TYPE:
25362306a36Sopenharmony_ci		error_msg = "Invalid command type";
25462306a36Sopenharmony_ci		ret = -EINVAL;
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci	case INVALID_DATA_SIZE:
25762306a36Sopenharmony_ci		error_msg = "Invalid data size";
25862306a36Sopenharmony_ci		ret = -EINVAL;
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case INVALID_CMD_PARAM:
26162306a36Sopenharmony_ci		error_msg = "Invalid command parameter";
26262306a36Sopenharmony_ci		ret = -EINVAL;
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	case ENCRYP_CMD_REQUIRED:
26562306a36Sopenharmony_ci		error_msg = "Secure/encrypted command required";
26662306a36Sopenharmony_ci		ret = -EACCES;
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	case NO_SECURE_SESSION:
26962306a36Sopenharmony_ci		error_msg = "No secure session established";
27062306a36Sopenharmony_ci		ret = -EACCES;
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci	case SECURE_SESSION_FOUND:
27362306a36Sopenharmony_ci		error_msg = "Secure session already established";
27462306a36Sopenharmony_ci		ret = -EACCES;
27562306a36Sopenharmony_ci		break;
27662306a36Sopenharmony_ci	case SECURE_SESSION_FAILED:
27762306a36Sopenharmony_ci		error_msg = "Secure session failed";
27862306a36Sopenharmony_ci		ret = -EIO;
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case AUTH_FAILED:
28162306a36Sopenharmony_ci		error_msg = "Other permission/Authentication failed";
28262306a36Sopenharmony_ci		ret = -EACCES;
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case INVALID_BIOS_AUTH:
28562306a36Sopenharmony_ci		error_msg = "Invalid BIOS administrator password";
28662306a36Sopenharmony_ci		ret = -EINVAL;
28762306a36Sopenharmony_ci		break;
28862306a36Sopenharmony_ci	case NONCE_DID_NOT_MATCH:
28962306a36Sopenharmony_ci		error_msg = "Nonce did not match";
29062306a36Sopenharmony_ci		ret = -EINVAL;
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case GENERIC_ERROR:
29362306a36Sopenharmony_ci		error_msg = "Generic/Other error";
29462306a36Sopenharmony_ci		ret = -EIO;
29562306a36Sopenharmony_ci		break;
29662306a36Sopenharmony_ci	case BIOS_ADMIN_POLICY_NOT_MET:
29762306a36Sopenharmony_ci		error_msg = "BIOS Admin password does not meet password policy requirements";
29862306a36Sopenharmony_ci		ret = -EINVAL;
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	case BIOS_ADMIN_NOT_SET:
30162306a36Sopenharmony_ci		error_msg = "BIOS Setup password is not set";
30262306a36Sopenharmony_ci		ret = -EPERM;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case P21_NO_PROVISIONED:
30562306a36Sopenharmony_ci		error_msg = "P21 is not provisioned";
30662306a36Sopenharmony_ci		ret = -EPERM;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case P21_PROVISION_IN_PROGRESS:
30962306a36Sopenharmony_ci		error_msg = "P21 is already provisioned or provisioning is in progress and a signing key has already been sent";
31062306a36Sopenharmony_ci		ret = -EINPROGRESS;
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	case P21_IN_USE:
31362306a36Sopenharmony_ci		error_msg = "P21 in use (cannot deprovision)";
31462306a36Sopenharmony_ci		ret = -EPERM;
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	case HEP_NOT_ACTIVE:
31762306a36Sopenharmony_ci		error_msg = "HEP not activated";
31862306a36Sopenharmony_ci		ret = -EPERM;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case HEP_ALREADY_SET:
32162306a36Sopenharmony_ci		error_msg = "HEP Transport already set";
32262306a36Sopenharmony_ci		ret = -EINVAL;
32362306a36Sopenharmony_ci		break;
32462306a36Sopenharmony_ci	case HEP_CHECK_STATE:
32562306a36Sopenharmony_ci		error_msg = "Check the current HEP state";
32662306a36Sopenharmony_ci		ret = -EINVAL;
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		error_msg = "Generic/Other error";
33062306a36Sopenharmony_ci		ret = -EIO;
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (error_code)
33562306a36Sopenharmony_ci		pr_warn_ratelimited("Returned error 0x%x, \"%s\"\n", error_code, error_msg);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return ret;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic ssize_t pending_reboot_show(struct kobject *kobj,
34162306a36Sopenharmony_ci				   struct kobj_attribute *attr,
34262306a36Sopenharmony_ci				   char *buf)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", bioscfg_drv.pending_reboot);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/*
35062306a36Sopenharmony_ci * create_attributes_level_sysfs_files() - Creates pending_reboot attributes
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic int create_attributes_level_sysfs_files(void)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	return  sysfs_create_file(&bioscfg_drv.main_dir_kset->kobj,
35562306a36Sopenharmony_ci				  &pending_reboot.attr);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void attr_name_release(struct kobject *kobj)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	kfree(kobj);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic const struct kobj_type attr_name_ktype = {
36462306a36Sopenharmony_ci	.release	= attr_name_release,
36562306a36Sopenharmony_ci	.sysfs_ops	= &kobj_sysfs_ops,
36662306a36Sopenharmony_ci};
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/**
36962306a36Sopenharmony_ci * hp_get_wmiobj_pointer() - Get Content of WMI block for particular instance
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * @instance_id: WMI instance ID
37262306a36Sopenharmony_ci * @guid_string: WMI GUID (in str form)
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * Fetches the content for WMI block (instance_id) under GUID (guid_string)
37562306a36Sopenharmony_ci * Caller must kfree the return
37662306a36Sopenharmony_ci */
37762306a36Sopenharmony_ciunion acpi_object *hp_get_wmiobj_pointer(int instance_id, const char *guid_string)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
38062306a36Sopenharmony_ci	acpi_status status;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	status = wmi_query_block(guid_string, instance_id, &out);
38362306a36Sopenharmony_ci	return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * hp_get_instance_count() - Compute total number of instances under guid_string
38862306a36Sopenharmony_ci *
38962306a36Sopenharmony_ci * @guid_string: WMI GUID (in string form)
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_ciint hp_get_instance_count(const char *guid_string)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	union acpi_object *wmi_obj = NULL;
39462306a36Sopenharmony_ci	int i = 0;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	do {
39762306a36Sopenharmony_ci		kfree(wmi_obj);
39862306a36Sopenharmony_ci		wmi_obj = hp_get_wmiobj_pointer(i, guid_string);
39962306a36Sopenharmony_ci		i++;
40062306a36Sopenharmony_ci	} while (wmi_obj);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return i - 1;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/**
40662306a36Sopenharmony_ci * hp_alloc_attributes_data() - Allocate attributes data for a particular type
40762306a36Sopenharmony_ci *
40862306a36Sopenharmony_ci * @attr_type: Attribute type to allocate
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_cistatic int hp_alloc_attributes_data(int attr_type)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	switch (attr_type) {
41362306a36Sopenharmony_ci	case HPWMI_STRING_TYPE:
41462306a36Sopenharmony_ci		return hp_alloc_string_data();
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	case HPWMI_INTEGER_TYPE:
41762306a36Sopenharmony_ci		return hp_alloc_integer_data();
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	case HPWMI_ENUMERATION_TYPE:
42062306a36Sopenharmony_ci		return hp_alloc_enumeration_data();
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	case HPWMI_ORDERED_LIST_TYPE:
42362306a36Sopenharmony_ci		return hp_alloc_ordered_list_data();
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	case HPWMI_PASSWORD_TYPE:
42662306a36Sopenharmony_ci		return hp_alloc_password_data();
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	default:
42962306a36Sopenharmony_ci		return 0;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ciint hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *len)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	int ret = 0;
43662306a36Sopenharmony_ci	int new_len = 0;
43762306a36Sopenharmony_ci	char tmp[] = "0x00";
43862306a36Sopenharmony_ci	char *new_str = NULL;
43962306a36Sopenharmony_ci	long  ch;
44062306a36Sopenharmony_ci	int i;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (input_len <= 0 || !input || !str || !len)
44362306a36Sopenharmony_ci		return -EINVAL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	*len = 0;
44662306a36Sopenharmony_ci	*str = NULL;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	new_str = kmalloc(input_len, GFP_KERNEL);
44962306a36Sopenharmony_ci	if (!new_str)
45062306a36Sopenharmony_ci		return -ENOMEM;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	for (i = 0; i < input_len; i += 5) {
45362306a36Sopenharmony_ci		strncpy(tmp, input + i, strlen(tmp));
45462306a36Sopenharmony_ci		if (kstrtol(tmp, 16, &ch) == 0) {
45562306a36Sopenharmony_ci			// escape char
45662306a36Sopenharmony_ci			if (ch == '\\' ||
45762306a36Sopenharmony_ci			    ch == '\r' ||
45862306a36Sopenharmony_ci			    ch == '\n' || ch == '\t') {
45962306a36Sopenharmony_ci				if (ch == '\r')
46062306a36Sopenharmony_ci					ch = 'r';
46162306a36Sopenharmony_ci				else if (ch == '\n')
46262306a36Sopenharmony_ci					ch = 'n';
46362306a36Sopenharmony_ci				else if (ch == '\t')
46462306a36Sopenharmony_ci					ch = 't';
46562306a36Sopenharmony_ci				new_str[new_len++] = '\\';
46662306a36Sopenharmony_ci			}
46762306a36Sopenharmony_ci			new_str[new_len++] = ch;
46862306a36Sopenharmony_ci			if (ch == '\0')
46962306a36Sopenharmony_ci				break;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (new_len) {
47462306a36Sopenharmony_ci		new_str[new_len] = '\0';
47562306a36Sopenharmony_ci		*str = krealloc(new_str, (new_len + 1) * sizeof(char),
47662306a36Sopenharmony_ci				GFP_KERNEL);
47762306a36Sopenharmony_ci		if (*str)
47862306a36Sopenharmony_ci			*len = new_len;
47962306a36Sopenharmony_ci		else
48062306a36Sopenharmony_ci			ret = -ENOMEM;
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		ret = -EFAULT;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		kfree(new_str);
48762306a36Sopenharmony_ci	return ret;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/* map output size to the corresponding WMI method id */
49162306a36Sopenharmony_ciint hp_encode_outsize_for_pvsz(int outsize)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	if (outsize > 4096)
49462306a36Sopenharmony_ci		return -EINVAL;
49562306a36Sopenharmony_ci	if (outsize > 1024)
49662306a36Sopenharmony_ci		return 5;
49762306a36Sopenharmony_ci	if (outsize > 128)
49862306a36Sopenharmony_ci		return 4;
49962306a36Sopenharmony_ci	if (outsize > 4)
50062306a36Sopenharmony_ci		return 3;
50162306a36Sopenharmony_ci	if (outsize > 0)
50262306a36Sopenharmony_ci		return 2;
50362306a36Sopenharmony_ci	return 1;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/*
50762306a36Sopenharmony_ci * Update friendly display name for several attributes associated to
50862306a36Sopenharmony_ci * 'Schedule Power-On'
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_civoid hp_friendly_user_name_update(char *path, const char *attr_name,
51162306a36Sopenharmony_ci				  char *attr_display, int attr_size)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	if (strstr(path, SCHEDULE_POWER_ON))
51462306a36Sopenharmony_ci		snprintf(attr_display, attr_size, "%s - %s", SCHEDULE_POWER_ON, attr_name);
51562306a36Sopenharmony_ci	else
51662306a36Sopenharmony_ci		strscpy(attr_display, attr_name, attr_size);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/**
52062306a36Sopenharmony_ci * hp_update_attribute_permissions() - Update attributes permissions when
52162306a36Sopenharmony_ci * isReadOnly value is 1
52262306a36Sopenharmony_ci *
52362306a36Sopenharmony_ci * @is_readonly:  bool value to indicate if it a readonly attribute.
52462306a36Sopenharmony_ci * @current_val: kobj_attribute corresponding to attribute.
52562306a36Sopenharmony_ci *
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_civoid hp_update_attribute_permissions(bool is_readonly, struct kobj_attribute *current_val)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	current_val->attr.mode = is_readonly ? 0444 : 0644;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/**
53362306a36Sopenharmony_ci * destroy_attribute_objs() - Free a kset of kobjects
53462306a36Sopenharmony_ci * @kset: The kset to destroy
53562306a36Sopenharmony_ci *
53662306a36Sopenharmony_ci * Fress kobjects created for each attribute_name under attribute type kset
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic void destroy_attribute_objs(struct kset *kset)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct kobject *pos, *next;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	list_for_each_entry_safe(pos, next, &kset->list, entry)
54362306a36Sopenharmony_ci		kobject_put(pos);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci/**
54762306a36Sopenharmony_ci * release_attributes_data() - Clean-up all sysfs directories and files created
54862306a36Sopenharmony_ci */
54962306a36Sopenharmony_cistatic void release_attributes_data(void)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	mutex_lock(&bioscfg_drv.mutex);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	hp_exit_string_attributes();
55462306a36Sopenharmony_ci	hp_exit_integer_attributes();
55562306a36Sopenharmony_ci	hp_exit_enumeration_attributes();
55662306a36Sopenharmony_ci	hp_exit_ordered_list_attributes();
55762306a36Sopenharmony_ci	hp_exit_password_attributes();
55862306a36Sopenharmony_ci	hp_exit_sure_start_attributes();
55962306a36Sopenharmony_ci	hp_exit_secure_platform_attributes();
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (bioscfg_drv.authentication_dir_kset) {
56262306a36Sopenharmony_ci		destroy_attribute_objs(bioscfg_drv.authentication_dir_kset);
56362306a36Sopenharmony_ci		kset_unregister(bioscfg_drv.authentication_dir_kset);
56462306a36Sopenharmony_ci		bioscfg_drv.authentication_dir_kset = NULL;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci	if (bioscfg_drv.main_dir_kset) {
56762306a36Sopenharmony_ci		sysfs_remove_file(&bioscfg_drv.main_dir_kset->kobj, &pending_reboot.attr);
56862306a36Sopenharmony_ci		destroy_attribute_objs(bioscfg_drv.main_dir_kset);
56962306a36Sopenharmony_ci		kset_unregister(bioscfg_drv.main_dir_kset);
57062306a36Sopenharmony_ci		bioscfg_drv.main_dir_kset = NULL;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci	mutex_unlock(&bioscfg_drv.mutex);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/**
57662306a36Sopenharmony_ci * hp_add_other_attributes() - Initialize HP custom attributes not
57762306a36Sopenharmony_ci * reported by BIOS and required to support Secure Platform and Sure
57862306a36Sopenharmony_ci * Start.
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci * @attr_type: Custom HP attribute not reported by BIOS
58162306a36Sopenharmony_ci *
58262306a36Sopenharmony_ci * Initialize all 2 types of attributes: Platform and Sure Start
58362306a36Sopenharmony_ci * object.  Populates each attribute types respective properties
58462306a36Sopenharmony_ci * under sysfs files.
58562306a36Sopenharmony_ci *
58662306a36Sopenharmony_ci * Returns zero(0) if successful. Otherwise, a negative value.
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_cistatic int hp_add_other_attributes(int attr_type)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct kobject *attr_name_kobj;
59162306a36Sopenharmony_ci	union acpi_object *obj = NULL;
59262306a36Sopenharmony_ci	int ret;
59362306a36Sopenharmony_ci	char *attr_name;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
59662306a36Sopenharmony_ci	if (!attr_name_kobj)
59762306a36Sopenharmony_ci		return -ENOMEM;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	mutex_lock(&bioscfg_drv.mutex);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Check if attribute type is supported */
60262306a36Sopenharmony_ci	switch (attr_type) {
60362306a36Sopenharmony_ci	case HPWMI_SECURE_PLATFORM_TYPE:
60462306a36Sopenharmony_ci		attr_name_kobj->kset = bioscfg_drv.authentication_dir_kset;
60562306a36Sopenharmony_ci		attr_name = SPM_STR;
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	case HPWMI_SURE_START_TYPE:
60962306a36Sopenharmony_ci		attr_name_kobj->kset = bioscfg_drv.main_dir_kset;
61062306a36Sopenharmony_ci		attr_name = SURE_START_STR;
61162306a36Sopenharmony_ci		break;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	default:
61462306a36Sopenharmony_ci		pr_err("Error: Unknown attr_type: %d\n", attr_type);
61562306a36Sopenharmony_ci		ret = -EINVAL;
61662306a36Sopenharmony_ci		kfree(attr_name_kobj);
61762306a36Sopenharmony_ci		goto unlock_drv_mutex;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype,
62162306a36Sopenharmony_ci				   NULL, "%s", attr_name);
62262306a36Sopenharmony_ci	if (ret) {
62362306a36Sopenharmony_ci		pr_err("Error encountered [%d]\n", ret);
62462306a36Sopenharmony_ci		goto err_other_attr_init;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* Populate attribute data */
62862306a36Sopenharmony_ci	switch (attr_type) {
62962306a36Sopenharmony_ci	case HPWMI_SECURE_PLATFORM_TYPE:
63062306a36Sopenharmony_ci		ret = hp_populate_secure_platform_data(attr_name_kobj);
63162306a36Sopenharmony_ci		break;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	case HPWMI_SURE_START_TYPE:
63462306a36Sopenharmony_ci		ret = hp_populate_sure_start_data(attr_name_kobj);
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	default:
63862306a36Sopenharmony_ci		ret = -EINVAL;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (ret)
64262306a36Sopenharmony_ci		goto err_other_attr_init;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	mutex_unlock(&bioscfg_drv.mutex);
64562306a36Sopenharmony_ci	return 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cierr_other_attr_init:
64862306a36Sopenharmony_ci	kobject_put(attr_name_kobj);
64962306a36Sopenharmony_ciunlock_drv_mutex:
65062306a36Sopenharmony_ci	mutex_unlock(&bioscfg_drv.mutex);
65162306a36Sopenharmony_ci	kfree(obj);
65262306a36Sopenharmony_ci	return ret;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type,
65662306a36Sopenharmony_ci					  union acpi_object *obj,
65762306a36Sopenharmony_ci					  const char *guid, int min_elements,
65862306a36Sopenharmony_ci					  int instance_id)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct kobject *attr_name_kobj, *duplicate;
66162306a36Sopenharmony_ci	union acpi_object *elements;
66262306a36Sopenharmony_ci	struct kset *temp_kset;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	char *str_value = NULL;
66562306a36Sopenharmony_ci	int str_len;
66662306a36Sopenharmony_ci	int ret = 0;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Take action appropriate to each ACPI TYPE */
66962306a36Sopenharmony_ci	if (obj->package.count < min_elements) {
67062306a36Sopenharmony_ci		pr_err("ACPI-package does not have enough elements: %d < %d\n",
67162306a36Sopenharmony_ci		       obj->package.count, min_elements);
67262306a36Sopenharmony_ci		goto pack_attr_exit;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	elements = obj->package.elements;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* sanity checking */
67862306a36Sopenharmony_ci	if (elements[NAME].type != ACPI_TYPE_STRING) {
67962306a36Sopenharmony_ci		pr_debug("incorrect element type\n");
68062306a36Sopenharmony_ci		goto pack_attr_exit;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci	if (strlen(elements[NAME].string.pointer) == 0) {
68362306a36Sopenharmony_ci		pr_debug("empty attribute found\n");
68462306a36Sopenharmony_ci		goto pack_attr_exit;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (attr_type == HPWMI_PASSWORD_TYPE)
68862306a36Sopenharmony_ci		temp_kset = bioscfg_drv.authentication_dir_kset;
68962306a36Sopenharmony_ci	else
69062306a36Sopenharmony_ci		temp_kset = bioscfg_drv.main_dir_kset;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* convert attribute name to string */
69362306a36Sopenharmony_ci	ret = hp_convert_hexstr_to_str(elements[NAME].string.pointer,
69462306a36Sopenharmony_ci				       elements[NAME].string.length,
69562306a36Sopenharmony_ci				       &str_value, &str_len);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (ret) {
69862306a36Sopenharmony_ci		pr_debug("Failed to populate integer package data. Error [0%0x]\n",
69962306a36Sopenharmony_ci			 ret);
70062306a36Sopenharmony_ci		kfree(str_value);
70162306a36Sopenharmony_ci		return ret;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* All duplicate attributes found are ignored */
70562306a36Sopenharmony_ci	duplicate = kset_find_obj(temp_kset, str_value);
70662306a36Sopenharmony_ci	if (duplicate) {
70762306a36Sopenharmony_ci		pr_debug("Duplicate attribute name found - %s\n", str_value);
70862306a36Sopenharmony_ci		/* kset_find_obj() returns a reference */
70962306a36Sopenharmony_ci		kobject_put(duplicate);
71062306a36Sopenharmony_ci		goto pack_attr_exit;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* build attribute */
71462306a36Sopenharmony_ci	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
71562306a36Sopenharmony_ci	if (!attr_name_kobj) {
71662306a36Sopenharmony_ci		ret = -ENOMEM;
71762306a36Sopenharmony_ci		goto pack_attr_exit;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	attr_name_kobj->kset = temp_kset;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype,
72362306a36Sopenharmony_ci				   NULL, "%s", str_value);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (ret) {
72662306a36Sopenharmony_ci		kobject_put(attr_name_kobj);
72762306a36Sopenharmony_ci		goto pack_attr_exit;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* enumerate all of these attributes */
73162306a36Sopenharmony_ci	switch (attr_type) {
73262306a36Sopenharmony_ci	case HPWMI_STRING_TYPE:
73362306a36Sopenharmony_ci		ret = hp_populate_string_package_data(elements,
73462306a36Sopenharmony_ci						      instance_id,
73562306a36Sopenharmony_ci						      attr_name_kobj);
73662306a36Sopenharmony_ci		break;
73762306a36Sopenharmony_ci	case HPWMI_INTEGER_TYPE:
73862306a36Sopenharmony_ci		ret = hp_populate_integer_package_data(elements,
73962306a36Sopenharmony_ci						       instance_id,
74062306a36Sopenharmony_ci						       attr_name_kobj);
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case HPWMI_ENUMERATION_TYPE:
74362306a36Sopenharmony_ci		ret = hp_populate_enumeration_package_data(elements,
74462306a36Sopenharmony_ci							   instance_id,
74562306a36Sopenharmony_ci							   attr_name_kobj);
74662306a36Sopenharmony_ci		break;
74762306a36Sopenharmony_ci	case HPWMI_ORDERED_LIST_TYPE:
74862306a36Sopenharmony_ci		ret = hp_populate_ordered_list_package_data(elements,
74962306a36Sopenharmony_ci							    instance_id,
75062306a36Sopenharmony_ci							    attr_name_kobj);
75162306a36Sopenharmony_ci		break;
75262306a36Sopenharmony_ci	case HPWMI_PASSWORD_TYPE:
75362306a36Sopenharmony_ci		ret = hp_populate_password_package_data(elements,
75462306a36Sopenharmony_ci							instance_id,
75562306a36Sopenharmony_ci							attr_name_kobj);
75662306a36Sopenharmony_ci		break;
75762306a36Sopenharmony_ci	default:
75862306a36Sopenharmony_ci		pr_debug("Unknown attribute type found: 0x%x\n", attr_type);
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cipack_attr_exit:
76362306a36Sopenharmony_ci	kfree(str_value);
76462306a36Sopenharmony_ci	return ret;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type,
76862306a36Sopenharmony_ci					 union acpi_object *obj,
76962306a36Sopenharmony_ci					 const char *guid, int min_elements,
77062306a36Sopenharmony_ci					 int instance_id)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct kobject *attr_name_kobj, *duplicate;
77362306a36Sopenharmony_ci	struct kset *temp_kset;
77462306a36Sopenharmony_ci	char str[MAX_BUFF_SIZE];
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	char *temp_str = NULL;
77762306a36Sopenharmony_ci	char *str_value = NULL;
77862306a36Sopenharmony_ci	u8 *buffer_ptr = NULL;
77962306a36Sopenharmony_ci	int buffer_size;
78062306a36Sopenharmony_ci	int ret = 0;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	buffer_size = obj->buffer.length;
78362306a36Sopenharmony_ci	buffer_ptr = obj->buffer.pointer;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ret = hp_get_string_from_buffer(&buffer_ptr,
78662306a36Sopenharmony_ci					&buffer_size, str, MAX_BUFF_SIZE);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (ret < 0)
78962306a36Sopenharmony_ci		goto buff_attr_exit;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (attr_type == HPWMI_PASSWORD_TYPE ||
79262306a36Sopenharmony_ci	    attr_type == HPWMI_SECURE_PLATFORM_TYPE)
79362306a36Sopenharmony_ci		temp_kset = bioscfg_drv.authentication_dir_kset;
79462306a36Sopenharmony_ci	else
79562306a36Sopenharmony_ci		temp_kset = bioscfg_drv.main_dir_kset;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* All duplicate attributes found are ignored */
79862306a36Sopenharmony_ci	duplicate = kset_find_obj(temp_kset, str);
79962306a36Sopenharmony_ci	if (duplicate) {
80062306a36Sopenharmony_ci		pr_debug("Duplicate attribute name found - %s\n", str);
80162306a36Sopenharmony_ci		/* kset_find_obj() returns a reference */
80262306a36Sopenharmony_ci		kobject_put(duplicate);
80362306a36Sopenharmony_ci		goto buff_attr_exit;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* build attribute */
80762306a36Sopenharmony_ci	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
80862306a36Sopenharmony_ci	if (!attr_name_kobj) {
80962306a36Sopenharmony_ci		ret = -ENOMEM;
81062306a36Sopenharmony_ci		goto buff_attr_exit;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	attr_name_kobj->kset = temp_kset;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	temp_str = str;
81662306a36Sopenharmony_ci	if (attr_type == HPWMI_SECURE_PLATFORM_TYPE)
81762306a36Sopenharmony_ci		temp_str = "SPM";
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ret = kobject_init_and_add(attr_name_kobj,
82062306a36Sopenharmony_ci				   &attr_name_ktype, NULL, "%s", temp_str);
82162306a36Sopenharmony_ci	if (ret) {
82262306a36Sopenharmony_ci		kobject_put(attr_name_kobj);
82362306a36Sopenharmony_ci		goto buff_attr_exit;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* enumerate all of these attributes */
82762306a36Sopenharmony_ci	switch (attr_type) {
82862306a36Sopenharmony_ci	case HPWMI_STRING_TYPE:
82962306a36Sopenharmony_ci		ret = hp_populate_string_buffer_data(buffer_ptr,
83062306a36Sopenharmony_ci						     &buffer_size,
83162306a36Sopenharmony_ci						     instance_id,
83262306a36Sopenharmony_ci						     attr_name_kobj);
83362306a36Sopenharmony_ci		break;
83462306a36Sopenharmony_ci	case HPWMI_INTEGER_TYPE:
83562306a36Sopenharmony_ci		ret = hp_populate_integer_buffer_data(buffer_ptr,
83662306a36Sopenharmony_ci						      &buffer_size,
83762306a36Sopenharmony_ci						      instance_id,
83862306a36Sopenharmony_ci						      attr_name_kobj);
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	case HPWMI_ENUMERATION_TYPE:
84162306a36Sopenharmony_ci		ret = hp_populate_enumeration_buffer_data(buffer_ptr,
84262306a36Sopenharmony_ci							  &buffer_size,
84362306a36Sopenharmony_ci							  instance_id,
84462306a36Sopenharmony_ci							  attr_name_kobj);
84562306a36Sopenharmony_ci		break;
84662306a36Sopenharmony_ci	case HPWMI_ORDERED_LIST_TYPE:
84762306a36Sopenharmony_ci		ret = hp_populate_ordered_list_buffer_data(buffer_ptr,
84862306a36Sopenharmony_ci							   &buffer_size,
84962306a36Sopenharmony_ci							   instance_id,
85062306a36Sopenharmony_ci							   attr_name_kobj);
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	case HPWMI_PASSWORD_TYPE:
85362306a36Sopenharmony_ci		ret = hp_populate_password_buffer_data(buffer_ptr,
85462306a36Sopenharmony_ci						       &buffer_size,
85562306a36Sopenharmony_ci						       instance_id,
85662306a36Sopenharmony_ci						       attr_name_kobj);
85762306a36Sopenharmony_ci		break;
85862306a36Sopenharmony_ci	default:
85962306a36Sopenharmony_ci		pr_debug("Unknown attribute type found: 0x%x\n", attr_type);
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cibuff_attr_exit:
86462306a36Sopenharmony_ci	kfree(str_value);
86562306a36Sopenharmony_ci	return ret;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci/**
86962306a36Sopenharmony_ci * hp_init_bios_attributes() - Initialize all attributes for a type
87062306a36Sopenharmony_ci * @attr_type: The attribute type to initialize
87162306a36Sopenharmony_ci * @guid: The WMI GUID associated with this type to initialize
87262306a36Sopenharmony_ci *
87362306a36Sopenharmony_ci * Initialize all 5 types of attributes: enumeration, integer,
87462306a36Sopenharmony_ci * string, password, ordered list  object.  Populates each attribute types
87562306a36Sopenharmony_ci * respective properties under sysfs files
87662306a36Sopenharmony_ci */
87762306a36Sopenharmony_cistatic int hp_init_bios_attributes(enum hp_wmi_data_type attr_type, const char *guid)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	union acpi_object *obj = NULL;
88062306a36Sopenharmony_ci	int min_elements;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* instance_id needs to be reset for each type GUID
88362306a36Sopenharmony_ci	 * also, instance IDs are unique within GUID but not across
88462306a36Sopenharmony_ci	 */
88562306a36Sopenharmony_ci	int instance_id = 0;
88662306a36Sopenharmony_ci	int cur_instance_id = instance_id;
88762306a36Sopenharmony_ci	int ret = 0;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	ret = hp_alloc_attributes_data(attr_type);
89062306a36Sopenharmony_ci	if (ret)
89162306a36Sopenharmony_ci		return ret;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	switch (attr_type) {
89462306a36Sopenharmony_ci	case HPWMI_STRING_TYPE:
89562306a36Sopenharmony_ci		min_elements = STR_ELEM_CNT;
89662306a36Sopenharmony_ci		break;
89762306a36Sopenharmony_ci	case HPWMI_INTEGER_TYPE:
89862306a36Sopenharmony_ci		min_elements = INT_ELEM_CNT;
89962306a36Sopenharmony_ci		break;
90062306a36Sopenharmony_ci	case HPWMI_ENUMERATION_TYPE:
90162306a36Sopenharmony_ci		min_elements = ENUM_ELEM_CNT;
90262306a36Sopenharmony_ci		break;
90362306a36Sopenharmony_ci	case HPWMI_ORDERED_LIST_TYPE:
90462306a36Sopenharmony_ci		min_elements = ORD_ELEM_CNT;
90562306a36Sopenharmony_ci		break;
90662306a36Sopenharmony_ci	case HPWMI_PASSWORD_TYPE:
90762306a36Sopenharmony_ci		min_elements = PSWD_ELEM_CNT;
90862306a36Sopenharmony_ci		break;
90962306a36Sopenharmony_ci	default:
91062306a36Sopenharmony_ci		pr_err("Error: Unknown attr_type: %d\n", attr_type);
91162306a36Sopenharmony_ci		return -EINVAL;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/* need to use specific instance_id and guid combination to get right data */
91562306a36Sopenharmony_ci	obj = hp_get_wmiobj_pointer(instance_id, guid);
91662306a36Sopenharmony_ci	if (!obj)
91762306a36Sopenharmony_ci		return -ENODEV;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	mutex_lock(&bioscfg_drv.mutex);
92062306a36Sopenharmony_ci	while (obj) {
92162306a36Sopenharmony_ci		/* Take action appropriate to each ACPI TYPE */
92262306a36Sopenharmony_ci		if (obj->type == ACPI_TYPE_PACKAGE) {
92362306a36Sopenharmony_ci			ret = hp_init_bios_package_attribute(attr_type, obj,
92462306a36Sopenharmony_ci							     guid, min_elements,
92562306a36Sopenharmony_ci							     cur_instance_id);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		} else if (obj->type == ACPI_TYPE_BUFFER) {
92862306a36Sopenharmony_ci			ret = hp_init_bios_buffer_attribute(attr_type, obj,
92962306a36Sopenharmony_ci							    guid, min_elements,
93062306a36Sopenharmony_ci							    cur_instance_id);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		} else {
93362306a36Sopenharmony_ci			pr_err("Expected ACPI-package or buffer type, got: %d\n",
93462306a36Sopenharmony_ci			       obj->type);
93562306a36Sopenharmony_ci			ret = -EIO;
93662306a36Sopenharmony_ci			goto err_attr_init;
93762306a36Sopenharmony_ci		}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		/*
94062306a36Sopenharmony_ci		 * Failure reported in one attribute must not
94162306a36Sopenharmony_ci		 * stop process of the remaining attribute values.
94262306a36Sopenharmony_ci		 */
94362306a36Sopenharmony_ci		if (ret >= 0)
94462306a36Sopenharmony_ci			cur_instance_id++;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		kfree(obj);
94762306a36Sopenharmony_ci		instance_id++;
94862306a36Sopenharmony_ci		obj = hp_get_wmiobj_pointer(instance_id, guid);
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cierr_attr_init:
95262306a36Sopenharmony_ci	mutex_unlock(&bioscfg_drv.mutex);
95362306a36Sopenharmony_ci	kfree(obj);
95462306a36Sopenharmony_ci	return ret;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int __init hp_init(void)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	int ret;
96062306a36Sopenharmony_ci	int hp_bios_capable = wmi_has_guid(HP_WMI_BIOS_GUID);
96162306a36Sopenharmony_ci	int set_bios_settings = wmi_has_guid(HP_WMI_SET_BIOS_SETTING_GUID);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (!hp_bios_capable) {
96462306a36Sopenharmony_ci		pr_err("Unable to run on non-HP system\n");
96562306a36Sopenharmony_ci		return -ENODEV;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (!set_bios_settings) {
96962306a36Sopenharmony_ci		pr_err("Unable to set BIOS settings on HP systems\n");
97062306a36Sopenharmony_ci		return -ENODEV;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	ret = hp_init_attr_set_interface();
97462306a36Sopenharmony_ci	if (ret)
97562306a36Sopenharmony_ci		return ret;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	ret = fw_attributes_class_get(&fw_attr_class);
97862306a36Sopenharmony_ci	if (ret)
97962306a36Sopenharmony_ci		goto err_unregister_class;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
98262306a36Sopenharmony_ci					      NULL, "%s", DRIVER_NAME);
98362306a36Sopenharmony_ci	if (IS_ERR(bioscfg_drv.class_dev)) {
98462306a36Sopenharmony_ci		ret = PTR_ERR(bioscfg_drv.class_dev);
98562306a36Sopenharmony_ci		goto err_unregister_class;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	bioscfg_drv.main_dir_kset = kset_create_and_add("attributes", NULL,
98962306a36Sopenharmony_ci							&bioscfg_drv.class_dev->kobj);
99062306a36Sopenharmony_ci	if (!bioscfg_drv.main_dir_kset) {
99162306a36Sopenharmony_ci		ret = -ENOMEM;
99262306a36Sopenharmony_ci		pr_debug("Failed to create and add attributes\n");
99362306a36Sopenharmony_ci		goto err_destroy_classdev;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	bioscfg_drv.authentication_dir_kset = kset_create_and_add("authentication", NULL,
99762306a36Sopenharmony_ci								  &bioscfg_drv.class_dev->kobj);
99862306a36Sopenharmony_ci	if (!bioscfg_drv.authentication_dir_kset) {
99962306a36Sopenharmony_ci		ret = -ENOMEM;
100062306a36Sopenharmony_ci		pr_debug("Failed to create and add authentication\n");
100162306a36Sopenharmony_ci		goto err_release_attributes_data;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/*
100562306a36Sopenharmony_ci	 * sysfs level attributes.
100662306a36Sopenharmony_ci	 * - pending_reboot
100762306a36Sopenharmony_ci	 */
100862306a36Sopenharmony_ci	ret = create_attributes_level_sysfs_files();
100962306a36Sopenharmony_ci	if (ret)
101062306a36Sopenharmony_ci		pr_debug("Failed to create sysfs level attributes\n");
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ret = hp_init_bios_attributes(HPWMI_STRING_TYPE, HP_WMI_BIOS_STRING_GUID);
101362306a36Sopenharmony_ci	if (ret)
101462306a36Sopenharmony_ci		pr_debug("Failed to populate string type attributes\n");
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	ret = hp_init_bios_attributes(HPWMI_INTEGER_TYPE, HP_WMI_BIOS_INTEGER_GUID);
101762306a36Sopenharmony_ci	if (ret)
101862306a36Sopenharmony_ci		pr_debug("Failed to populate integer type attributes\n");
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	ret = hp_init_bios_attributes(HPWMI_ENUMERATION_TYPE, HP_WMI_BIOS_ENUMERATION_GUID);
102162306a36Sopenharmony_ci	if (ret)
102262306a36Sopenharmony_ci		pr_debug("Failed to populate enumeration type attributes\n");
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	ret = hp_init_bios_attributes(HPWMI_ORDERED_LIST_TYPE, HP_WMI_BIOS_ORDERED_LIST_GUID);
102562306a36Sopenharmony_ci	if (ret)
102662306a36Sopenharmony_ci		pr_debug("Failed to populate ordered list object type attributes\n");
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	ret = hp_init_bios_attributes(HPWMI_PASSWORD_TYPE, HP_WMI_BIOS_PASSWORD_GUID);
102962306a36Sopenharmony_ci	if (ret)
103062306a36Sopenharmony_ci		pr_debug("Failed to populate password object type attributes\n");
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	bioscfg_drv.spm_data.attr_name_kobj = NULL;
103362306a36Sopenharmony_ci	ret = hp_add_other_attributes(HPWMI_SECURE_PLATFORM_TYPE);
103462306a36Sopenharmony_ci	if (ret)
103562306a36Sopenharmony_ci		pr_debug("Failed to populate secure platform object type attribute\n");
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	bioscfg_drv.sure_start_attr_kobj = NULL;
103862306a36Sopenharmony_ci	ret = hp_add_other_attributes(HPWMI_SURE_START_TYPE);
103962306a36Sopenharmony_ci	if (ret)
104062306a36Sopenharmony_ci		pr_debug("Failed to populate sure start object type attribute\n");
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return 0;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cierr_release_attributes_data:
104562306a36Sopenharmony_ci	release_attributes_data();
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cierr_destroy_classdev:
104862306a36Sopenharmony_ci	device_destroy(fw_attr_class, MKDEV(0, 0));
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cierr_unregister_class:
105162306a36Sopenharmony_ci	fw_attributes_class_put();
105262306a36Sopenharmony_ci	hp_exit_attr_set_interface();
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	return ret;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic void __exit hp_exit(void)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	release_attributes_data();
106062306a36Sopenharmony_ci	device_destroy(fw_attr_class, MKDEV(0, 0));
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	fw_attributes_class_put();
106362306a36Sopenharmony_ci	hp_exit_attr_set_interface();
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cimodule_init(hp_init);
106762306a36Sopenharmony_cimodule_exit(hp_exit);
1068