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