434 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Functions corresponding to ordered list type attributes under4 * 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 values43 *44 * @instance: The instance on which input is validated45 * @buf: Input value46 */47static int validate_ordered_list_input(int instance, char *buf)48{49 /* validation is done by BIOS. This validation function will50 * convert semicolon to commas. BIOS uses commas as51 * 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, attr_value);61}62 63ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, ordered_list);64static struct kobj_attribute ordered_list_display_name =65 __ATTR_RO(display_name);66 67ATTRIBUTE_PROPERTY_STORE(current_value, ordered_list);68static struct kobj_attribute ordered_list_current_val =69 __ATTR_RW_MODE(current_value, 0644);70 71ATTRIBUTE_VALUES_PROPERTY_SHOW(elements, ordered_list, SEMICOLON_SEP);72static struct kobj_attribute ordered_list_elements_val =73 __ATTR_RO(elements);74 75static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,76 char *buf)77{78 return sysfs_emit(buf, "ordered-list\n");79}80 81static struct kobj_attribute ordered_list_type =82 __ATTR_RO(type);83 84static struct attribute *ordered_list_attrs[] = {85 &common_display_langcode.attr,86 &ordered_list_display_name.attr,87 &ordered_list_current_val.attr,88 &ordered_list_elements_val.attr,89 &ordered_list_type.attr,90 NULL91};92 93static const struct attribute_group ordered_list_attr_group = {94 .attrs = ordered_list_attrs,95};96 97int hp_alloc_ordered_list_data(void)98{99 bioscfg_drv.ordered_list_instances_count =100 hp_get_instance_count(HP_WMI_BIOS_ORDERED_LIST_GUID);101 bioscfg_drv.ordered_list_data = kcalloc(bioscfg_drv.ordered_list_instances_count,102 sizeof(*bioscfg_drv.ordered_list_data),103 GFP_KERNEL);104 if (!bioscfg_drv.ordered_list_data) {105 bioscfg_drv.ordered_list_instances_count = 0;106 return -ENOMEM;107 }108 return 0;109}110 111/* Expected Values types associated with each element */112static const acpi_object_type expected_order_types[] = {113 [NAME] = ACPI_TYPE_STRING,114 [VALUE] = ACPI_TYPE_STRING,115 [PATH] = ACPI_TYPE_STRING,116 [IS_READONLY] = ACPI_TYPE_INTEGER,117 [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER,118 [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER,119 [SEQUENCE] = ACPI_TYPE_INTEGER,120 [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER,121 [PREREQUISITES] = ACPI_TYPE_STRING,122 [SECURITY_LEVEL] = ACPI_TYPE_INTEGER,123 [ORD_LIST_SIZE] = ACPI_TYPE_INTEGER,124 [ORD_LIST_ELEMENTS] = ACPI_TYPE_STRING,125};126 127static int hp_populate_ordered_list_elements_from_package(union acpi_object *order_obj,128 int order_obj_count,129 int instance_id)130{131 char *str_value = NULL;132 int value_len = 0;133 int ret;134 u32 size;135 u32 int_value = 0;136 int elem;137 int olist_elem;138 int reqs;139 int eloc;140 char *tmpstr = NULL;141 char *part_tmp = NULL;142 int tmp_len = 0;143 char *part = NULL;144 struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];145 146 if (!order_obj)147 return -EINVAL;148 149 for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) {150 151 switch (order_obj[elem].type) {152 case ACPI_TYPE_STRING:153 if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) {154 ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer,155 order_obj[elem].string.length,156 &str_value, &value_len);157 if (ret)158 continue;159 }160 break;161 case ACPI_TYPE_INTEGER:162 int_value = (u32)order_obj[elem].integer.value;163 break;164 default:165 pr_warn("Unsupported object type [%d]\n", order_obj[elem].type);166 continue;167 }168 169 /* Check that both expected and read object type match */170 if (expected_order_types[eloc] != order_obj[elem].type) {171 pr_err("Error expected type %d for elem %d, but got type %d instead\n",172 expected_order_types[eloc], elem, order_obj[elem].type);173 kfree(str_value);174 return -EIO;175 }176 177 /* Assign appropriate element value to corresponding field*/178 switch (eloc) {179 case VALUE:180 strscpy(ordered_list_data->current_value, str_value);181 replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP);182 break;183 case PATH:184 strscpy(ordered_list_data->common.path, str_value);185 break;186 case IS_READONLY:187 ordered_list_data->common.is_readonly = int_value;188 break;189 case DISPLAY_IN_UI:190 ordered_list_data->common.display_in_ui = int_value;191 break;192 case REQUIRES_PHYSICAL_PRESENCE:193 ordered_list_data->common.requires_physical_presence = int_value;194 break;195 case SEQUENCE:196 ordered_list_data->common.sequence = int_value;197 break;198 case PREREQUISITES_SIZE:199 if (int_value > MAX_PREREQUISITES_SIZE) {200 pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");201 int_value = MAX_PREREQUISITES_SIZE;202 }203 ordered_list_data->common.prerequisites_size = int_value;204 205 /*206 * This step is needed to keep the expected207 * element list pointing to the right obj[elem].type208 * when the size is zero. PREREQUISITES209 * object is omitted by BIOS when the size is210 * zero.211 */212 if (int_value == 0)213 eloc++;214 break;215 case PREREQUISITES:216 size = min_t(u32, ordered_list_data->common.prerequisites_size,217 MAX_PREREQUISITES_SIZE);218 for (reqs = 0; reqs < size; reqs++) {219 ret = hp_convert_hexstr_to_str(order_obj[elem + reqs].string.pointer,220 order_obj[elem + reqs].string.length,221 &str_value, &value_len);222 223 if (ret)224 continue;225 226 strscpy(ordered_list_data->common.prerequisites[reqs], str_value);227 228 kfree(str_value);229 str_value = NULL;230 }231 break;232 233 case SECURITY_LEVEL:234 ordered_list_data->common.security_level = int_value;235 break;236 237 case ORD_LIST_SIZE:238 if (int_value > MAX_ELEMENTS_SIZE) {239 pr_warn("Order List size value exceeded the maximum number of elements supported or data may be malformed\n");240 int_value = MAX_ELEMENTS_SIZE;241 }242 ordered_list_data->elements_size = int_value;243 244 /*245 * This step is needed to keep the expected246 * element list pointing to the right obj[elem].type247 * when the size is zero. ORD_LIST_ELEMENTS248 * object is omitted by BIOS when the size is249 * zero.250 */251 if (int_value == 0)252 eloc++;253 break;254 case ORD_LIST_ELEMENTS:255 256 /*257 * Ordered list data is stored in hex and comma separated format258 * Convert the data and split it to show each element259 */260 ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);261 if (ret)262 goto exit_list;263 264 part_tmp = tmpstr;265 part = strsep(&part_tmp, COMMA_SEP);266 267 for (olist_elem = 0; olist_elem < MAX_ELEMENTS_SIZE && part; olist_elem++) {268 strscpy(ordered_list_data->elements[olist_elem], part);269 part = strsep(&part_tmp, COMMA_SEP);270 }271 ordered_list_data->elements_size = olist_elem;272 273 kfree(str_value);274 str_value = NULL;275 break;276 default:277 pr_warn("Invalid element: %d found in Ordered_List attribute or data may be malformed\n", elem);278 break;279 }280 kfree(tmpstr);281 tmpstr = NULL;282 kfree(str_value);283 str_value = NULL;284 }285 286exit_list:287 kfree(tmpstr);288 kfree(str_value);289 return 0;290}291 292/**293 * hp_populate_ordered_list_package_data() -294 * Populate all properties of an instance under ordered_list attribute295 *296 * @order_obj: ACPI object with ordered_list data297 * @instance_id: The instance to enumerate298 * @attr_name_kobj: The parent kernel object299 */300int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id,301 struct kobject *attr_name_kobj)302{303 struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];304 305 ordered_list_data->attr_name_kobj = attr_name_kobj;306 307 hp_populate_ordered_list_elements_from_package(order_obj,308 order_obj->package.count,309 instance_id);310 hp_update_attribute_permissions(ordered_list_data->common.is_readonly,311 &ordered_list_current_val);312 hp_friendly_user_name_update(ordered_list_data->common.path,313 attr_name_kobj->name,314 ordered_list_data->common.display_name,315 sizeof(ordered_list_data->common.display_name));316 return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group);317}318 319static int hp_populate_ordered_list_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size,320 int instance_id)321{322 int values;323 struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];324 int ret = 0;325 326 /*327 * Only data relevant to this driver and its functionality is328 * read. BIOS defines the order in which each * element is329 * read. Element 0 data is not relevant to this330 * driver hence it is ignored. For clarity, all element names331 * (DISPLAY_IN_UI) which defines the order in which is read332 * and the name matches the variable where the data is stored.333 *334 * In earlier implementation, reported errors were ignored335 * causing the data to remain uninitialized. It is not336 * possible to determine if data read from BIOS is valid or337 * not. It is for this reason functions may return a error338 * without validating the data itself.339 */340 341 // VALUE:342 ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, ordered_list_data->current_value,343 sizeof(ordered_list_data->current_value));344 if (ret < 0)345 goto buffer_exit;346 347 replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP);348 349 // COMMON:350 ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size,351 &ordered_list_data->common);352 if (ret < 0)353 goto buffer_exit;354 355 // ORD_LIST_SIZE:356 ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size,357 &ordered_list_data->elements_size);358 359 if (ordered_list_data->elements_size > MAX_ELEMENTS_SIZE) {360 /* Report a message and limit elements size to maximum value */361 pr_warn("Ordered List size value exceeded the maximum number of elements supported or data may be malformed\n");362 ordered_list_data->elements_size = MAX_ELEMENTS_SIZE;363 }364 365 // ORD_LIST_ELEMENTS:366 for (values = 0; values < ordered_list_data->elements_size; values++) {367 ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,368 ordered_list_data->elements[values],369 sizeof(ordered_list_data->elements[values]));370 if (ret < 0)371 break;372 }373 374buffer_exit:375 return ret;376}377 378/**379 * hp_populate_ordered_list_buffer_data() - Populate all properties of an380 * instance under ordered list attribute381 *382 * @buffer_ptr: Buffer pointer383 * @buffer_size: Buffer size384 * @instance_id: The instance to enumerate385 * @attr_name_kobj: The parent kernel object386 */387int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id,388 struct kobject *attr_name_kobj)389{390 struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];391 int ret = 0;392 393 ordered_list_data->attr_name_kobj = attr_name_kobj;394 395 /* Populate ordered list elements */396 ret = hp_populate_ordered_list_elements_from_buffer(buffer_ptr, buffer_size,397 instance_id);398 if (ret < 0)399 return ret;400 401 hp_update_attribute_permissions(ordered_list_data->common.is_readonly,402 &ordered_list_current_val);403 hp_friendly_user_name_update(ordered_list_data->common.path,404 attr_name_kobj->name,405 ordered_list_data->common.display_name,406 sizeof(ordered_list_data->common.display_name));407 408 return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group);409}410 411/**412 * hp_exit_ordered_list_attributes() - Clear all attribute data413 *414 * Clears all data allocated for this group of attributes415 */416void hp_exit_ordered_list_attributes(void)417{418 int instance_id;419 420 for (instance_id = 0; instance_id < bioscfg_drv.ordered_list_instances_count;421 instance_id++) {422 struct kobject *attr_name_kobj =423 bioscfg_drv.ordered_list_data[instance_id].attr_name_kobj;424 425 if (attr_name_kobj)426 sysfs_remove_group(attr_name_kobj,427 &ordered_list_attr_group);428 }429 bioscfg_drv.ordered_list_instances_count = 0;430 431 kfree(bioscfg_drv.ordered_list_data);432 bioscfg_drv.ordered_list_data = NULL;433}434