1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Functions corresponding to ordered list type attributes under
4 * BIOS ORDERED LIST GUID for use with hp-bioscfg driver.
5 *
6 * Copyright (c) 2022 HP Development Company, L.P.
7 */
8
9#include "bioscfg.h"
10
11GET_INSTANCE_ID(ordered_list);
12
13static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
14{
15	int instance_id = get_ordered_list_instance_id(kobj);
16
17	if (instance_id < 0)
18		return -EIO;
19
20	return sysfs_emit(buf, "%s\n",
21			 bioscfg_drv.ordered_list_data[instance_id].current_value);
22}
23
24static int replace_char_str(u8 *buffer, char *repl_char, char *repl_with)
25{
26	char *src = buffer;
27	int buflen = strlen(buffer);
28	int item;
29
30	if (buflen < 1)
31		return -EINVAL;
32
33	for (item = 0; item < buflen; item++)
34		if (src[item] == *repl_char)
35			src[item] = *repl_with;
36
37	return 0;
38}
39
40/**
41 * validate_ordered_list_input() -
42 * Validate input of current_value against possible values
43 *
44 * @instance: The instance on which input is validated
45 * @buf: Input value
46 */
47static int validate_ordered_list_input(int instance, char *buf)
48{
49	/* validation is done by BIOS. This validation function will
50	 * convert semicolon to commas. BIOS uses commas as
51	 * separators when reporting ordered-list values.
52	 */
53	return replace_char_str(buf, SEMICOLON_SEP, COMMA_SEP);
54}
55
56static void update_ordered_list_value(int instance, char *attr_value)
57{
58	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance];
59
60	strscpy(ordered_list_data->current_value,
61		attr_value,
62		sizeof(ordered_list_data->current_value));
63}
64
65ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, ordered_list);
66static struct kobj_attribute ordered_list_display_name =
67	__ATTR_RO(display_name);
68
69ATTRIBUTE_PROPERTY_STORE(current_value, ordered_list);
70static struct kobj_attribute ordered_list_current_val =
71	__ATTR_RW_MODE(current_value, 0644);
72
73ATTRIBUTE_VALUES_PROPERTY_SHOW(elements, ordered_list, SEMICOLON_SEP);
74static struct kobj_attribute ordered_list_elements_val =
75	__ATTR_RO(elements);
76
77static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
78			 char *buf)
79{
80	return sysfs_emit(buf, "ordered-list\n");
81}
82
83static struct kobj_attribute ordered_list_type =
84	__ATTR_RO(type);
85
86static struct attribute *ordered_list_attrs[] = {
87	&common_display_langcode.attr,
88	&ordered_list_display_name.attr,
89	&ordered_list_current_val.attr,
90	&ordered_list_elements_val.attr,
91	&ordered_list_type.attr,
92	NULL
93};
94
95static const struct attribute_group ordered_list_attr_group = {
96	.attrs = ordered_list_attrs,
97};
98
99int hp_alloc_ordered_list_data(void)
100{
101	bioscfg_drv.ordered_list_instances_count =
102		hp_get_instance_count(HP_WMI_BIOS_ORDERED_LIST_GUID);
103	bioscfg_drv.ordered_list_data = kcalloc(bioscfg_drv.ordered_list_instances_count,
104						sizeof(*bioscfg_drv.ordered_list_data),
105						GFP_KERNEL);
106	if (!bioscfg_drv.ordered_list_data) {
107		bioscfg_drv.ordered_list_instances_count = 0;
108		return -ENOMEM;
109	}
110	return 0;
111}
112
113/* Expected Values types associated with each element */
114static const acpi_object_type expected_order_types[] = {
115	[NAME]	= ACPI_TYPE_STRING,
116	[VALUE] = ACPI_TYPE_STRING,
117	[PATH] = ACPI_TYPE_STRING,
118	[IS_READONLY] = ACPI_TYPE_INTEGER,
119	[DISPLAY_IN_UI] = ACPI_TYPE_INTEGER,
120	[REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER,
121	[SEQUENCE] = ACPI_TYPE_INTEGER,
122	[PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER,
123	[PREREQUISITES] = ACPI_TYPE_STRING,
124	[SECURITY_LEVEL] = ACPI_TYPE_INTEGER,
125	[ORD_LIST_SIZE] = ACPI_TYPE_INTEGER,
126	[ORD_LIST_ELEMENTS] = ACPI_TYPE_STRING,
127};
128
129static int hp_populate_ordered_list_elements_from_package(union acpi_object *order_obj,
130							  int order_obj_count,
131							  int instance_id)
132{
133	char *str_value = NULL;
134	int value_len = 0;
135	int ret;
136	u32 size;
137	u32 int_value = 0;
138	int elem;
139	int olist_elem;
140	int reqs;
141	int eloc;
142	char *tmpstr = NULL;
143	char *part_tmp = NULL;
144	int tmp_len = 0;
145	char *part = NULL;
146	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
147
148	if (!order_obj)
149		return -EINVAL;
150
151	for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) {
152
153		switch (order_obj[elem].type) {
154		case ACPI_TYPE_STRING:
155			if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) {
156				ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer,
157							       order_obj[elem].string.length,
158							       &str_value, &value_len);
159				if (ret)
160					continue;
161			}
162			break;
163		case ACPI_TYPE_INTEGER:
164			int_value = (u32)order_obj[elem].integer.value;
165			break;
166		default:
167			pr_warn("Unsupported object type [%d]\n", order_obj[elem].type);
168			continue;
169		}
170
171		/* Check that both expected and read object type match */
172		if (expected_order_types[eloc] != order_obj[elem].type) {
173			pr_err("Error expected type %d for elem %d, but got type %d instead\n",
174			       expected_order_types[eloc], elem, order_obj[elem].type);
175			kfree(str_value);
176			return -EIO;
177		}
178
179		/* Assign appropriate element value to corresponding field*/
180		switch (eloc) {
181		case VALUE:
182			strscpy(ordered_list_data->current_value,
183				str_value, sizeof(ordered_list_data->current_value));
184			replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP);
185			break;
186		case PATH:
187			strscpy(ordered_list_data->common.path, str_value,
188				sizeof(ordered_list_data->common.path));
189			break;
190		case IS_READONLY:
191			ordered_list_data->common.is_readonly = int_value;
192			break;
193		case DISPLAY_IN_UI:
194			ordered_list_data->common.display_in_ui = int_value;
195			break;
196		case REQUIRES_PHYSICAL_PRESENCE:
197			ordered_list_data->common.requires_physical_presence = int_value;
198			break;
199		case SEQUENCE:
200			ordered_list_data->common.sequence = int_value;
201			break;
202		case PREREQUISITES_SIZE:
203			if (int_value > MAX_PREREQUISITES_SIZE) {
204				pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
205				int_value = MAX_PREREQUISITES_SIZE;
206			}
207			ordered_list_data->common.prerequisites_size = int_value;
208
209			/*
210			 * This step is needed to keep the expected
211			 * element list pointing to the right obj[elem].type
212			 * when the size is zero. PREREQUISITES
213			 * object is omitted by BIOS when the size is
214			 * zero.
215			 */
216			if (int_value == 0)
217				eloc++;
218			break;
219		case PREREQUISITES:
220			size = min_t(u32, ordered_list_data->common.prerequisites_size,
221				     MAX_PREREQUISITES_SIZE);
222			for (reqs = 0; reqs < size; reqs++) {
223				ret = hp_convert_hexstr_to_str(order_obj[elem + reqs].string.pointer,
224							       order_obj[elem + reqs].string.length,
225							       &str_value, &value_len);
226
227				if (ret)
228					continue;
229
230				strscpy(ordered_list_data->common.prerequisites[reqs],
231					str_value,
232					sizeof(ordered_list_data->common.prerequisites[reqs]));
233
234				kfree(str_value);
235				str_value = NULL;
236			}
237			break;
238
239		case SECURITY_LEVEL:
240			ordered_list_data->common.security_level = int_value;
241			break;
242
243		case ORD_LIST_SIZE:
244			if (int_value > MAX_ELEMENTS_SIZE) {
245				pr_warn("Order List size value exceeded the maximum number of elements supported or data may be malformed\n");
246				int_value = MAX_ELEMENTS_SIZE;
247			}
248			ordered_list_data->elements_size = int_value;
249
250			/*
251			 * This step is needed to keep the expected
252			 * element list pointing to the right obj[elem].type
253			 * when the size is zero. ORD_LIST_ELEMENTS
254			 * object is omitted by BIOS when the size is
255			 * zero.
256			 */
257			if (int_value == 0)
258				eloc++;
259			break;
260		case ORD_LIST_ELEMENTS:
261
262			/*
263			 * Ordered list data is stored in hex and comma separated format
264			 * Convert the data and split it to show each element
265			 */
266			ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);
267			if (ret)
268				goto exit_list;
269
270			part_tmp = tmpstr;
271			part = strsep(&part_tmp, COMMA_SEP);
272
273			for (olist_elem = 0; olist_elem < MAX_ELEMENTS_SIZE && part; olist_elem++) {
274				strscpy(ordered_list_data->elements[olist_elem],
275					part,
276					sizeof(ordered_list_data->elements[olist_elem]));
277				part = strsep(&part_tmp, COMMA_SEP);
278			}
279			ordered_list_data->elements_size = olist_elem;
280
281			kfree(str_value);
282			str_value = NULL;
283			break;
284		default:
285			pr_warn("Invalid element: %d found in Ordered_List attribute or data may be malformed\n", elem);
286			break;
287		}
288		kfree(tmpstr);
289		tmpstr = NULL;
290		kfree(str_value);
291		str_value = NULL;
292	}
293
294exit_list:
295	kfree(tmpstr);
296	kfree(str_value);
297	return 0;
298}
299
300/**
301 * hp_populate_ordered_list_package_data() -
302 * Populate all properties of an instance under ordered_list attribute
303 *
304 * @order_obj: ACPI object with ordered_list data
305 * @instance_id: The instance to enumerate
306 * @attr_name_kobj: The parent kernel object
307 */
308int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id,
309					  struct kobject *attr_name_kobj)
310{
311	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
312
313	ordered_list_data->attr_name_kobj = attr_name_kobj;
314
315	hp_populate_ordered_list_elements_from_package(order_obj,
316						       order_obj->package.count,
317						       instance_id);
318	hp_update_attribute_permissions(ordered_list_data->common.is_readonly,
319					&ordered_list_current_val);
320	hp_friendly_user_name_update(ordered_list_data->common.path,
321				     attr_name_kobj->name,
322				     ordered_list_data->common.display_name,
323				     sizeof(ordered_list_data->common.display_name));
324	return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group);
325}
326
327static int hp_populate_ordered_list_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size,
328							 int instance_id)
329{
330	int values;
331	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
332	int ret = 0;
333
334	/*
335	 * Only data relevant to this driver and its functionality is
336	 * read. BIOS defines the order in which each * element is
337	 * read. Element 0 data is not relevant to this
338	 * driver hence it is ignored. For clarity, all element names
339	 * (DISPLAY_IN_UI) which defines the order in which is read
340	 * and the name matches the variable where the data is stored.
341	 *
342	 * In earlier implementation, reported errors were ignored
343	 * causing the data to remain uninitialized. It is not
344	 * possible to determine if data read from BIOS is valid or
345	 * not. It is for this reason functions may return a error
346	 * without validating the data itself.
347	 */
348
349	// VALUE:
350	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, ordered_list_data->current_value,
351					sizeof(ordered_list_data->current_value));
352	if (ret < 0)
353		goto buffer_exit;
354
355	replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP);
356
357	// COMMON:
358	ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size,
359					     &ordered_list_data->common);
360	if (ret < 0)
361		goto buffer_exit;
362
363	// ORD_LIST_SIZE:
364	ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size,
365					 &ordered_list_data->elements_size);
366
367	if (ordered_list_data->elements_size > MAX_ELEMENTS_SIZE) {
368		/* Report a message and limit elements size to maximum value */
369		pr_warn("Ordered List size value exceeded the maximum number of elements supported or data may be malformed\n");
370		ordered_list_data->elements_size = MAX_ELEMENTS_SIZE;
371	}
372
373	// ORD_LIST_ELEMENTS:
374	for (values = 0; values < ordered_list_data->elements_size; values++) {
375		ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
376						ordered_list_data->elements[values],
377						sizeof(ordered_list_data->elements[values]));
378		if (ret < 0)
379			break;
380	}
381
382buffer_exit:
383	return ret;
384}
385
386/**
387 * hp_populate_ordered_list_buffer_data() - Populate all properties of an
388 * instance under ordered list attribute
389 *
390 * @buffer_ptr: Buffer pointer
391 * @buffer_size: Buffer size
392 * @instance_id: The instance to enumerate
393 * @attr_name_kobj: The parent kernel object
394 */
395int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id,
396					 struct kobject *attr_name_kobj)
397{
398	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
399	int ret = 0;
400
401	ordered_list_data->attr_name_kobj = attr_name_kobj;
402
403	/* Populate ordered list elements */
404	ret = hp_populate_ordered_list_elements_from_buffer(buffer_ptr, buffer_size,
405							    instance_id);
406	if (ret < 0)
407		return ret;
408
409	hp_update_attribute_permissions(ordered_list_data->common.is_readonly,
410					&ordered_list_current_val);
411	hp_friendly_user_name_update(ordered_list_data->common.path,
412				     attr_name_kobj->name,
413				     ordered_list_data->common.display_name,
414				     sizeof(ordered_list_data->common.display_name));
415
416	return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group);
417}
418
419/**
420 * hp_exit_ordered_list_attributes() - Clear all attribute data
421 *
422 * Clears all data allocated for this group of attributes
423 */
424void hp_exit_ordered_list_attributes(void)
425{
426	int instance_id;
427
428	for (instance_id = 0; instance_id < bioscfg_drv.ordered_list_instances_count;
429	     instance_id++) {
430		struct kobject *attr_name_kobj =
431			bioscfg_drv.ordered_list_data[instance_id].attr_name_kobj;
432
433		if (attr_name_kobj)
434			sysfs_remove_group(attr_name_kobj,
435					   &ordered_list_attr_group);
436	}
437	bioscfg_drv.ordered_list_instances_count = 0;
438
439	kfree(bioscfg_drv.ordered_list_data);
440	bioscfg_drv.ordered_list_data = NULL;
441}
442