xref: /linux/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Functions corresponding to enumeration type attributes under
4  * BIOS Enumeration GUID for use with hp-bioscfg driver.
5  *
6  * Copyright (c) 2022 HP Development Company, L.P.
7  */
8 
9 #include "bioscfg.h"
10 
11 GET_INSTANCE_ID(enumeration);
12 
current_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)13 static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
14 {
15 	int instance_id = get_enumeration_instance_id(kobj);
16 
17 	if (instance_id < 0)
18 		return -EIO;
19 
20 	return sysfs_emit(buf, "%s\n",
21 			 bioscfg_drv.enumeration_data[instance_id].current_value);
22 }
23 
24 /**
25  * validate_enumeration_input() -
26  * Validate input of current_value against possible values
27  *
28  * @instance_id: The instance on which input is validated
29  * @buf: Input value
30  */
validate_enumeration_input(int instance_id,const char * buf)31 static int validate_enumeration_input(int instance_id, const char *buf)
32 {
33 	int i;
34 	int found = 0;
35 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
36 
37 	/* Is it a read only attribute */
38 	if (enum_data->common.is_readonly)
39 		return -EIO;
40 
41 	for (i = 0; i < enum_data->possible_values_size && !found; i++)
42 		if (!strcmp(enum_data->possible_values[i], buf))
43 			found = 1;
44 
45 	if (!found)
46 		return -EINVAL;
47 
48 	return 0;
49 }
50 
update_enumeration_value(int instance_id,char * attr_value)51 static void update_enumeration_value(int instance_id, char *attr_value)
52 {
53 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
54 
55 	strscpy(enum_data->current_value, attr_value);
56 }
57 
58 ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration);
59 static struct kobj_attribute enumeration_display_name =
60 		__ATTR_RO(display_name);
61 
62 ATTRIBUTE_PROPERTY_STORE(current_value, enumeration);
63 static struct kobj_attribute enumeration_current_val =
64 		__ATTR_RW(current_value);
65 
66 ATTRIBUTE_VALUES_PROPERTY_SHOW(possible_values, enumeration, SEMICOLON_SEP);
67 static struct kobj_attribute enumeration_poss_val =
68 		__ATTR_RO(possible_values);
69 
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)70 static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
71 			 char *buf)
72 {
73 	return sysfs_emit(buf, "enumeration\n");
74 }
75 
76 static struct kobj_attribute enumeration_type =
77 		__ATTR_RO(type);
78 
79 static struct attribute *enumeration_attrs[] = {
80 	&common_display_langcode.attr,
81 	&enumeration_display_name.attr,
82 	&enumeration_current_val.attr,
83 	&enumeration_poss_val.attr,
84 	&enumeration_type.attr,
85 	NULL
86 };
87 
88 static const struct attribute_group enumeration_attr_group = {
89 	.attrs = enumeration_attrs,
90 };
91 
hp_alloc_enumeration_data(void)92 int hp_alloc_enumeration_data(void)
93 {
94 	bioscfg_drv.enumeration_instances_count =
95 		hp_get_instance_count(HP_WMI_BIOS_ENUMERATION_GUID);
96 
97 	bioscfg_drv.enumeration_data = kcalloc(bioscfg_drv.enumeration_instances_count,
98 					       sizeof(*bioscfg_drv.enumeration_data), GFP_KERNEL);
99 	if (!bioscfg_drv.enumeration_data) {
100 		bioscfg_drv.enumeration_instances_count = 0;
101 		return -ENOMEM;
102 	}
103 	return 0;
104 }
105 
106 /* Expected Values types associated with each element */
107 static const acpi_object_type expected_enum_types[] = {
108 	[NAME] = ACPI_TYPE_STRING,
109 	[VALUE] = ACPI_TYPE_STRING,
110 	[PATH] = ACPI_TYPE_STRING,
111 	[IS_READONLY] = ACPI_TYPE_INTEGER,
112 	[DISPLAY_IN_UI] = ACPI_TYPE_INTEGER,
113 	[REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER,
114 	[SEQUENCE] = ACPI_TYPE_INTEGER,
115 	[PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER,
116 	[PREREQUISITES] = ACPI_TYPE_STRING,
117 	[SECURITY_LEVEL] = ACPI_TYPE_INTEGER,
118 	[ENUM_CURRENT_VALUE] = ACPI_TYPE_STRING,
119 	[ENUM_SIZE] = ACPI_TYPE_INTEGER,
120 	[ENUM_POSSIBLE_VALUES] = ACPI_TYPE_STRING,
121 };
122 
hp_populate_enumeration_elements_from_package(union acpi_object * enum_obj,int enum_obj_count,int instance_id)123 static int hp_populate_enumeration_elements_from_package(union acpi_object *enum_obj,
124 							 int enum_obj_count,
125 							 int instance_id)
126 {
127 	char *str_value = NULL;
128 	int value_len;
129 	u32 size = 0;
130 	u32 int_value = 0;
131 	int elem = 0;
132 	int reqs;
133 	int pos_values;
134 	int ret;
135 	int eloc;
136 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
137 
138 	for (elem = 1, eloc = 1; elem < enum_obj_count; elem++, eloc++) {
139 		/* ONLY look at the first ENUM_ELEM_CNT elements */
140 		if (eloc == ENUM_ELEM_CNT)
141 			goto exit_enumeration_package;
142 
143 		switch (enum_obj[elem].type) {
144 		case ACPI_TYPE_STRING:
145 			if (PREREQUISITES != elem && ENUM_POSSIBLE_VALUES != elem) {
146 				ret = hp_convert_hexstr_to_str(enum_obj[elem].string.pointer,
147 							       enum_obj[elem].string.length,
148 							       &str_value, &value_len);
149 				if (ret)
150 					return -EINVAL;
151 			}
152 			break;
153 		case ACPI_TYPE_INTEGER:
154 			int_value = (u32)enum_obj[elem].integer.value;
155 			break;
156 		default:
157 			pr_warn("Unsupported object type [%d]\n", enum_obj[elem].type);
158 			continue;
159 		}
160 
161 		/* Check that both expected and read object type match */
162 		if (expected_enum_types[eloc] != enum_obj[elem].type) {
163 			pr_err("Error expected type %d for elem %d, but got type %d instead\n",
164 			       expected_enum_types[eloc], elem, enum_obj[elem].type);
165 			kfree(str_value);
166 			return -EIO;
167 		}
168 
169 		/* Assign appropriate element value to corresponding field */
170 		switch (eloc) {
171 		case NAME:
172 		case VALUE:
173 			break;
174 		case PATH:
175 			strscpy(enum_data->common.path, str_value);
176 			break;
177 		case IS_READONLY:
178 			enum_data->common.is_readonly = int_value;
179 			break;
180 		case DISPLAY_IN_UI:
181 			enum_data->common.display_in_ui = int_value;
182 			break;
183 		case REQUIRES_PHYSICAL_PRESENCE:
184 			enum_data->common.requires_physical_presence = int_value;
185 			break;
186 		case SEQUENCE:
187 			enum_data->common.sequence = int_value;
188 			break;
189 		case PREREQUISITES_SIZE:
190 			if (int_value > MAX_PREREQUISITES_SIZE) {
191 				pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
192 				int_value = MAX_PREREQUISITES_SIZE;
193 			}
194 			enum_data->common.prerequisites_size = int_value;
195 
196 			/*
197 			 * This step is needed to keep the expected
198 			 * element list pointing to the right obj[elem].type
199 			 * when the size is zero. PREREQUISITES
200 			 * object is omitted by BIOS when the size is
201 			 * zero.
202 			 */
203 			if (int_value == 0)
204 				eloc++;
205 			break;
206 
207 		case PREREQUISITES:
208 			size = min_t(u32, enum_data->common.prerequisites_size, MAX_PREREQUISITES_SIZE);
209 			for (reqs = 0; reqs < size; reqs++) {
210 				if (elem >= enum_obj_count) {
211 					pr_err("Error enum-objects package is too small\n");
212 					return -EINVAL;
213 				}
214 
215 				ret = hp_convert_hexstr_to_str(enum_obj[elem + reqs].string.pointer,
216 							       enum_obj[elem + reqs].string.length,
217 							       &str_value, &value_len);
218 
219 				if (ret)
220 					return -EINVAL;
221 
222 				strscpy(enum_data->common.prerequisites[reqs], str_value);
223 
224 				kfree(str_value);
225 				str_value = NULL;
226 			}
227 			break;
228 
229 		case SECURITY_LEVEL:
230 			enum_data->common.security_level = int_value;
231 			break;
232 
233 		case ENUM_CURRENT_VALUE:
234 			strscpy(enum_data->current_value, str_value);
235 			break;
236 		case ENUM_SIZE:
237 			if (int_value > MAX_VALUES_SIZE) {
238 				pr_warn("Possible number values size value exceeded the maximum number of elements supported or data may be malformed\n");
239 				int_value = MAX_VALUES_SIZE;
240 			}
241 			enum_data->possible_values_size = int_value;
242 
243 			/*
244 			 * This step is needed to keep the expected
245 			 * element list pointing to the right obj[elem].type
246 			 * when the size is zero. POSSIBLE_VALUES
247 			 * object is omitted by BIOS when the size is zero.
248 			 */
249 			if (int_value == 0)
250 				eloc++;
251 			break;
252 
253 		case ENUM_POSSIBLE_VALUES:
254 			size = enum_data->possible_values_size;
255 
256 			for (pos_values = 0; pos_values < size && pos_values < MAX_VALUES_SIZE;
257 			     pos_values++) {
258 				if (elem >= enum_obj_count) {
259 					pr_err("Error enum-objects package is too small\n");
260 					return -EINVAL;
261 				}
262 
263 				ret = hp_convert_hexstr_to_str(enum_obj[elem + pos_values].string.pointer,
264 							       enum_obj[elem + pos_values].string.length,
265 							       &str_value, &value_len);
266 
267 				if (ret)
268 					return -EINVAL;
269 
270 				/*
271 				 * ignore strings when possible values size
272 				 * is greater than MAX_VALUES_SIZE
273 				 */
274 				if (size < MAX_VALUES_SIZE)
275 					strscpy(enum_data->possible_values[pos_values], str_value);
276 
277 				kfree(str_value);
278 				str_value = NULL;
279 			}
280 			break;
281 		default:
282 			pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem);
283 			break;
284 		}
285 
286 		kfree(str_value);
287 		str_value = NULL;
288 	}
289 
290 exit_enumeration_package:
291 	kfree(str_value);
292 	return 0;
293 }
294 
295 /**
296  * hp_populate_enumeration_package_data() -
297  * Populate all properties of an instance under enumeration attribute
298  *
299  * @enum_obj: ACPI object with enumeration data
300  * @instance_id: The instance to enumerate
301  * @attr_name_kobj: The parent kernel object
302  */
hp_populate_enumeration_package_data(union acpi_object * enum_obj,int instance_id,struct kobject * attr_name_kobj)303 int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
304 					 int instance_id,
305 					 struct kobject *attr_name_kobj)
306 {
307 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
308 
309 	enum_data->attr_name_kobj = attr_name_kobj;
310 
311 	hp_populate_enumeration_elements_from_package(enum_obj,
312 						      enum_obj->package.count,
313 						      instance_id);
314 	hp_update_attribute_permissions(enum_data->common.is_readonly,
315 					&enumeration_current_val);
316 	/*
317 	 * Several attributes have names such "MONDAY". Friendly
318 	 * user nane is generated to make the name more descriptive
319 	 */
320 	hp_friendly_user_name_update(enum_data->common.path,
321 				     attr_name_kobj->name,
322 				     enum_data->common.display_name,
323 				     sizeof(enum_data->common.display_name));
324 	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
325 }
326 
hp_populate_enumeration_elements_from_buffer(u8 * buffer_ptr,u32 * buffer_size,int instance_id)327 static int hp_populate_enumeration_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size,
328 							int instance_id)
329 {
330 	int values;
331 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_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, enum_data->current_value,
351 					sizeof(enum_data->current_value));
352 	if (ret < 0)
353 		goto buffer_exit;
354 
355 	// COMMON:
356 	ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &enum_data->common);
357 	if (ret < 0)
358 		goto buffer_exit;
359 
360 	// ENUM_CURRENT_VALUE:
361 	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
362 					enum_data->current_value,
363 					sizeof(enum_data->current_value));
364 	if (ret < 0)
365 		goto buffer_exit;
366 
367 	// ENUM_SIZE:
368 	ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size,
369 					 &enum_data->possible_values_size);
370 
371 	if (enum_data->possible_values_size > MAX_VALUES_SIZE) {
372 		/* Report a message and limit possible values size to maximum value */
373 		pr_warn("Enum Possible size value exceeded the maximum number of elements supported or data may be malformed\n");
374 		enum_data->possible_values_size = MAX_VALUES_SIZE;
375 	}
376 
377 	// ENUM_POSSIBLE_VALUES:
378 	for (values = 0; values < enum_data->possible_values_size; values++) {
379 		ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
380 						enum_data->possible_values[values],
381 						sizeof(enum_data->possible_values[values]));
382 		if (ret < 0)
383 			break;
384 	}
385 
386 buffer_exit:
387 	return ret;
388 }
389 
390 /**
391  * hp_populate_enumeration_buffer_data() -
392  * Populate all properties of an instance under enumeration attribute
393  *
394  * @buffer_ptr: Buffer pointer
395  * @buffer_size: Buffer size
396  * @instance_id: The instance to enumerate
397  * @attr_name_kobj: The parent kernel object
398  */
hp_populate_enumeration_buffer_data(u8 * buffer_ptr,u32 * buffer_size,int instance_id,struct kobject * attr_name_kobj)399 int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
400 					int instance_id,
401 					struct kobject *attr_name_kobj)
402 {
403 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
404 	int ret = 0;
405 
406 	enum_data->attr_name_kobj = attr_name_kobj;
407 
408 	/* Populate enumeration elements */
409 	ret = hp_populate_enumeration_elements_from_buffer(buffer_ptr, buffer_size,
410 							   instance_id);
411 	if (ret < 0)
412 		return ret;
413 
414 	hp_update_attribute_permissions(enum_data->common.is_readonly,
415 					&enumeration_current_val);
416 	/*
417 	 * Several attributes have names such "MONDAY". A Friendlier
418 	 * user nane is generated to make the name more descriptive
419 	 */
420 	hp_friendly_user_name_update(enum_data->common.path,
421 				     attr_name_kobj->name,
422 				     enum_data->common.display_name,
423 				     sizeof(enum_data->common.display_name));
424 
425 	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
426 }
427 
428 /**
429  * hp_exit_enumeration_attributes() - Clear all attribute data
430  *
431  * Clears all data allocated for this group of attributes
432  */
hp_exit_enumeration_attributes(void)433 void hp_exit_enumeration_attributes(void)
434 {
435 	int instance_id;
436 
437 	for (instance_id = 0; instance_id < bioscfg_drv.enumeration_instances_count;
438 	     instance_id++) {
439 		struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
440 		struct kobject *attr_name_kobj = enum_data->attr_name_kobj;
441 
442 		if (attr_name_kobj)
443 			sysfs_remove_group(attr_name_kobj, &enumeration_attr_group);
444 	}
445 	bioscfg_drv.enumeration_instances_count = 0;
446 
447 	kfree(bioscfg_drv.enumeration_data);
448 	bioscfg_drv.enumeration_data = NULL;
449 }
450