xref: /openbmc/linux/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1a34fc329SJorge Lopez // SPDX-License-Identifier: GPL-2.0
2a34fc329SJorge Lopez /*
3a34fc329SJorge Lopez  * Common methods for use with hp-bioscfg driver
4a34fc329SJorge Lopez  *
5a34fc329SJorge Lopez  *  Copyright (c) 2022 HP Development Company, L.P.
6a34fc329SJorge Lopez  */
7a34fc329SJorge Lopez 
8a34fc329SJorge Lopez #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9a34fc329SJorge Lopez 
10a34fc329SJorge Lopez #include <linux/fs.h>
11a34fc329SJorge Lopez #include <linux/module.h>
12a34fc329SJorge Lopez #include <linux/kernel.h>
13a34fc329SJorge Lopez #include <linux/wmi.h>
14a34fc329SJorge Lopez #include "bioscfg.h"
15a34fc329SJorge Lopez #include "../../firmware_attributes_class.h"
16a34fc329SJorge Lopez #include <linux/nls.h>
17a34fc329SJorge Lopez #include <linux/errno.h>
18a34fc329SJorge Lopez 
19a34fc329SJorge Lopez MODULE_AUTHOR("Jorge Lopez <jorge.lopez2@hp.com>");
20a34fc329SJorge Lopez MODULE_DESCRIPTION("HP BIOS Configuration Driver");
21a34fc329SJorge Lopez MODULE_LICENSE("GPL");
22a34fc329SJorge Lopez 
23a34fc329SJorge Lopez struct bioscfg_priv bioscfg_drv = {
24a34fc329SJorge Lopez 	.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
25a34fc329SJorge Lopez };
26a34fc329SJorge Lopez 
27a34fc329SJorge Lopez static struct class *fw_attr_class;
28a34fc329SJorge Lopez 
display_name_language_code_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)29a34fc329SJorge Lopez ssize_t display_name_language_code_show(struct kobject *kobj,
30a34fc329SJorge Lopez 					struct kobj_attribute *attr,
31a34fc329SJorge Lopez 					char *buf)
32a34fc329SJorge Lopez {
33a34fc329SJorge Lopez 	return sysfs_emit(buf, "%s\n", LANG_CODE_STR);
34a34fc329SJorge Lopez }
35a34fc329SJorge Lopez 
36a34fc329SJorge Lopez struct kobj_attribute common_display_langcode =
37a34fc329SJorge Lopez 	__ATTR_RO(display_name_language_code);
38a34fc329SJorge Lopez 
hp_get_integer_from_buffer(u8 ** buffer,u32 * buffer_size,u32 * integer)39a34fc329SJorge Lopez int hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer)
40a34fc329SJorge Lopez {
41a34fc329SJorge Lopez 	int *ptr = PTR_ALIGN((int *)*buffer, sizeof(int));
42a34fc329SJorge Lopez 
43a34fc329SJorge Lopez 	/* Ensure there is enough space remaining to read the integer */
44a34fc329SJorge Lopez 	if (*buffer_size < sizeof(int))
45a34fc329SJorge Lopez 		return -EINVAL;
46a34fc329SJorge Lopez 
47a34fc329SJorge Lopez 	*integer = *(ptr++);
48a34fc329SJorge Lopez 	*buffer = (u8 *)ptr;
49a34fc329SJorge Lopez 	*buffer_size -= sizeof(int);
50a34fc329SJorge Lopez 
51a34fc329SJorge Lopez 	return 0;
52a34fc329SJorge Lopez }
53a34fc329SJorge Lopez 
hp_get_string_from_buffer(u8 ** buffer,u32 * buffer_size,char * dst,u32 dst_size)54a34fc329SJorge Lopez int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size)
55a34fc329SJorge Lopez {
56a34fc329SJorge Lopez 	u16 *src = (u16 *)*buffer;
57a34fc329SJorge Lopez 	u16 src_size;
58a34fc329SJorge Lopez 
59a34fc329SJorge Lopez 	u16 size;
60a34fc329SJorge Lopez 	int i;
61a34fc329SJorge Lopez 	int conv_dst_size;
62a34fc329SJorge Lopez 
63a34fc329SJorge Lopez 	if (*buffer_size < sizeof(u16))
64a34fc329SJorge Lopez 		return -EINVAL;
65a34fc329SJorge Lopez 
66a34fc329SJorge Lopez 	src_size = *(src++);
67a34fc329SJorge Lopez 	/* size value in u16 chars */
68a34fc329SJorge Lopez 	size = src_size / sizeof(u16);
69a34fc329SJorge Lopez 
70a34fc329SJorge Lopez 	/* Ensure there is enough space remaining to read and convert
71a34fc329SJorge Lopez 	 * the string
72a34fc329SJorge Lopez 	 */
73a34fc329SJorge Lopez 	if (*buffer_size < src_size)
74a34fc329SJorge Lopez 		return -EINVAL;
75a34fc329SJorge Lopez 
76a34fc329SJorge Lopez 	for (i = 0; i < size; i++)
77a34fc329SJorge Lopez 		if (src[i] == '\\' ||
78a34fc329SJorge Lopez 		    src[i] == '\r' ||
79a34fc329SJorge Lopez 		    src[i] == '\n' ||
80a34fc329SJorge Lopez 		    src[i] == '\t')
81a34fc329SJorge Lopez 			size++;
82a34fc329SJorge Lopez 
83a34fc329SJorge Lopez 	/*
84a34fc329SJorge Lopez 	 * Conversion is limited to destination string max number of
85a34fc329SJorge Lopez 	 * bytes.
86a34fc329SJorge Lopez 	 */
87a34fc329SJorge Lopez 	conv_dst_size = size;
88a34fc329SJorge Lopez 	if (size > dst_size)
89a34fc329SJorge Lopez 		conv_dst_size = dst_size - 1;
90a34fc329SJorge Lopez 
91a34fc329SJorge Lopez 	/*
92a34fc329SJorge Lopez 	 * convert from UTF-16 unicode to ASCII
93a34fc329SJorge Lopez 	 */
94a34fc329SJorge Lopez 	utf16s_to_utf8s(src, src_size, UTF16_HOST_ENDIAN, dst, conv_dst_size);
95a34fc329SJorge Lopez 	dst[conv_dst_size] = 0;
96a34fc329SJorge Lopez 
97b3a8692dSDan Carpenter 	for (i = 0; i < conv_dst_size; i++) {
98a34fc329SJorge Lopez 		if (*src == '\\' ||
99a34fc329SJorge Lopez 		    *src == '\r' ||
100a34fc329SJorge Lopez 		    *src == '\n' ||
101b3a8692dSDan Carpenter 		    *src == '\t') {
102a34fc329SJorge Lopez 			dst[i++] = '\\';
103b3a8692dSDan Carpenter 			if (i == conv_dst_size)
104b3a8692dSDan Carpenter 				break;
105b3a8692dSDan Carpenter 		}
106a34fc329SJorge Lopez 
107a34fc329SJorge Lopez 		if (*src == '\r')
108a34fc329SJorge Lopez 			dst[i] = 'r';
109a34fc329SJorge Lopez 		else if (*src == '\n')
110a34fc329SJorge Lopez 			dst[i] = 'n';
111a34fc329SJorge Lopez 		else if (*src == '\t')
112a34fc329SJorge Lopez 			dst[i] = 't';
113a34fc329SJorge Lopez 		else if (*src == '"')
114a34fc329SJorge Lopez 			dst[i] = '\'';
115a34fc329SJorge Lopez 		else
116a34fc329SJorge Lopez 			dst[i] = *src;
117a34fc329SJorge Lopez 		src++;
118a34fc329SJorge Lopez 	}
119a34fc329SJorge Lopez 
120a34fc329SJorge Lopez 	*buffer = (u8 *)src;
121a34fc329SJorge Lopez 	*buffer_size -= size * sizeof(u16);
122a34fc329SJorge Lopez 
123a34fc329SJorge Lopez 	return size;
124a34fc329SJorge Lopez }
125a34fc329SJorge Lopez 
hp_get_common_data_from_buffer(u8 ** buffer_ptr,u32 * buffer_size,struct common_data * common_data)126a34fc329SJorge Lopez int hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size,
127a34fc329SJorge Lopez 				   struct common_data *common_data)
128a34fc329SJorge Lopez {
129a34fc329SJorge Lopez 	int ret = 0;
130a34fc329SJorge Lopez 	int reqs;
131a34fc329SJorge Lopez 
132a34fc329SJorge Lopez 	// PATH:
133a34fc329SJorge Lopez 	ret = hp_get_string_from_buffer(buffer_ptr, buffer_size, common_data->path,
134a34fc329SJorge Lopez 					sizeof(common_data->path));
135a34fc329SJorge Lopez 	if (ret < 0)
136a34fc329SJorge Lopez 		goto common_exit;
137a34fc329SJorge Lopez 
138a34fc329SJorge Lopez 	// IS_READONLY:
139a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
140a34fc329SJorge Lopez 					 &common_data->is_readonly);
141a34fc329SJorge Lopez 	if (ret < 0)
142a34fc329SJorge Lopez 		goto common_exit;
143a34fc329SJorge Lopez 
144a34fc329SJorge Lopez 	//DISPLAY_IN_UI:
145a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
146a34fc329SJorge Lopez 					 &common_data->display_in_ui);
147a34fc329SJorge Lopez 	if (ret < 0)
148a34fc329SJorge Lopez 		goto common_exit;
149a34fc329SJorge Lopez 
150a34fc329SJorge Lopez 	// REQUIRES_PHYSICAL_PRESENCE:
151a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
152a34fc329SJorge Lopez 					 &common_data->requires_physical_presence);
153a34fc329SJorge Lopez 	if (ret < 0)
154a34fc329SJorge Lopez 		goto common_exit;
155a34fc329SJorge Lopez 
156a34fc329SJorge Lopez 	// SEQUENCE:
157a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
158a34fc329SJorge Lopez 					 &common_data->sequence);
159a34fc329SJorge Lopez 	if (ret < 0)
160a34fc329SJorge Lopez 		goto common_exit;
161a34fc329SJorge Lopez 
162a34fc329SJorge Lopez 	// PREREQUISITES_SIZE:
163a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
164a34fc329SJorge Lopez 					 &common_data->prerequisites_size);
165a34fc329SJorge Lopez 	if (ret < 0)
166a34fc329SJorge Lopez 		goto common_exit;
167a34fc329SJorge Lopez 
168a34fc329SJorge Lopez 	if (common_data->prerequisites_size > MAX_PREREQUISITES_SIZE) {
169a34fc329SJorge Lopez 		/* Report a message and limit prerequisite size to maximum value */
170a34fc329SJorge Lopez 		pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
171a34fc329SJorge Lopez 		common_data->prerequisites_size = MAX_PREREQUISITES_SIZE;
172a34fc329SJorge Lopez 	}
173a34fc329SJorge Lopez 
174a34fc329SJorge Lopez 	// PREREQUISITES:
175a34fc329SJorge Lopez 	for (reqs = 0; reqs < common_data->prerequisites_size; reqs++) {
176a34fc329SJorge Lopez 		ret = hp_get_string_from_buffer(buffer_ptr, buffer_size,
177a34fc329SJorge Lopez 						common_data->prerequisites[reqs],
178a34fc329SJorge Lopez 						sizeof(common_data->prerequisites[reqs]));
179a34fc329SJorge Lopez 		if (ret < 0)
180a34fc329SJorge Lopez 			break;
181a34fc329SJorge Lopez 	}
182a34fc329SJorge Lopez 
183a34fc329SJorge Lopez 	// SECURITY_LEVEL:
184a34fc329SJorge Lopez 	ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
185a34fc329SJorge Lopez 					 &common_data->security_level);
186a34fc329SJorge Lopez 
187a34fc329SJorge Lopez common_exit:
188a34fc329SJorge Lopez 	return ret;
189a34fc329SJorge Lopez }
190a34fc329SJorge Lopez 
hp_enforce_single_line_input(char * buf,size_t count)191a34fc329SJorge Lopez int hp_enforce_single_line_input(char *buf, size_t count)
192a34fc329SJorge Lopez {
193a34fc329SJorge Lopez 	char *p;
194a34fc329SJorge Lopez 
195a34fc329SJorge Lopez 	p = memchr(buf, '\n', count);
196a34fc329SJorge Lopez 
197a34fc329SJorge Lopez 	if (p == buf + count - 1)
198a34fc329SJorge Lopez 		*p = '\0'; /* strip trailing newline */
199a34fc329SJorge Lopez 	else if (p)
200a34fc329SJorge Lopez 		return -EINVAL;  /* enforce single line input */
201a34fc329SJorge Lopez 
202a34fc329SJorge Lopez 	return 0;
203a34fc329SJorge Lopez }
204a34fc329SJorge Lopez 
205a34fc329SJorge Lopez /* Set pending reboot value and generate KOBJ_NAME event */
hp_set_reboot_and_signal_event(void)206a34fc329SJorge Lopez void hp_set_reboot_and_signal_event(void)
207a34fc329SJorge Lopez {
208a34fc329SJorge Lopez 	bioscfg_drv.pending_reboot = true;
209a34fc329SJorge Lopez 	kobject_uevent(&bioscfg_drv.class_dev->kobj, KOBJ_CHANGE);
210a34fc329SJorge Lopez }
211a34fc329SJorge Lopez 
212a34fc329SJorge Lopez /**
213a34fc329SJorge Lopez  * hp_calculate_string_buffer() - determines size of string buffer for
214a34fc329SJorge Lopez  * use with BIOS communication
215a34fc329SJorge Lopez  *
216a34fc329SJorge Lopez  * @str: the string to calculate based upon
217a34fc329SJorge Lopez  */
hp_calculate_string_buffer(const char * str)218a34fc329SJorge Lopez size_t hp_calculate_string_buffer(const char *str)
219a34fc329SJorge Lopez {
220a34fc329SJorge Lopez 	size_t length = strlen(str);
221a34fc329SJorge Lopez 
222a34fc329SJorge Lopez 	/* BIOS expects 4 bytes when an empty string is found */
223a34fc329SJorge Lopez 	if (length == 0)
224a34fc329SJorge Lopez 		return 4;
225a34fc329SJorge Lopez 
226a34fc329SJorge Lopez 	/* u16 length field + one UTF16 char for each input char */
227a34fc329SJorge Lopez 	return sizeof(u16) + strlen(str) * sizeof(u16);
228a34fc329SJorge Lopez }
229a34fc329SJorge Lopez 
hp_wmi_error_and_message(int error_code)230a34fc329SJorge Lopez int hp_wmi_error_and_message(int error_code)
231a34fc329SJorge Lopez {
232a34fc329SJorge Lopez 	char *error_msg = NULL;
233a34fc329SJorge Lopez 	int ret;
234a34fc329SJorge Lopez 
235a34fc329SJorge Lopez 	switch (error_code) {
236a34fc329SJorge Lopez 	case SUCCESS:
237a34fc329SJorge Lopez 		error_msg = "Success";
238a34fc329SJorge Lopez 		ret = 0;
239a34fc329SJorge Lopez 		break;
240a34fc329SJorge Lopez 	case CMD_FAILED:
241a34fc329SJorge Lopez 		error_msg = "Command failed";
242a34fc329SJorge Lopez 		ret = -EINVAL;
243a34fc329SJorge Lopez 		break;
244a34fc329SJorge Lopez 	case INVALID_SIGN:
245a34fc329SJorge Lopez 		error_msg = "Invalid signature";
246a34fc329SJorge Lopez 		ret = -EINVAL;
247a34fc329SJorge Lopez 		break;
248a34fc329SJorge Lopez 	case INVALID_CMD_VALUE:
249a34fc329SJorge Lopez 		error_msg = "Invalid command value/Feature not supported";
250a34fc329SJorge Lopez 		ret = -EOPNOTSUPP;
251a34fc329SJorge Lopez 		break;
252a34fc329SJorge Lopez 	case INVALID_CMD_TYPE:
253a34fc329SJorge Lopez 		error_msg = "Invalid command type";
254a34fc329SJorge Lopez 		ret = -EINVAL;
255a34fc329SJorge Lopez 		break;
256a34fc329SJorge Lopez 	case INVALID_DATA_SIZE:
257a34fc329SJorge Lopez 		error_msg = "Invalid data size";
258a34fc329SJorge Lopez 		ret = -EINVAL;
259a34fc329SJorge Lopez 		break;
260a34fc329SJorge Lopez 	case INVALID_CMD_PARAM:
261a34fc329SJorge Lopez 		error_msg = "Invalid command parameter";
262a34fc329SJorge Lopez 		ret = -EINVAL;
263a34fc329SJorge Lopez 		break;
264a34fc329SJorge Lopez 	case ENCRYP_CMD_REQUIRED:
265a34fc329SJorge Lopez 		error_msg = "Secure/encrypted command required";
266a34fc329SJorge Lopez 		ret = -EACCES;
267a34fc329SJorge Lopez 		break;
268a34fc329SJorge Lopez 	case NO_SECURE_SESSION:
269a34fc329SJorge Lopez 		error_msg = "No secure session established";
270a34fc329SJorge Lopez 		ret = -EACCES;
271a34fc329SJorge Lopez 		break;
272a34fc329SJorge Lopez 	case SECURE_SESSION_FOUND:
273a34fc329SJorge Lopez 		error_msg = "Secure session already established";
274a34fc329SJorge Lopez 		ret = -EACCES;
275a34fc329SJorge Lopez 		break;
276a34fc329SJorge Lopez 	case SECURE_SESSION_FAILED:
277a34fc329SJorge Lopez 		error_msg = "Secure session failed";
278a34fc329SJorge Lopez 		ret = -EIO;
279a34fc329SJorge Lopez 		break;
280a34fc329SJorge Lopez 	case AUTH_FAILED:
281a34fc329SJorge Lopez 		error_msg = "Other permission/Authentication failed";
282a34fc329SJorge Lopez 		ret = -EACCES;
283a34fc329SJorge Lopez 		break;
284a34fc329SJorge Lopez 	case INVALID_BIOS_AUTH:
285a34fc329SJorge Lopez 		error_msg = "Invalid BIOS administrator password";
286a34fc329SJorge Lopez 		ret = -EINVAL;
287a34fc329SJorge Lopez 		break;
288a34fc329SJorge Lopez 	case NONCE_DID_NOT_MATCH:
289a34fc329SJorge Lopez 		error_msg = "Nonce did not match";
290a34fc329SJorge Lopez 		ret = -EINVAL;
291a34fc329SJorge Lopez 		break;
292a34fc329SJorge Lopez 	case GENERIC_ERROR:
293a34fc329SJorge Lopez 		error_msg = "Generic/Other error";
294a34fc329SJorge Lopez 		ret = -EIO;
295a34fc329SJorge Lopez 		break;
296a34fc329SJorge Lopez 	case BIOS_ADMIN_POLICY_NOT_MET:
297a34fc329SJorge Lopez 		error_msg = "BIOS Admin password does not meet password policy requirements";
298a34fc329SJorge Lopez 		ret = -EINVAL;
299a34fc329SJorge Lopez 		break;
300a34fc329SJorge Lopez 	case BIOS_ADMIN_NOT_SET:
301a34fc329SJorge Lopez 		error_msg = "BIOS Setup password is not set";
302a34fc329SJorge Lopez 		ret = -EPERM;
303a34fc329SJorge Lopez 		break;
304a34fc329SJorge Lopez 	case P21_NO_PROVISIONED:
305a34fc329SJorge Lopez 		error_msg = "P21 is not provisioned";
306a34fc329SJorge Lopez 		ret = -EPERM;
307a34fc329SJorge Lopez 		break;
308a34fc329SJorge Lopez 	case P21_PROVISION_IN_PROGRESS:
309a34fc329SJorge Lopez 		error_msg = "P21 is already provisioned or provisioning is in progress and a signing key has already been sent";
310a34fc329SJorge Lopez 		ret = -EINPROGRESS;
311a34fc329SJorge Lopez 		break;
312a34fc329SJorge Lopez 	case P21_IN_USE:
313a34fc329SJorge Lopez 		error_msg = "P21 in use (cannot deprovision)";
314a34fc329SJorge Lopez 		ret = -EPERM;
315a34fc329SJorge Lopez 		break;
316a34fc329SJorge Lopez 	case HEP_NOT_ACTIVE:
317a34fc329SJorge Lopez 		error_msg = "HEP not activated";
318a34fc329SJorge Lopez 		ret = -EPERM;
319a34fc329SJorge Lopez 		break;
320a34fc329SJorge Lopez 	case HEP_ALREADY_SET:
321a34fc329SJorge Lopez 		error_msg = "HEP Transport already set";
322a34fc329SJorge Lopez 		ret = -EINVAL;
323a34fc329SJorge Lopez 		break;
324a34fc329SJorge Lopez 	case HEP_CHECK_STATE:
325a34fc329SJorge Lopez 		error_msg = "Check the current HEP state";
326a34fc329SJorge Lopez 		ret = -EINVAL;
327a34fc329SJorge Lopez 		break;
328a34fc329SJorge Lopez 	default:
329a34fc329SJorge Lopez 		error_msg = "Generic/Other error";
330a34fc329SJorge Lopez 		ret = -EIO;
331a34fc329SJorge Lopez 		break;
332a34fc329SJorge Lopez 	}
333a34fc329SJorge Lopez 
334a34fc329SJorge Lopez 	if (error_code)
335a34fc329SJorge Lopez 		pr_warn_ratelimited("Returned error 0x%x, \"%s\"\n", error_code, error_msg);
336a34fc329SJorge Lopez 
337a34fc329SJorge Lopez 	return ret;
338a34fc329SJorge Lopez }
339a34fc329SJorge Lopez 
pending_reboot_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)340a34fc329SJorge Lopez static ssize_t pending_reboot_show(struct kobject *kobj,
341a34fc329SJorge Lopez 				   struct kobj_attribute *attr,
342a34fc329SJorge Lopez 				   char *buf)
343a34fc329SJorge Lopez {
344a34fc329SJorge Lopez 	return sysfs_emit(buf, "%d\n", bioscfg_drv.pending_reboot);
345a34fc329SJorge Lopez }
346a34fc329SJorge Lopez 
347a34fc329SJorge Lopez static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
348a34fc329SJorge Lopez 
349a34fc329SJorge Lopez /*
350a34fc329SJorge Lopez  * create_attributes_level_sysfs_files() - Creates pending_reboot attributes
351a34fc329SJorge Lopez  */
create_attributes_level_sysfs_files(void)352a34fc329SJorge Lopez static int create_attributes_level_sysfs_files(void)
353a34fc329SJorge Lopez {
354a34fc329SJorge Lopez 	return  sysfs_create_file(&bioscfg_drv.main_dir_kset->kobj,
355a34fc329SJorge Lopez 				  &pending_reboot.attr);
356a34fc329SJorge Lopez }
357a34fc329SJorge Lopez 
attr_name_release(struct kobject * kobj)358a34fc329SJorge Lopez static void attr_name_release(struct kobject *kobj)
359a34fc329SJorge Lopez {
360a34fc329SJorge Lopez 	kfree(kobj);
361a34fc329SJorge Lopez }
362a34fc329SJorge Lopez 
363a34fc329SJorge Lopez static const struct kobj_type attr_name_ktype = {
364a34fc329SJorge Lopez 	.release	= attr_name_release,
365a34fc329SJorge Lopez 	.sysfs_ops	= &kobj_sysfs_ops,
366a34fc329SJorge Lopez };
367a34fc329SJorge Lopez 
368a34fc329SJorge Lopez /**
369a34fc329SJorge Lopez  * hp_get_wmiobj_pointer() - Get Content of WMI block for particular instance
370a34fc329SJorge Lopez  *
371a34fc329SJorge Lopez  * @instance_id: WMI instance ID
372a34fc329SJorge Lopez  * @guid_string: WMI GUID (in str form)
373a34fc329SJorge Lopez  *
374a34fc329SJorge Lopez  * Fetches the content for WMI block (instance_id) under GUID (guid_string)
375a34fc329SJorge Lopez  * Caller must kfree the return
376a34fc329SJorge Lopez  */
hp_get_wmiobj_pointer(int instance_id,const char * guid_string)377a34fc329SJorge Lopez union acpi_object *hp_get_wmiobj_pointer(int instance_id, const char *guid_string)
378a34fc329SJorge Lopez {
379a34fc329SJorge Lopez 	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
380a34fc329SJorge Lopez 	acpi_status status;
381a34fc329SJorge Lopez 
382a34fc329SJorge Lopez 	status = wmi_query_block(guid_string, instance_id, &out);
383a34fc329SJorge Lopez 	return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL;
384a34fc329SJorge Lopez }
385a34fc329SJorge Lopez 
386a34fc329SJorge Lopez /**
387a34fc329SJorge Lopez  * hp_get_instance_count() - Compute total number of instances under guid_string
388a34fc329SJorge Lopez  *
389a34fc329SJorge Lopez  * @guid_string: WMI GUID (in string form)
390a34fc329SJorge Lopez  */
hp_get_instance_count(const char * guid_string)391a34fc329SJorge Lopez int hp_get_instance_count(const char *guid_string)
392a34fc329SJorge Lopez {
393a34fc329SJorge Lopez 	union acpi_object *wmi_obj = NULL;
394a34fc329SJorge Lopez 	int i = 0;
395a34fc329SJorge Lopez 
396a34fc329SJorge Lopez 	do {
397a34fc329SJorge Lopez 		kfree(wmi_obj);
398a34fc329SJorge Lopez 		wmi_obj = hp_get_wmiobj_pointer(i, guid_string);
399a34fc329SJorge Lopez 		i++;
400a34fc329SJorge Lopez 	} while (wmi_obj);
401a34fc329SJorge Lopez 
402a34fc329SJorge Lopez 	return i - 1;
403a34fc329SJorge Lopez }
404a34fc329SJorge Lopez 
405a34fc329SJorge Lopez /**
406a34fc329SJorge Lopez  * hp_alloc_attributes_data() - Allocate attributes data for a particular type
407a34fc329SJorge Lopez  *
408a34fc329SJorge Lopez  * @attr_type: Attribute type to allocate
409a34fc329SJorge Lopez  */
hp_alloc_attributes_data(int attr_type)410a34fc329SJorge Lopez static int hp_alloc_attributes_data(int attr_type)
411a34fc329SJorge Lopez {
412a34fc329SJorge Lopez 	switch (attr_type) {
413a34fc329SJorge Lopez 	case HPWMI_STRING_TYPE:
414a34fc329SJorge Lopez 		return hp_alloc_string_data();
415a34fc329SJorge Lopez 
416a34fc329SJorge Lopez 	case HPWMI_INTEGER_TYPE:
417a34fc329SJorge Lopez 		return hp_alloc_integer_data();
418a34fc329SJorge Lopez 
419a34fc329SJorge Lopez 	case HPWMI_ENUMERATION_TYPE:
420a34fc329SJorge Lopez 		return hp_alloc_enumeration_data();
421a34fc329SJorge Lopez 
422a34fc329SJorge Lopez 	case HPWMI_ORDERED_LIST_TYPE:
423a34fc329SJorge Lopez 		return hp_alloc_ordered_list_data();
424a34fc329SJorge Lopez 
425a34fc329SJorge Lopez 	case HPWMI_PASSWORD_TYPE:
426a34fc329SJorge Lopez 		return hp_alloc_password_data();
427a34fc329SJorge Lopez 
428a34fc329SJorge Lopez 	default:
429a34fc329SJorge Lopez 		return 0;
430a34fc329SJorge Lopez 	}
431a34fc329SJorge Lopez }
432a34fc329SJorge Lopez 
hp_convert_hexstr_to_str(const char * input,u32 input_len,char ** str,int * len)433a34fc329SJorge Lopez int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *len)
434a34fc329SJorge Lopez {
435a34fc329SJorge Lopez 	int ret = 0;
436a34fc329SJorge Lopez 	int new_len = 0;
437a34fc329SJorge Lopez 	char tmp[] = "0x00";
438a34fc329SJorge Lopez 	char *new_str = NULL;
439a34fc329SJorge Lopez 	long  ch;
440a34fc329SJorge Lopez 	int i;
441a34fc329SJorge Lopez 
442a34fc329SJorge Lopez 	if (input_len <= 0 || !input || !str || !len)
443a34fc329SJorge Lopez 		return -EINVAL;
444a34fc329SJorge Lopez 
445a34fc329SJorge Lopez 	*len = 0;
446a34fc329SJorge Lopez 	*str = NULL;
447a34fc329SJorge Lopez 
448a34fc329SJorge Lopez 	new_str = kmalloc(input_len, GFP_KERNEL);
449a34fc329SJorge Lopez 	if (!new_str)
450a34fc329SJorge Lopez 		return -ENOMEM;
451a34fc329SJorge Lopez 
452a34fc329SJorge Lopez 	for (i = 0; i < input_len; i += 5) {
453a34fc329SJorge Lopez 		strncpy(tmp, input + i, strlen(tmp));
454a34fc329SJorge Lopez 		if (kstrtol(tmp, 16, &ch) == 0) {
455a34fc329SJorge Lopez 			// escape char
456a34fc329SJorge Lopez 			if (ch == '\\' ||
457a34fc329SJorge Lopez 			    ch == '\r' ||
458a34fc329SJorge Lopez 			    ch == '\n' || ch == '\t') {
459a34fc329SJorge Lopez 				if (ch == '\r')
460a34fc329SJorge Lopez 					ch = 'r';
461a34fc329SJorge Lopez 				else if (ch == '\n')
462a34fc329SJorge Lopez 					ch = 'n';
463a34fc329SJorge Lopez 				else if (ch == '\t')
464a34fc329SJorge Lopez 					ch = 't';
465a34fc329SJorge Lopez 				new_str[new_len++] = '\\';
466a34fc329SJorge Lopez 			}
467a34fc329SJorge Lopez 			new_str[new_len++] = ch;
468a34fc329SJorge Lopez 			if (ch == '\0')
469a34fc329SJorge Lopez 				break;
470a34fc329SJorge Lopez 		}
471a34fc329SJorge Lopez 	}
472a34fc329SJorge Lopez 
473a34fc329SJorge Lopez 	if (new_len) {
474a34fc329SJorge Lopez 		new_str[new_len] = '\0';
475a34fc329SJorge Lopez 		*str = krealloc(new_str, (new_len + 1) * sizeof(char),
476a34fc329SJorge Lopez 				GFP_KERNEL);
477a34fc329SJorge Lopez 		if (*str)
478a34fc329SJorge Lopez 			*len = new_len;
479a34fc329SJorge Lopez 		else
480a34fc329SJorge Lopez 			ret = -ENOMEM;
481a34fc329SJorge Lopez 	} else {
482a34fc329SJorge Lopez 		ret = -EFAULT;
483a34fc329SJorge Lopez 	}
484a34fc329SJorge Lopez 
485a34fc329SJorge Lopez 	if (ret)
486a34fc329SJorge Lopez 		kfree(new_str);
487a34fc329SJorge Lopez 	return ret;
488a34fc329SJorge Lopez }
489a34fc329SJorge Lopez 
490a34fc329SJorge Lopez /* map output size to the corresponding WMI method id */
hp_encode_outsize_for_pvsz(int outsize)491a34fc329SJorge Lopez int hp_encode_outsize_for_pvsz(int outsize)
492a34fc329SJorge Lopez {
493a34fc329SJorge Lopez 	if (outsize > 4096)
494a34fc329SJorge Lopez 		return -EINVAL;
495a34fc329SJorge Lopez 	if (outsize > 1024)
496a34fc329SJorge Lopez 		return 5;
497a34fc329SJorge Lopez 	if (outsize > 128)
498a34fc329SJorge Lopez 		return 4;
499a34fc329SJorge Lopez 	if (outsize > 4)
500a34fc329SJorge Lopez 		return 3;
501a34fc329SJorge Lopez 	if (outsize > 0)
502a34fc329SJorge Lopez 		return 2;
503a34fc329SJorge Lopez 	return 1;
504a34fc329SJorge Lopez }
505a34fc329SJorge Lopez 
506a34fc329SJorge Lopez /*
507a34fc329SJorge Lopez  * Update friendly display name for several attributes associated to
508a34fc329SJorge Lopez  * 'Schedule Power-On'
509a34fc329SJorge Lopez  */
hp_friendly_user_name_update(char * path,const char * attr_name,char * attr_display,int attr_size)510a34fc329SJorge Lopez void hp_friendly_user_name_update(char *path, const char *attr_name,
511a34fc329SJorge Lopez 				  char *attr_display, int attr_size)
512a34fc329SJorge Lopez {
513a34fc329SJorge Lopez 	if (strstr(path, SCHEDULE_POWER_ON))
514a34fc329SJorge Lopez 		snprintf(attr_display, attr_size, "%s - %s", SCHEDULE_POWER_ON, attr_name);
515a34fc329SJorge Lopez 	else
516a34fc329SJorge Lopez 		strscpy(attr_display, attr_name, attr_size);
517a34fc329SJorge Lopez }
518a34fc329SJorge Lopez 
519a34fc329SJorge Lopez /**
520a34fc329SJorge Lopez  * hp_update_attribute_permissions() - Update attributes permissions when
521a34fc329SJorge Lopez  * isReadOnly value is 1
522a34fc329SJorge Lopez  *
523a34fc329SJorge Lopez  * @is_readonly:  bool value to indicate if it a readonly attribute.
524a34fc329SJorge Lopez  * @current_val: kobj_attribute corresponding to attribute.
525a34fc329SJorge Lopez  *
526a34fc329SJorge Lopez  */
hp_update_attribute_permissions(bool is_readonly,struct kobj_attribute * current_val)527a34fc329SJorge Lopez void hp_update_attribute_permissions(bool is_readonly, struct kobj_attribute *current_val)
528a34fc329SJorge Lopez {
529a34fc329SJorge Lopez 	current_val->attr.mode = is_readonly ? 0444 : 0644;
530a34fc329SJorge Lopez }
531a34fc329SJorge Lopez 
532a34fc329SJorge Lopez /**
533a34fc329SJorge Lopez  * destroy_attribute_objs() - Free a kset of kobjects
534a34fc329SJorge Lopez  * @kset: The kset to destroy
535a34fc329SJorge Lopez  *
536a34fc329SJorge Lopez  * Fress kobjects created for each attribute_name under attribute type kset
537a34fc329SJorge Lopez  */
destroy_attribute_objs(struct kset * kset)538a34fc329SJorge Lopez static void destroy_attribute_objs(struct kset *kset)
539a34fc329SJorge Lopez {
540a34fc329SJorge Lopez 	struct kobject *pos, *next;
541a34fc329SJorge Lopez 
542a34fc329SJorge Lopez 	list_for_each_entry_safe(pos, next, &kset->list, entry)
543a34fc329SJorge Lopez 		kobject_put(pos);
544a34fc329SJorge Lopez }
545a34fc329SJorge Lopez 
546a34fc329SJorge Lopez /**
547a34fc329SJorge Lopez  * release_attributes_data() - Clean-up all sysfs directories and files created
548a34fc329SJorge Lopez  */
release_attributes_data(void)549a34fc329SJorge Lopez static void release_attributes_data(void)
550a34fc329SJorge Lopez {
551a34fc329SJorge Lopez 	mutex_lock(&bioscfg_drv.mutex);
552a34fc329SJorge Lopez 
553a34fc329SJorge Lopez 	hp_exit_string_attributes();
554a34fc329SJorge Lopez 	hp_exit_integer_attributes();
555a34fc329SJorge Lopez 	hp_exit_enumeration_attributes();
556a34fc329SJorge Lopez 	hp_exit_ordered_list_attributes();
557a34fc329SJorge Lopez 	hp_exit_password_attributes();
558a34fc329SJorge Lopez 	hp_exit_sure_start_attributes();
559a34fc329SJorge Lopez 	hp_exit_secure_platform_attributes();
560a34fc329SJorge Lopez 
561a34fc329SJorge Lopez 	if (bioscfg_drv.authentication_dir_kset) {
562a34fc329SJorge Lopez 		destroy_attribute_objs(bioscfg_drv.authentication_dir_kset);
563a34fc329SJorge Lopez 		kset_unregister(bioscfg_drv.authentication_dir_kset);
564a34fc329SJorge Lopez 		bioscfg_drv.authentication_dir_kset = NULL;
565a34fc329SJorge Lopez 	}
566a34fc329SJorge Lopez 	if (bioscfg_drv.main_dir_kset) {
567a34fc329SJorge Lopez 		sysfs_remove_file(&bioscfg_drv.main_dir_kset->kobj, &pending_reboot.attr);
568a34fc329SJorge Lopez 		destroy_attribute_objs(bioscfg_drv.main_dir_kset);
569a34fc329SJorge Lopez 		kset_unregister(bioscfg_drv.main_dir_kset);
570a34fc329SJorge Lopez 		bioscfg_drv.main_dir_kset = NULL;
571a34fc329SJorge Lopez 	}
572a34fc329SJorge Lopez 	mutex_unlock(&bioscfg_drv.mutex);
573a34fc329SJorge Lopez }
574a34fc329SJorge Lopez 
575a34fc329SJorge Lopez /**
576a34fc329SJorge Lopez  * hp_add_other_attributes() - Initialize HP custom attributes not
577a34fc329SJorge Lopez  * reported by BIOS and required to support Secure Platform and Sure
578a34fc329SJorge Lopez  * Start.
579a34fc329SJorge Lopez  *
580a34fc329SJorge Lopez  * @attr_type: Custom HP attribute not reported by BIOS
581a34fc329SJorge Lopez  *
582a34fc329SJorge Lopez  * Initialize all 2 types of attributes: Platform and Sure Start
583a34fc329SJorge Lopez  * object.  Populates each attribute types respective properties
584a34fc329SJorge Lopez  * under sysfs files.
585a34fc329SJorge Lopez  *
586a34fc329SJorge Lopez  * Returns zero(0) if successful. Otherwise, a negative value.
587a34fc329SJorge Lopez  */
hp_add_other_attributes(int attr_type)588a34fc329SJorge Lopez static int hp_add_other_attributes(int attr_type)
589a34fc329SJorge Lopez {
590a34fc329SJorge Lopez 	struct kobject *attr_name_kobj;
591a34fc329SJorge Lopez 	union acpi_object *obj = NULL;
592a34fc329SJorge Lopez 	int ret;
593a34fc329SJorge Lopez 	char *attr_name;
594a34fc329SJorge Lopez 
595a34fc329SJorge Lopez 	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
596e73ebb93SHarshit Mogalapalli 	if (!attr_name_kobj)
597e73ebb93SHarshit Mogalapalli 		return -ENOMEM;
598e73ebb93SHarshit Mogalapalli 
599e73ebb93SHarshit Mogalapalli 	mutex_lock(&bioscfg_drv.mutex);
600a34fc329SJorge Lopez 
601a34fc329SJorge Lopez 	/* Check if attribute type is supported */
602a34fc329SJorge Lopez 	switch (attr_type) {
603a34fc329SJorge Lopez 	case HPWMI_SECURE_PLATFORM_TYPE:
604a34fc329SJorge Lopez 		attr_name_kobj->kset = bioscfg_drv.authentication_dir_kset;
605a34fc329SJorge Lopez 		attr_name = SPM_STR;
606a34fc329SJorge Lopez 		break;
607a34fc329SJorge Lopez 
608a34fc329SJorge Lopez 	case HPWMI_SURE_START_TYPE:
609a34fc329SJorge Lopez 		attr_name_kobj->kset = bioscfg_drv.main_dir_kset;
610a34fc329SJorge Lopez 		attr_name = SURE_START_STR;
611a34fc329SJorge Lopez 		break;
612a34fc329SJorge Lopez 
613a34fc329SJorge Lopez 	default:
614a34fc329SJorge Lopez 		pr_err("Error: Unknown attr_type: %d\n", attr_type);
615a34fc329SJorge Lopez 		ret = -EINVAL;
616*9a98ab01SHarshit Mogalapalli 		kfree(attr_name_kobj);
617*9a98ab01SHarshit Mogalapalli 		goto unlock_drv_mutex;
618a34fc329SJorge Lopez 	}
619a34fc329SJorge Lopez 
620a34fc329SJorge Lopez 	ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype,
621a34fc329SJorge Lopez 				   NULL, "%s", attr_name);
622a34fc329SJorge Lopez 	if (ret) {
623a34fc329SJorge Lopez 		pr_err("Error encountered [%d]\n", ret);
624a34fc329SJorge Lopez 		goto err_other_attr_init;
625a34fc329SJorge Lopez 	}
626a34fc329SJorge Lopez 
627a34fc329SJorge Lopez 	/* Populate attribute data */
628a34fc329SJorge Lopez 	switch (attr_type) {
629a34fc329SJorge Lopez 	case HPWMI_SECURE_PLATFORM_TYPE:
630a34fc329SJorge Lopez 		ret = hp_populate_secure_platform_data(attr_name_kobj);
631a34fc329SJorge Lopez 		break;
632a34fc329SJorge Lopez 
633a34fc329SJorge Lopez 	case HPWMI_SURE_START_TYPE:
634a34fc329SJorge Lopez 		ret = hp_populate_sure_start_data(attr_name_kobj);
635a34fc329SJorge Lopez 		break;
636a34fc329SJorge Lopez 
637a34fc329SJorge Lopez 	default:
638d4e695c0SDan Carpenter 		ret = -EINVAL;
639a34fc329SJorge Lopez 	}
640a34fc329SJorge Lopez 
641a9dc6f2eSHarshit Mogalapalli 	if (ret)
642a9dc6f2eSHarshit Mogalapalli 		goto err_other_attr_init;
643a9dc6f2eSHarshit Mogalapalli 
644a34fc329SJorge Lopez 	mutex_unlock(&bioscfg_drv.mutex);
645a34fc329SJorge Lopez 	return 0;
646a34fc329SJorge Lopez 
647a34fc329SJorge Lopez err_other_attr_init:
648*9a98ab01SHarshit Mogalapalli 	kobject_put(attr_name_kobj);
649*9a98ab01SHarshit Mogalapalli unlock_drv_mutex:
650a34fc329SJorge Lopez 	mutex_unlock(&bioscfg_drv.mutex);
651a34fc329SJorge Lopez 	kfree(obj);
652a34fc329SJorge Lopez 	return ret;
653a34fc329SJorge Lopez }
654a34fc329SJorge Lopez 
hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type,union acpi_object * obj,const char * guid,int min_elements,int instance_id)655a34fc329SJorge Lopez static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type,
656a34fc329SJorge Lopez 					  union acpi_object *obj,
657a34fc329SJorge Lopez 					  const char *guid, int min_elements,
658a34fc329SJorge Lopez 					  int instance_id)
659a34fc329SJorge Lopez {
660981368e1SArmin Wolf 	struct kobject *attr_name_kobj, *duplicate;
661a34fc329SJorge Lopez 	union acpi_object *elements;
662a34fc329SJorge Lopez 	struct kset *temp_kset;
663a34fc329SJorge Lopez 
664a34fc329SJorge Lopez 	char *str_value = NULL;
665a34fc329SJorge Lopez 	int str_len;
666a34fc329SJorge Lopez 	int ret = 0;
667a34fc329SJorge Lopez 
668a34fc329SJorge Lopez 	/* Take action appropriate to each ACPI TYPE */
669a34fc329SJorge Lopez 	if (obj->package.count < min_elements) {
670a34fc329SJorge Lopez 		pr_err("ACPI-package does not have enough elements: %d < %d\n",
671a34fc329SJorge Lopez 		       obj->package.count, min_elements);
672a34fc329SJorge Lopez 		goto pack_attr_exit;
673a34fc329SJorge Lopez 	}
674a34fc329SJorge Lopez 
675a34fc329SJorge Lopez 	elements = obj->package.elements;
676a34fc329SJorge Lopez 
677a34fc329SJorge Lopez 	/* sanity checking */
678a34fc329SJorge Lopez 	if (elements[NAME].type != ACPI_TYPE_STRING) {
679a34fc329SJorge Lopez 		pr_debug("incorrect element type\n");
680a34fc329SJorge Lopez 		goto pack_attr_exit;
681a34fc329SJorge Lopez 	}
682a34fc329SJorge Lopez 	if (strlen(elements[NAME].string.pointer) == 0) {
683a34fc329SJorge Lopez 		pr_debug("empty attribute found\n");
684a34fc329SJorge Lopez 		goto pack_attr_exit;
685a34fc329SJorge Lopez 	}
686a34fc329SJorge Lopez 
687a34fc329SJorge Lopez 	if (attr_type == HPWMI_PASSWORD_TYPE)
688a34fc329SJorge Lopez 		temp_kset = bioscfg_drv.authentication_dir_kset;
689a34fc329SJorge Lopez 	else
690a34fc329SJorge Lopez 		temp_kset = bioscfg_drv.main_dir_kset;
691a34fc329SJorge Lopez 
692a34fc329SJorge Lopez 	/* convert attribute name to string */
693a34fc329SJorge Lopez 	ret = hp_convert_hexstr_to_str(elements[NAME].string.pointer,
694a34fc329SJorge Lopez 				       elements[NAME].string.length,
695a34fc329SJorge Lopez 				       &str_value, &str_len);
696a34fc329SJorge Lopez 
697a34fc329SJorge Lopez 	if (ret) {
698a34fc329SJorge Lopez 		pr_debug("Failed to populate integer package data. Error [0%0x]\n",
699a34fc329SJorge Lopez 			 ret);
700a34fc329SJorge Lopez 		kfree(str_value);
701a34fc329SJorge Lopez 		return ret;
702a34fc329SJorge Lopez 	}
703a34fc329SJorge Lopez 
704a34fc329SJorge Lopez 	/* All duplicate attributes found are ignored */
705981368e1SArmin Wolf 	duplicate = kset_find_obj(temp_kset, str_value);
706981368e1SArmin Wolf 	if (duplicate) {
707a34fc329SJorge Lopez 		pr_debug("Duplicate attribute name found - %s\n", str_value);
708981368e1SArmin Wolf 		/* kset_find_obj() returns a reference */
709981368e1SArmin Wolf 		kobject_put(duplicate);
710a34fc329SJorge Lopez 		goto pack_attr_exit;
711a34fc329SJorge Lopez 	}
712a34fc329SJorge Lopez 
713a34fc329SJorge Lopez 	/* build attribute */
714a34fc329SJorge Lopez 	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
715a34fc329SJorge Lopez 	if (!attr_name_kobj) {
716a34fc329SJorge Lopez 		ret = -ENOMEM;
717a34fc329SJorge Lopez 		goto pack_attr_exit;
718a34fc329SJorge Lopez 	}
719a34fc329SJorge Lopez 
720a34fc329SJorge Lopez 	attr_name_kobj->kset = temp_kset;
721a34fc329SJorge Lopez 
722a34fc329SJorge Lopez 	ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype,
723a34fc329SJorge Lopez 				   NULL, "%s", str_value);
724a34fc329SJorge Lopez 
725a34fc329SJorge Lopez 	if (ret) {
726a34fc329SJorge Lopez 		kobject_put(attr_name_kobj);
727a34fc329SJorge Lopez 		goto pack_attr_exit;
728a34fc329SJorge Lopez 	}
729a34fc329SJorge Lopez 
730a34fc329SJorge Lopez 	/* enumerate all of these attributes */
731a34fc329SJorge Lopez 	switch (attr_type) {
732a34fc329SJorge Lopez 	case HPWMI_STRING_TYPE:
733a34fc329SJorge Lopez 		ret = hp_populate_string_package_data(elements,
734a34fc329SJorge Lopez 						      instance_id,
735a34fc329SJorge Lopez 						      attr_name_kobj);
736a34fc329SJorge Lopez 		break;
737a34fc329SJorge Lopez 	case HPWMI_INTEGER_TYPE:
738a34fc329SJorge Lopez 		ret = hp_populate_integer_package_data(elements,
739a34fc329SJorge Lopez 						       instance_id,
740a34fc329SJorge Lopez 						       attr_name_kobj);
741a34fc329SJorge Lopez 		break;
742a34fc329SJorge Lopez 	case HPWMI_ENUMERATION_TYPE:
743a34fc329SJorge Lopez 		ret = hp_populate_enumeration_package_data(elements,
744a34fc329SJorge Lopez 							   instance_id,
745a34fc329SJorge Lopez 							   attr_name_kobj);
746a34fc329SJorge Lopez 		break;
747a34fc329SJorge Lopez 	case HPWMI_ORDERED_LIST_TYPE:
748a34fc329SJorge Lopez 		ret = hp_populate_ordered_list_package_data(elements,
749a34fc329SJorge Lopez 							    instance_id,
750a34fc329SJorge Lopez 							    attr_name_kobj);
751a34fc329SJorge Lopez 		break;
752a34fc329SJorge Lopez 	case HPWMI_PASSWORD_TYPE:
753a34fc329SJorge Lopez 		ret = hp_populate_password_package_data(elements,
754a34fc329SJorge Lopez 							instance_id,
755a34fc329SJorge Lopez 							attr_name_kobj);
756a34fc329SJorge Lopez 		break;
757a34fc329SJorge Lopez 	default:
758a34fc329SJorge Lopez 		pr_debug("Unknown attribute type found: 0x%x\n", attr_type);
759a34fc329SJorge Lopez 		break;
760a34fc329SJorge Lopez 	}
761a34fc329SJorge Lopez 
762a34fc329SJorge Lopez pack_attr_exit:
763a34fc329SJorge Lopez 	kfree(str_value);
764a34fc329SJorge Lopez 	return ret;
765a34fc329SJorge Lopez }
766a34fc329SJorge Lopez 
hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type,union acpi_object * obj,const char * guid,int min_elements,int instance_id)767a34fc329SJorge Lopez static int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type,
768a34fc329SJorge Lopez 					 union acpi_object *obj,
769a34fc329SJorge Lopez 					 const char *guid, int min_elements,
770a34fc329SJorge Lopez 					 int instance_id)
771a34fc329SJorge Lopez {
772981368e1SArmin Wolf 	struct kobject *attr_name_kobj, *duplicate;
773a34fc329SJorge Lopez 	struct kset *temp_kset;
774a34fc329SJorge Lopez 	char str[MAX_BUFF_SIZE];
775a34fc329SJorge Lopez 
776a34fc329SJorge Lopez 	char *temp_str = NULL;
777a34fc329SJorge Lopez 	char *str_value = NULL;
778a34fc329SJorge Lopez 	u8 *buffer_ptr = NULL;
779a34fc329SJorge Lopez 	int buffer_size;
780a34fc329SJorge Lopez 	int ret = 0;
781a34fc329SJorge Lopez 
782a34fc329SJorge Lopez 	buffer_size = obj->buffer.length;
783a34fc329SJorge Lopez 	buffer_ptr = obj->buffer.pointer;
784a34fc329SJorge Lopez 
785a34fc329SJorge Lopez 	ret = hp_get_string_from_buffer(&buffer_ptr,
786a34fc329SJorge Lopez 					&buffer_size, str, MAX_BUFF_SIZE);
787a34fc329SJorge Lopez 
788a34fc329SJorge Lopez 	if (ret < 0)
789a34fc329SJorge Lopez 		goto buff_attr_exit;
790a34fc329SJorge Lopez 
791a34fc329SJorge Lopez 	if (attr_type == HPWMI_PASSWORD_TYPE ||
792a34fc329SJorge Lopez 	    attr_type == HPWMI_SECURE_PLATFORM_TYPE)
793a34fc329SJorge Lopez 		temp_kset = bioscfg_drv.authentication_dir_kset;
794a34fc329SJorge Lopez 	else
795a34fc329SJorge Lopez 		temp_kset = bioscfg_drv.main_dir_kset;
796a34fc329SJorge Lopez 
797a34fc329SJorge Lopez 	/* All duplicate attributes found are ignored */
798981368e1SArmin Wolf 	duplicate = kset_find_obj(temp_kset, str);
799981368e1SArmin Wolf 	if (duplicate) {
800a34fc329SJorge Lopez 		pr_debug("Duplicate attribute name found - %s\n", str);
801981368e1SArmin Wolf 		/* kset_find_obj() returns a reference */
802981368e1SArmin Wolf 		kobject_put(duplicate);
803a34fc329SJorge Lopez 		goto buff_attr_exit;
804a34fc329SJorge Lopez 	}
805a34fc329SJorge Lopez 
806a34fc329SJorge Lopez 	/* build attribute */
807a34fc329SJorge Lopez 	attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL);
808a34fc329SJorge Lopez 	if (!attr_name_kobj) {
809a34fc329SJorge Lopez 		ret = -ENOMEM;
810a34fc329SJorge Lopez 		goto buff_attr_exit;
811a34fc329SJorge Lopez 	}
812a34fc329SJorge Lopez 
813a34fc329SJorge Lopez 	attr_name_kobj->kset = temp_kset;
814a34fc329SJorge Lopez 
815a34fc329SJorge Lopez 	temp_str = str;
816a34fc329SJorge Lopez 	if (attr_type == HPWMI_SECURE_PLATFORM_TYPE)
817a34fc329SJorge Lopez 		temp_str = "SPM";
818a34fc329SJorge Lopez 
819a34fc329SJorge Lopez 	ret = kobject_init_and_add(attr_name_kobj,
820a34fc329SJorge Lopez 				   &attr_name_ktype, NULL, "%s", temp_str);
821a34fc329SJorge Lopez 	if (ret) {
822a34fc329SJorge Lopez 		kobject_put(attr_name_kobj);
823a34fc329SJorge Lopez 		goto buff_attr_exit;
824a34fc329SJorge Lopez 	}
825a34fc329SJorge Lopez 
826a34fc329SJorge Lopez 	/* enumerate all of these attributes */
827a34fc329SJorge Lopez 	switch (attr_type) {
828a34fc329SJorge Lopez 	case HPWMI_STRING_TYPE:
829a34fc329SJorge Lopez 		ret = hp_populate_string_buffer_data(buffer_ptr,
830a34fc329SJorge Lopez 						     &buffer_size,
831a34fc329SJorge Lopez 						     instance_id,
832a34fc329SJorge Lopez 						     attr_name_kobj);
833a34fc329SJorge Lopez 		break;
834a34fc329SJorge Lopez 	case HPWMI_INTEGER_TYPE:
835a34fc329SJorge Lopez 		ret = hp_populate_integer_buffer_data(buffer_ptr,
836a34fc329SJorge Lopez 						      &buffer_size,
837a34fc329SJorge Lopez 						      instance_id,
838a34fc329SJorge Lopez 						      attr_name_kobj);
839a34fc329SJorge Lopez 		break;
840a34fc329SJorge Lopez 	case HPWMI_ENUMERATION_TYPE:
841a34fc329SJorge Lopez 		ret = hp_populate_enumeration_buffer_data(buffer_ptr,
842a34fc329SJorge Lopez 							  &buffer_size,
843a34fc329SJorge Lopez 							  instance_id,
844a34fc329SJorge Lopez 							  attr_name_kobj);
845a34fc329SJorge Lopez 		break;
846a34fc329SJorge Lopez 	case HPWMI_ORDERED_LIST_TYPE:
847a34fc329SJorge Lopez 		ret = hp_populate_ordered_list_buffer_data(buffer_ptr,
848a34fc329SJorge Lopez 							   &buffer_size,
849a34fc329SJorge Lopez 							   instance_id,
850a34fc329SJorge Lopez 							   attr_name_kobj);
851a34fc329SJorge Lopez 		break;
852a34fc329SJorge Lopez 	case HPWMI_PASSWORD_TYPE:
853a34fc329SJorge Lopez 		ret = hp_populate_password_buffer_data(buffer_ptr,
854a34fc329SJorge Lopez 						       &buffer_size,
855a34fc329SJorge Lopez 						       instance_id,
856a34fc329SJorge Lopez 						       attr_name_kobj);
857a34fc329SJorge Lopez 		break;
858a34fc329SJorge Lopez 	default:
859a34fc329SJorge Lopez 		pr_debug("Unknown attribute type found: 0x%x\n", attr_type);
860a34fc329SJorge Lopez 		break;
861a34fc329SJorge Lopez 	}
862a34fc329SJorge Lopez 
863a34fc329SJorge Lopez buff_attr_exit:
864a34fc329SJorge Lopez 	kfree(str_value);
865a34fc329SJorge Lopez 	return ret;
866a34fc329SJorge Lopez }
867a34fc329SJorge Lopez 
868a34fc329SJorge Lopez /**
869a34fc329SJorge Lopez  * hp_init_bios_attributes() - Initialize all attributes for a type
870a34fc329SJorge Lopez  * @attr_type: The attribute type to initialize
871a34fc329SJorge Lopez  * @guid: The WMI GUID associated with this type to initialize
872a34fc329SJorge Lopez  *
873a34fc329SJorge Lopez  * Initialize all 5 types of attributes: enumeration, integer,
874a34fc329SJorge Lopez  * string, password, ordered list  object.  Populates each attribute types
875a34fc329SJorge Lopez  * respective properties under sysfs files
876a34fc329SJorge Lopez  */
hp_init_bios_attributes(enum hp_wmi_data_type attr_type,const char * guid)877a34fc329SJorge Lopez static int hp_init_bios_attributes(enum hp_wmi_data_type attr_type, const char *guid)
878a34fc329SJorge Lopez {
879a34fc329SJorge Lopez 	union acpi_object *obj = NULL;
880a34fc329SJorge Lopez 	int min_elements;
881a34fc329SJorge Lopez 
882a34fc329SJorge Lopez 	/* instance_id needs to be reset for each type GUID
883a34fc329SJorge Lopez 	 * also, instance IDs are unique within GUID but not across
884a34fc329SJorge Lopez 	 */
885a34fc329SJorge Lopez 	int instance_id = 0;
886a34fc329SJorge Lopez 	int cur_instance_id = instance_id;
887a34fc329SJorge Lopez 	int ret = 0;
888a34fc329SJorge Lopez 
889a34fc329SJorge Lopez 	ret = hp_alloc_attributes_data(attr_type);
890a34fc329SJorge Lopez 	if (ret)
891a34fc329SJorge Lopez 		return ret;
892a34fc329SJorge Lopez 
893a34fc329SJorge Lopez 	switch (attr_type) {
894a34fc329SJorge Lopez 	case HPWMI_STRING_TYPE:
895a34fc329SJorge Lopez 		min_elements = STR_ELEM_CNT;
896a34fc329SJorge Lopez 		break;
897a34fc329SJorge Lopez 	case HPWMI_INTEGER_TYPE:
898a34fc329SJorge Lopez 		min_elements = INT_ELEM_CNT;
899a34fc329SJorge Lopez 		break;
900a34fc329SJorge Lopez 	case HPWMI_ENUMERATION_TYPE:
901a34fc329SJorge Lopez 		min_elements = ENUM_ELEM_CNT;
902a34fc329SJorge Lopez 		break;
903a34fc329SJorge Lopez 	case HPWMI_ORDERED_LIST_TYPE:
904a34fc329SJorge Lopez 		min_elements = ORD_ELEM_CNT;
905a34fc329SJorge Lopez 		break;
906a34fc329SJorge Lopez 	case HPWMI_PASSWORD_TYPE:
907a34fc329SJorge Lopez 		min_elements = PSWD_ELEM_CNT;
908a34fc329SJorge Lopez 		break;
909a34fc329SJorge Lopez 	default:
910a34fc329SJorge Lopez 		pr_err("Error: Unknown attr_type: %d\n", attr_type);
911a34fc329SJorge Lopez 		return -EINVAL;
912a34fc329SJorge Lopez 	}
913a34fc329SJorge Lopez 
914a34fc329SJorge Lopez 	/* need to use specific instance_id and guid combination to get right data */
915a34fc329SJorge Lopez 	obj = hp_get_wmiobj_pointer(instance_id, guid);
916a34fc329SJorge Lopez 	if (!obj)
917a34fc329SJorge Lopez 		return -ENODEV;
918a34fc329SJorge Lopez 
919a34fc329SJorge Lopez 	mutex_lock(&bioscfg_drv.mutex);
920a34fc329SJorge Lopez 	while (obj) {
921a34fc329SJorge Lopez 		/* Take action appropriate to each ACPI TYPE */
922a34fc329SJorge Lopez 		if (obj->type == ACPI_TYPE_PACKAGE) {
923a34fc329SJorge Lopez 			ret = hp_init_bios_package_attribute(attr_type, obj,
924a34fc329SJorge Lopez 							     guid, min_elements,
925a34fc329SJorge Lopez 							     cur_instance_id);
926a34fc329SJorge Lopez 
927a34fc329SJorge Lopez 		} else if (obj->type == ACPI_TYPE_BUFFER) {
928a34fc329SJorge Lopez 			ret = hp_init_bios_buffer_attribute(attr_type, obj,
929a34fc329SJorge Lopez 							    guid, min_elements,
930a34fc329SJorge Lopez 							    cur_instance_id);
931a34fc329SJorge Lopez 
932a34fc329SJorge Lopez 		} else {
933a34fc329SJorge Lopez 			pr_err("Expected ACPI-package or buffer type, got: %d\n",
934a34fc329SJorge Lopez 			       obj->type);
935a34fc329SJorge Lopez 			ret = -EIO;
936a34fc329SJorge Lopez 			goto err_attr_init;
937a34fc329SJorge Lopez 		}
938a34fc329SJorge Lopez 
939a34fc329SJorge Lopez 		/*
940a34fc329SJorge Lopez 		 * Failure reported in one attribute must not
941a34fc329SJorge Lopez 		 * stop process of the remaining attribute values.
942a34fc329SJorge Lopez 		 */
943a34fc329SJorge Lopez 		if (ret >= 0)
944a34fc329SJorge Lopez 			cur_instance_id++;
945a34fc329SJorge Lopez 
946a34fc329SJorge Lopez 		kfree(obj);
947a34fc329SJorge Lopez 		instance_id++;
948a34fc329SJorge Lopez 		obj = hp_get_wmiobj_pointer(instance_id, guid);
949a34fc329SJorge Lopez 	}
950a34fc329SJorge Lopez 
951a34fc329SJorge Lopez err_attr_init:
952a34fc329SJorge Lopez 	mutex_unlock(&bioscfg_drv.mutex);
953a34fc329SJorge Lopez 	kfree(obj);
954a34fc329SJorge Lopez 	return ret;
955a34fc329SJorge Lopez }
956a34fc329SJorge Lopez 
hp_init(void)957a34fc329SJorge Lopez static int __init hp_init(void)
958a34fc329SJorge Lopez {
959a34fc329SJorge Lopez 	int ret;
960a34fc329SJorge Lopez 	int hp_bios_capable = wmi_has_guid(HP_WMI_BIOS_GUID);
961a34fc329SJorge Lopez 	int set_bios_settings = wmi_has_guid(HP_WMI_SET_BIOS_SETTING_GUID);
962a34fc329SJorge Lopez 
963a34fc329SJorge Lopez 	if (!hp_bios_capable) {
964a34fc329SJorge Lopez 		pr_err("Unable to run on non-HP system\n");
965a34fc329SJorge Lopez 		return -ENODEV;
966a34fc329SJorge Lopez 	}
967a34fc329SJorge Lopez 
968a34fc329SJorge Lopez 	if (!set_bios_settings) {
969a34fc329SJorge Lopez 		pr_err("Unable to set BIOS settings on HP systems\n");
970a34fc329SJorge Lopez 		return -ENODEV;
971a34fc329SJorge Lopez 	}
972a34fc329SJorge Lopez 
973a34fc329SJorge Lopez 	ret = hp_init_attr_set_interface();
974a34fc329SJorge Lopez 	if (ret)
975a34fc329SJorge Lopez 		return ret;
976a34fc329SJorge Lopez 
977a34fc329SJorge Lopez 	ret = fw_attributes_class_get(&fw_attr_class);
978a34fc329SJorge Lopez 	if (ret)
979a34fc329SJorge Lopez 		goto err_unregister_class;
980a34fc329SJorge Lopez 
981a34fc329SJorge Lopez 	bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
982a34fc329SJorge Lopez 					      NULL, "%s", DRIVER_NAME);
983a34fc329SJorge Lopez 	if (IS_ERR(bioscfg_drv.class_dev)) {
984a34fc329SJorge Lopez 		ret = PTR_ERR(bioscfg_drv.class_dev);
985a34fc329SJorge Lopez 		goto err_unregister_class;
986a34fc329SJorge Lopez 	}
987a34fc329SJorge Lopez 
988a34fc329SJorge Lopez 	bioscfg_drv.main_dir_kset = kset_create_and_add("attributes", NULL,
989a34fc329SJorge Lopez 							&bioscfg_drv.class_dev->kobj);
990a34fc329SJorge Lopez 	if (!bioscfg_drv.main_dir_kset) {
991a34fc329SJorge Lopez 		ret = -ENOMEM;
992a34fc329SJorge Lopez 		pr_debug("Failed to create and add attributes\n");
993a34fc329SJorge Lopez 		goto err_destroy_classdev;
994a34fc329SJorge Lopez 	}
995a34fc329SJorge Lopez 
996a34fc329SJorge Lopez 	bioscfg_drv.authentication_dir_kset = kset_create_and_add("authentication", NULL,
997a34fc329SJorge Lopez 								  &bioscfg_drv.class_dev->kobj);
998a34fc329SJorge Lopez 	if (!bioscfg_drv.authentication_dir_kset) {
999a34fc329SJorge Lopez 		ret = -ENOMEM;
1000a34fc329SJorge Lopez 		pr_debug("Failed to create and add authentication\n");
1001a34fc329SJorge Lopez 		goto err_release_attributes_data;
1002a34fc329SJorge Lopez 	}
1003a34fc329SJorge Lopez 
1004a34fc329SJorge Lopez 	/*
1005a34fc329SJorge Lopez 	 * sysfs level attributes.
1006a34fc329SJorge Lopez 	 * - pending_reboot
1007a34fc329SJorge Lopez 	 */
1008a34fc329SJorge Lopez 	ret = create_attributes_level_sysfs_files();
1009a34fc329SJorge Lopez 	if (ret)
1010a34fc329SJorge Lopez 		pr_debug("Failed to create sysfs level attributes\n");
1011a34fc329SJorge Lopez 
1012a34fc329SJorge Lopez 	ret = hp_init_bios_attributes(HPWMI_STRING_TYPE, HP_WMI_BIOS_STRING_GUID);
1013a34fc329SJorge Lopez 	if (ret)
1014a34fc329SJorge Lopez 		pr_debug("Failed to populate string type attributes\n");
1015a34fc329SJorge Lopez 
1016a34fc329SJorge Lopez 	ret = hp_init_bios_attributes(HPWMI_INTEGER_TYPE, HP_WMI_BIOS_INTEGER_GUID);
1017a34fc329SJorge Lopez 	if (ret)
1018a34fc329SJorge Lopez 		pr_debug("Failed to populate integer type attributes\n");
1019a34fc329SJorge Lopez 
1020a34fc329SJorge Lopez 	ret = hp_init_bios_attributes(HPWMI_ENUMERATION_TYPE, HP_WMI_BIOS_ENUMERATION_GUID);
1021a34fc329SJorge Lopez 	if (ret)
1022a34fc329SJorge Lopez 		pr_debug("Failed to populate enumeration type attributes\n");
1023a34fc329SJorge Lopez 
1024a34fc329SJorge Lopez 	ret = hp_init_bios_attributes(HPWMI_ORDERED_LIST_TYPE, HP_WMI_BIOS_ORDERED_LIST_GUID);
1025a34fc329SJorge Lopez 	if (ret)
1026a34fc329SJorge Lopez 		pr_debug("Failed to populate ordered list object type attributes\n");
1027a34fc329SJorge Lopez 
1028a34fc329SJorge Lopez 	ret = hp_init_bios_attributes(HPWMI_PASSWORD_TYPE, HP_WMI_BIOS_PASSWORD_GUID);
1029a34fc329SJorge Lopez 	if (ret)
1030a34fc329SJorge Lopez 		pr_debug("Failed to populate password object type attributes\n");
1031a34fc329SJorge Lopez 
1032a34fc329SJorge Lopez 	bioscfg_drv.spm_data.attr_name_kobj = NULL;
1033a34fc329SJorge Lopez 	ret = hp_add_other_attributes(HPWMI_SECURE_PLATFORM_TYPE);
1034a34fc329SJorge Lopez 	if (ret)
1035a34fc329SJorge Lopez 		pr_debug("Failed to populate secure platform object type attribute\n");
1036a34fc329SJorge Lopez 
1037a34fc329SJorge Lopez 	bioscfg_drv.sure_start_attr_kobj = NULL;
1038a34fc329SJorge Lopez 	ret = hp_add_other_attributes(HPWMI_SURE_START_TYPE);
1039a34fc329SJorge Lopez 	if (ret)
1040a34fc329SJorge Lopez 		pr_debug("Failed to populate sure start object type attribute\n");
1041a34fc329SJorge Lopez 
1042a34fc329SJorge Lopez 	return 0;
1043a34fc329SJorge Lopez 
1044a34fc329SJorge Lopez err_release_attributes_data:
1045a34fc329SJorge Lopez 	release_attributes_data();
1046a34fc329SJorge Lopez 
1047a34fc329SJorge Lopez err_destroy_classdev:
1048a34fc329SJorge Lopez 	device_destroy(fw_attr_class, MKDEV(0, 0));
1049a34fc329SJorge Lopez 
1050a34fc329SJorge Lopez err_unregister_class:
1051a34fc329SJorge Lopez 	fw_attributes_class_put();
1052a34fc329SJorge Lopez 	hp_exit_attr_set_interface();
1053a34fc329SJorge Lopez 
1054a34fc329SJorge Lopez 	return ret;
1055a34fc329SJorge Lopez }
1056a34fc329SJorge Lopez 
hp_exit(void)1057a34fc329SJorge Lopez static void __exit hp_exit(void)
1058a34fc329SJorge Lopez {
1059a34fc329SJorge Lopez 	release_attributes_data();
1060a34fc329SJorge Lopez 	device_destroy(fw_attr_class, MKDEV(0, 0));
1061a34fc329SJorge Lopez 
1062a34fc329SJorge Lopez 	fw_attributes_class_put();
1063a34fc329SJorge Lopez 	hp_exit_attr_set_interface();
1064a34fc329SJorge Lopez }
1065a34fc329SJorge Lopez 
1066a34fc329SJorge Lopez module_init(hp_init);
1067a34fc329SJorge Lopez module_exit(hp_exit);
1068