1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 #include "array.h" 3 4 #include <libpldm/base.h> 5 #include <libpldm/bios.h> 6 #include <libpldm/bios_table.h> 7 #include <libpldm/utils.h> 8 9 #include <assert.h> 10 #include <endian.h> 11 #include <limits.h> 12 #include <stdbool.h> 13 #include <stdint.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #define POINTER_CHECK(pointer) \ 18 do { \ 19 if ((pointer) == NULL) \ 20 return PLDM_ERROR_INVALID_DATA; \ 21 } while (0) 22 23 #define ATTR_TYPE_EXPECT(type, expected) \ 24 do { \ 25 if ((type) != (expected) && (type) != ((expected) | 0x80)) \ 26 return PLDM_ERROR_INVALID_DATA; \ 27 } while (0) 28 29 #define BUFFER_SIZE_EXPECT(current_size, expected_size) \ 30 do { \ 31 if ((current_size) < (expected_size)) \ 32 return PLDM_ERROR_INVALID_LENGTH; \ 33 } while (0) 34 35 #define MEMBER_SIZE(type, member) sizeof(((struct type *)0)->member) 36 37 static void set_errmsg(const char **errmsg, const char *msg) 38 { 39 if (errmsg != NULL) { 40 *errmsg = msg; 41 } 42 } 43 44 static int get_bios_string_handle(uint16_t *val) 45 { 46 static uint16_t handle = 0; 47 assert(handle != UINT16_MAX); 48 if (handle == UINT16_MAX) { 49 return PLDM_ERROR_INVALID_DATA; 50 } 51 52 *val = handle++; 53 return PLDM_SUCCESS; 54 } 55 56 LIBPLDM_ABI_STABLE 57 size_t pldm_bios_table_string_entry_encode_length(uint16_t string_length) 58 { 59 return sizeof(struct pldm_bios_string_table_entry) - 60 MEMBER_SIZE(pldm_bios_string_table_entry, name) + string_length; 61 } 62 63 LIBPLDM_ABI_STABLE 64 int pldm_bios_table_string_entry_encode(void *entry, size_t entry_length, 65 const char *str, uint16_t str_length) 66 { 67 if (str_length == 0) { 68 return PLDM_ERROR_INVALID_DATA; 69 } 70 POINTER_CHECK(entry); 71 POINTER_CHECK(str); 72 size_t length = pldm_bios_table_string_entry_encode_length(str_length); 73 BUFFER_SIZE_EXPECT(entry_length, length); 74 struct pldm_bios_string_table_entry *string_entry = entry; 75 uint16_t handle; 76 int rc = get_bios_string_handle(&handle); 77 if (rc != PLDM_SUCCESS) { 78 return rc; 79 } 80 string_entry->string_handle = htole16(handle); 81 string_entry->string_length = htole16(str_length); 82 memcpy(string_entry->name, str, str_length); 83 return PLDM_SUCCESS; 84 } 85 86 LIBPLDM_ABI_STABLE 87 uint16_t pldm_bios_table_string_entry_decode_handle( 88 const struct pldm_bios_string_table_entry *entry) 89 { 90 return le16toh(entry->string_handle); 91 } 92 93 LIBPLDM_ABI_STABLE 94 uint16_t pldm_bios_table_string_entry_decode_string_length( 95 const struct pldm_bios_string_table_entry *entry) 96 { 97 return le16toh(entry->string_length); 98 } 99 100 LIBPLDM_ABI_STABLE 101 int pldm_bios_table_string_entry_decode_string( 102 const struct pldm_bios_string_table_entry *entry, char *buffer, 103 size_t size) 104 { 105 POINTER_CHECK(entry); 106 POINTER_CHECK(buffer); 107 if (size == 0) { 108 return PLDM_ERROR_INVALID_LENGTH; 109 } 110 size_t length = 111 pldm_bios_table_string_entry_decode_string_length(entry); 112 length = length < (size - 1) ? length : (size - 1); 113 memcpy(buffer, entry->name, length); 114 buffer[length] = 0; 115 return PLDM_SUCCESS; 116 } 117 118 static ssize_t string_table_entry_length(const void *table_entry) 119 { 120 const struct pldm_bios_string_table_entry *entry = table_entry; 121 size_t len = sizeof(*entry) - sizeof(entry->name) + 122 pldm_bios_table_string_entry_decode_string_length(entry); 123 if (len > SSIZE_MAX) { 124 return -1; 125 } 126 return (ssize_t)len; 127 } 128 129 static int get_bios_attr_handle(uint16_t *val) 130 { 131 static uint16_t handle = 0; 132 assert(handle != UINT16_MAX); 133 if (handle == UINT16_MAX) { 134 return PLDM_ERROR_INVALID_DATA; 135 } 136 137 *val = handle++; 138 return PLDM_SUCCESS; 139 } 140 141 static int attr_table_entry_encode_header(void *entry, size_t length, 142 uint8_t attr_type, 143 uint16_t string_handle) 144 { 145 struct pldm_bios_attr_table_entry *attr_entry = entry; 146 147 assert(sizeof(*attr_entry) <= length); 148 if (sizeof(*attr_entry) > length) { 149 return PLDM_ERROR_INVALID_LENGTH; 150 } 151 152 uint16_t handle; 153 int rc = get_bios_attr_handle(&handle); 154 if (rc != PLDM_SUCCESS) { 155 return rc; 156 } 157 158 attr_entry->attr_handle = htole16(handle); 159 attr_entry->attr_type = attr_type; 160 attr_entry->string_handle = htole16(string_handle); 161 162 return PLDM_SUCCESS; 163 } 164 165 LIBPLDM_ABI_STABLE 166 uint16_t pldm_bios_table_attr_entry_decode_attribute_handle( 167 const struct pldm_bios_attr_table_entry *entry) 168 { 169 return le16toh(entry->attr_handle); 170 } 171 172 LIBPLDM_ABI_STABLE 173 uint8_t pldm_bios_table_attr_entry_decode_attribute_type( 174 const struct pldm_bios_attr_table_entry *entry) 175 { 176 return entry->attr_type; 177 } 178 179 LIBPLDM_ABI_STABLE 180 uint16_t pldm_bios_table_attr_entry_decode_string_handle( 181 const struct pldm_bios_attr_table_entry *entry) 182 { 183 return le16toh(entry->string_handle); 184 } 185 186 LIBPLDM_ABI_STABLE 187 size_t pldm_bios_table_attr_entry_enum_encode_length(uint8_t pv_num, 188 uint8_t def_num) 189 { 190 return sizeof(struct pldm_bios_attr_table_entry) - 191 MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) + 192 sizeof(pv_num) + pv_num * sizeof(uint16_t) + sizeof(def_num) + 193 def_num; 194 } 195 196 LIBPLDM_ABI_STABLE 197 int pldm_bios_table_attr_entry_enum_encode( 198 void *entry, size_t entry_length, 199 const struct pldm_bios_table_attr_entry_enum_info *info) 200 { 201 POINTER_CHECK(entry); 202 POINTER_CHECK(info); 203 size_t length = pldm_bios_table_attr_entry_enum_encode_length( 204 info->pv_num, info->def_num); 205 BUFFER_SIZE_EXPECT(entry_length, length); 206 uint8_t attr_type = info->read_only ? PLDM_BIOS_ENUMERATION_READ_ONLY : 207 PLDM_BIOS_ENUMERATION; 208 int rc = attr_table_entry_encode_header(entry, entry_length, attr_type, 209 info->name_handle); 210 if (rc != PLDM_SUCCESS) { 211 return rc; 212 } 213 struct pldm_bios_attr_table_entry *attr_entry = entry; 214 attr_entry->metadata[0] = info->pv_num; 215 uint16_t *pv_hdls = 216 (uint16_t *)(attr_entry->metadata + 1 /* sizeof(pv num) */); 217 size_t i; 218 for (i = 0; i < info->pv_num; i++) { 219 pv_hdls[i] = htole16(info->pv_handle[i]); 220 } 221 attr_entry->metadata[1 + info->pv_num * sizeof(uint16_t)] = 222 info->def_num; 223 memcpy(attr_entry->metadata + 1 /* sizeof(pv num) */ + 224 info->pv_num * sizeof(uint16_t) + 1 /* sizeof(def num)*/, 225 info->def_index, info->def_num); 226 return PLDM_SUCCESS; 227 } 228 229 #define ATTR_TYPE_EXPECT(type, expected) \ 230 do { \ 231 if ((type) != (expected) && (type) != ((expected) | 0x80)) \ 232 return PLDM_ERROR_INVALID_DATA; \ 233 } while (0) 234 235 LIBPLDM_ABI_STABLE 236 int pldm_bios_table_attr_entry_enum_decode_pv_num( 237 const struct pldm_bios_attr_table_entry *entry, uint8_t *pv_num) 238 { 239 POINTER_CHECK(entry); 240 POINTER_CHECK(pv_num); 241 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION); 242 *pv_num = entry->metadata[0]; 243 return PLDM_SUCCESS; 244 } 245 246 static uint8_t pldm_bios_table_attr_entry_enum_decode_def_num( 247 const struct pldm_bios_attr_table_entry *entry) 248 { 249 uint8_t pv_num = entry->metadata[0]; 250 return entry->metadata[sizeof(uint8_t) /* pv_num */ + 251 sizeof(uint16_t) * pv_num]; 252 } 253 254 LIBPLDM_ABI_STABLE 255 int pldm_bios_table_attr_entry_enum_decode_def_num_check( 256 const struct pldm_bios_attr_table_entry *entry, uint8_t *def_num) 257 { 258 POINTER_CHECK(entry); 259 POINTER_CHECK(def_num); 260 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION); 261 *def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry); 262 return PLDM_SUCCESS; 263 } 264 265 LIBPLDM_ABI_STABLE 266 int pldm_bios_table_attr_entry_enum_decode_pv_hdls_check( 267 const struct pldm_bios_attr_table_entry *entry, uint16_t *pv_hdls, 268 uint8_t pv_num) 269 { 270 POINTER_CHECK(entry); 271 POINTER_CHECK(pv_hdls); 272 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION); 273 uint8_t num = entry->metadata[0]; 274 num = num < pv_num ? num : pv_num; 275 size_t i; 276 for (i = 0; i < num; i++) { 277 uint16_t *hdl = (uint16_t *)(entry->metadata + sizeof(uint8_t) + 278 i * sizeof(uint16_t)); 279 pv_hdls[i] = le16toh(*hdl); 280 } 281 return PLDM_SUCCESS; 282 } 283 284 LIBPLDM_ABI_STABLE 285 uint8_t pldm_bios_table_attr_entry_enum_decode_def_indices( 286 const struct pldm_bios_attr_table_entry *entry, uint8_t *def_indices, 287 uint8_t def_num) 288 { 289 uint8_t num = pldm_bios_table_attr_entry_enum_decode_def_num(entry); 290 num = num < def_num ? num : def_num; 291 uint8_t pv_num = entry->metadata[0]; 292 const uint8_t *p = entry->metadata + 293 sizeof(uint8_t) /* number of possible values*/ 294 + pv_num * sizeof(uint16_t) /* possible values */ 295 + sizeof(uint8_t); /* number of default values */ 296 memcpy(def_indices, p, num); 297 return num; 298 } 299 300 /** @brief Get length of an enum attribute entry 301 */ 302 static ssize_t attr_table_entry_length_enum(const void *arg) 303 { 304 const struct pldm_bios_attr_table_entry *entry = arg; 305 uint8_t pv_num = entry->metadata[0]; 306 uint8_t def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry); 307 size_t len = 308 pldm_bios_table_attr_entry_enum_encode_length(pv_num, def_num); 309 if (len > SSIZE_MAX) { 310 return -1; 311 } 312 return (ssize_t)len; 313 } 314 315 struct attr_table_string_entry_fields { 316 uint8_t string_type; 317 uint16_t min_length; 318 uint16_t max_length; 319 uint16_t def_length; 320 uint8_t def_string[1]; 321 } __attribute__((packed)); 322 323 LIBPLDM_ABI_STABLE 324 size_t pldm_bios_table_attr_entry_string_encode_length(uint16_t def_str_len) 325 { 326 return sizeof(struct pldm_bios_attr_table_entry) - 327 MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) + 328 sizeof(struct attr_table_string_entry_fields) - 329 MEMBER_SIZE(attr_table_string_entry_fields, def_string) + 330 def_str_len; 331 } 332 333 #define PLDM_STRING_TYPE_MAX 5 334 #define PLDM_STRING_TYPE_VENDOR 0xff 335 336 LIBPLDM_ABI_STABLE 337 int pldm_bios_table_attr_entry_string_info_check( 338 const struct pldm_bios_table_attr_entry_string_info *info, 339 const char **errmsg) 340 { 341 if (info->min_length > info->max_length) { 342 set_errmsg(errmsg, "MinimumStingLength should not be greater " 343 "than MaximumStringLength"); 344 return PLDM_ERROR_INVALID_DATA; 345 } 346 if (info->min_length == info->max_length && 347 info->def_length != info->min_length) { 348 set_errmsg(errmsg, "Wrong DefaultStringLength"); 349 return PLDM_ERROR_INVALID_DATA; 350 } 351 if (info->def_length > info->max_length || 352 info->def_length < info->min_length) { 353 set_errmsg(errmsg, "Wrong DefaultStringLength"); 354 return PLDM_ERROR_INVALID_DATA; 355 } 356 if (info->string_type > PLDM_STRING_TYPE_MAX && 357 info->string_type != PLDM_STRING_TYPE_VENDOR) { 358 set_errmsg(errmsg, "Wrong StringType"); 359 return PLDM_ERROR_INVALID_DATA; 360 } 361 if (info->def_string && info->def_length != strlen(info->def_string)) { 362 set_errmsg(errmsg, "Length of DefaultString should be equal to " 363 "DefaultStringLength"); 364 return PLDM_ERROR_INVALID_DATA; 365 } 366 367 return PLDM_SUCCESS; 368 } 369 370 LIBPLDM_ABI_STABLE 371 int pldm_bios_table_attr_entry_string_encode_check( 372 void *entry, size_t entry_length, 373 const struct pldm_bios_table_attr_entry_string_info *info) 374 { 375 POINTER_CHECK(entry); 376 POINTER_CHECK(info); 377 size_t length = pldm_bios_table_attr_entry_string_encode_length( 378 info->def_length); 379 BUFFER_SIZE_EXPECT(entry_length, length); 380 if (pldm_bios_table_attr_entry_string_info_check(info, NULL) != 381 PLDM_SUCCESS) { 382 return PLDM_ERROR_INVALID_DATA; 383 } 384 uint8_t attr_type = info->read_only ? PLDM_BIOS_STRING_READ_ONLY : 385 PLDM_BIOS_STRING; 386 int rc = attr_table_entry_encode_header(entry, entry_length, attr_type, 387 info->name_handle); 388 if (rc != PLDM_SUCCESS) { 389 return rc; 390 } 391 struct pldm_bios_attr_table_entry *attr_entry = entry; 392 struct attr_table_string_entry_fields *attr_fields = 393 (struct attr_table_string_entry_fields *)attr_entry->metadata; 394 attr_fields->string_type = info->string_type; 395 attr_fields->min_length = htole16(info->min_length); 396 attr_fields->max_length = htole16(info->max_length); 397 attr_fields->def_length = htole16(info->def_length); 398 if (info->def_length != 0 && info->def_string != NULL) { 399 memcpy(attr_fields->def_string, info->def_string, 400 info->def_length); 401 } 402 return PLDM_SUCCESS; 403 } 404 405 static uint16_t pldm_bios_table_attr_entry_string_decode_def_string_length( 406 const struct pldm_bios_attr_table_entry *entry) 407 { 408 struct attr_table_string_entry_fields *fields = 409 (struct attr_table_string_entry_fields *)entry->metadata; 410 return le16toh(fields->def_length); 411 } 412 413 LIBPLDM_ABI_STABLE 414 int pldm_bios_table_attr_entry_string_decode_def_string_length_check( 415 const struct pldm_bios_attr_table_entry *entry, 416 uint16_t *def_string_length) 417 { 418 POINTER_CHECK(entry); 419 POINTER_CHECK(def_string_length); 420 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_STRING); 421 *def_string_length = 422 pldm_bios_table_attr_entry_string_decode_def_string_length( 423 entry); 424 return PLDM_SUCCESS; 425 } 426 427 LIBPLDM_ABI_STABLE 428 uint8_t pldm_bios_table_attr_entry_string_decode_string_type( 429 const struct pldm_bios_attr_table_entry *entry) 430 { 431 struct attr_table_string_entry_fields *fields = 432 (struct attr_table_string_entry_fields *)entry->metadata; 433 return fields->string_type; 434 } 435 436 LIBPLDM_ABI_STABLE 437 uint16_t pldm_bios_table_attr_entry_string_decode_max_length( 438 const struct pldm_bios_attr_table_entry *entry) 439 { 440 struct attr_table_string_entry_fields *fields = 441 (struct attr_table_string_entry_fields *)entry->metadata; 442 return le16toh(fields->max_length); 443 } 444 445 LIBPLDM_ABI_STABLE 446 uint16_t pldm_bios_table_attr_entry_string_decode_min_length( 447 const struct pldm_bios_attr_table_entry *entry) 448 { 449 struct attr_table_string_entry_fields *fields = 450 (struct attr_table_string_entry_fields *)entry->metadata; 451 return le16toh(fields->min_length); 452 } 453 454 LIBPLDM_ABI_STABLE 455 uint16_t pldm_bios_table_attr_entry_string_decode_def_string( 456 const struct pldm_bios_attr_table_entry *entry, char *buffer, 457 size_t size) 458 { 459 uint16_t length = 460 pldm_bios_table_attr_entry_string_decode_def_string_length( 461 entry); 462 length = length < (size - 1) ? length : (size - 1); 463 struct attr_table_string_entry_fields *fields = 464 (struct attr_table_string_entry_fields *)entry->metadata; 465 memcpy(buffer, fields->def_string, length); 466 buffer[length] = 0; 467 return length; 468 } 469 470 /** @brief Get length of a string attribute entry 471 */ 472 static ssize_t attr_table_entry_length_string(const void *entry) 473 { 474 uint16_t def_str_len = 475 pldm_bios_table_attr_entry_string_decode_def_string_length( 476 entry); 477 size_t len = 478 pldm_bios_table_attr_entry_string_encode_length(def_str_len); 479 if (len > SSIZE_MAX) { 480 return -1; 481 } 482 return (ssize_t)len; 483 } 484 485 struct attr_table_integer_entry_fields { 486 uint64_t lower_bound; 487 uint64_t upper_bound; 488 uint32_t scalar_increment; 489 uint64_t default_value; 490 } __attribute__((packed)); 491 492 LIBPLDM_ABI_STABLE 493 size_t pldm_bios_table_attr_entry_integer_encode_length(void) 494 { 495 return sizeof(struct pldm_bios_attr_table_entry) - 1 + 496 sizeof(struct attr_table_integer_entry_fields); 497 } 498 499 LIBPLDM_ABI_STABLE 500 int pldm_bios_table_attr_entry_integer_info_check( 501 const struct pldm_bios_table_attr_entry_integer_info *info, 502 const char **errmsg) 503 { 504 if (info->lower_bound == info->upper_bound) { 505 if (info->default_value != info->lower_bound) { 506 set_errmsg(errmsg, "Wrong DefaultValue"); 507 return PLDM_ERROR_INVALID_DATA; 508 } 509 if (info->scalar_increment != 0) { 510 set_errmsg(errmsg, "Wrong ScalarIncrement"); 511 return PLDM_ERROR_INVALID_DATA; 512 } 513 return PLDM_SUCCESS; 514 } 515 if (info->lower_bound > info->upper_bound) { 516 set_errmsg(errmsg, 517 "LowerBound should not be greater than UpperBound"); 518 return PLDM_ERROR_INVALID_DATA; 519 } 520 if (info->default_value > info->upper_bound || 521 info->default_value < info->lower_bound) { 522 set_errmsg(errmsg, "Wrong DefaultValue"); 523 return PLDM_ERROR_INVALID_DATA; 524 } 525 if (info->scalar_increment == 0) { 526 set_errmsg(errmsg, "ScalarIncrement should not be zero when " 527 "lower_bound != upper_bound"); 528 return PLDM_ERROR_INVALID_DATA; 529 } 530 if ((info->default_value - info->lower_bound) % 531 info->scalar_increment != 532 0) { 533 set_errmsg(errmsg, "Wrong DefaultValue or ScalarIncrement"); 534 return PLDM_ERROR_INVALID_DATA; 535 } 536 return PLDM_SUCCESS; 537 } 538 539 LIBPLDM_ABI_STABLE 540 int pldm_bios_table_attr_entry_integer_encode_check( 541 void *entry, size_t entry_length, 542 const struct pldm_bios_table_attr_entry_integer_info *info) 543 { 544 POINTER_CHECK(entry); 545 POINTER_CHECK(info); 546 size_t length = pldm_bios_table_attr_entry_integer_encode_length(); 547 BUFFER_SIZE_EXPECT(entry_length, length); 548 if (pldm_bios_table_attr_entry_integer_info_check(info, NULL) != 549 PLDM_SUCCESS) { 550 return PLDM_ERROR_INVALID_DATA; 551 } 552 uint8_t attr_type = info->read_only ? PLDM_BIOS_INTEGER_READ_ONLY : 553 PLDM_BIOS_INTEGER; 554 int rc = attr_table_entry_encode_header(entry, entry_length, attr_type, 555 info->name_handle); 556 if (rc != PLDM_SUCCESS) { 557 return rc; 558 } 559 struct pldm_bios_attr_table_entry *attr_entry = entry; 560 struct attr_table_integer_entry_fields *attr_fields = 561 (struct attr_table_integer_entry_fields *)attr_entry->metadata; 562 attr_fields->lower_bound = htole64(info->lower_bound); 563 attr_fields->upper_bound = htole64(info->upper_bound); 564 attr_fields->scalar_increment = htole32(info->scalar_increment); 565 attr_fields->default_value = htole64(info->default_value); 566 return PLDM_SUCCESS; 567 } 568 569 LIBPLDM_ABI_STABLE 570 void pldm_bios_table_attr_entry_integer_decode( 571 const struct pldm_bios_attr_table_entry *entry, uint64_t *lower, 572 uint64_t *upper, uint32_t *scalar, uint64_t *def) 573 { 574 struct attr_table_integer_entry_fields *fields = 575 (struct attr_table_integer_entry_fields *)entry->metadata; 576 *lower = le64toh(fields->lower_bound); 577 *upper = le64toh(fields->upper_bound); 578 *scalar = le32toh(fields->scalar_increment); 579 *def = le64toh(fields->default_value); 580 } 581 582 static ssize_t attr_table_entry_length_integer(const void *entry) 583 { 584 (void)entry; 585 size_t len = pldm_bios_table_attr_entry_integer_encode_length(); 586 if (len > SSIZE_MAX) { 587 return -1; 588 } 589 return (ssize_t)len; 590 } 591 592 struct table_entry_length { 593 uint8_t attr_type; 594 ssize_t (*entry_length_handler)(const void *); 595 }; 596 597 static const struct table_entry_length * 598 find_table_entry_length_by_type(uint8_t attr_type, 599 const struct table_entry_length *handlers, 600 size_t count) 601 { 602 size_t i; 603 for (i = 0; i < count; i++) { 604 if (attr_type == handlers[i].attr_type) { 605 return &handlers[i]; 606 } 607 } 608 return NULL; 609 } 610 611 static const struct table_entry_length attr_table_entries[] = { 612 { .attr_type = PLDM_BIOS_ENUMERATION, 613 .entry_length_handler = attr_table_entry_length_enum }, 614 { .attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY, 615 .entry_length_handler = attr_table_entry_length_enum }, 616 { .attr_type = PLDM_BIOS_STRING, 617 .entry_length_handler = attr_table_entry_length_string }, 618 { .attr_type = PLDM_BIOS_STRING_READ_ONLY, 619 .entry_length_handler = attr_table_entry_length_string }, 620 { .attr_type = PLDM_BIOS_INTEGER, 621 .entry_length_handler = attr_table_entry_length_integer }, 622 { .attr_type = PLDM_BIOS_INTEGER_READ_ONLY, 623 .entry_length_handler = attr_table_entry_length_integer }, 624 }; 625 626 static ssize_t attr_table_entry_length(const void *table_entry) 627 { 628 const struct pldm_bios_attr_table_entry *entry = table_entry; 629 const struct table_entry_length *attr_table_entry = 630 find_table_entry_length_by_type(entry->attr_type, 631 attr_table_entries, 632 ARRAY_SIZE(attr_table_entries)); 633 assert(attr_table_entry != NULL); 634 if (!attr_table_entry) { 635 return -1; 636 } 637 assert(attr_table_entry->entry_length_handler != NULL); 638 if (!attr_table_entry->entry_length_handler) { 639 return -1; 640 } 641 642 return attr_table_entry->entry_length_handler(entry); 643 } 644 645 LIBPLDM_ABI_STABLE 646 uint16_t pldm_bios_table_attr_value_entry_decode_attribute_handle( 647 const struct pldm_bios_attr_val_table_entry *entry) 648 { 649 return le16toh(entry->attr_handle); 650 } 651 652 LIBPLDM_ABI_STABLE 653 uint8_t pldm_bios_table_attr_value_entry_decode_attribute_type( 654 const struct pldm_bios_attr_val_table_entry *entry) 655 { 656 return entry->attr_type; 657 } 658 659 LIBPLDM_ABI_STABLE 660 size_t pldm_bios_table_attr_value_entry_encode_enum_length(uint8_t count) 661 { 662 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 + 663 sizeof(count) + count; 664 } 665 666 LIBPLDM_ABI_STABLE 667 uint8_t pldm_bios_table_attr_value_entry_enum_decode_number( 668 const struct pldm_bios_attr_val_table_entry *entry) 669 { 670 return entry->value[0]; 671 } 672 673 LIBPLDM_ABI_STABLE 674 uint8_t pldm_bios_table_attr_value_entry_enum_decode_handles( 675 const struct pldm_bios_attr_val_table_entry *entry, uint8_t *handles, 676 uint8_t number) 677 { 678 uint8_t curr_num = 679 pldm_bios_table_attr_value_entry_enum_decode_number(entry); 680 curr_num = number < curr_num ? number : curr_num; 681 memcpy(handles, &entry->value[1], curr_num); 682 683 return curr_num; 684 } 685 686 LIBPLDM_ABI_STABLE 687 int pldm_bios_table_attr_value_entry_encode_enum_check( 688 void *entry, size_t entry_length, uint16_t attr_handle, 689 uint8_t attr_type, uint8_t count, const uint8_t *handles) 690 { 691 POINTER_CHECK(entry); 692 POINTER_CHECK(handles); 693 if (count != 0 && handles == NULL) { 694 return PLDM_ERROR_INVALID_DATA; 695 } 696 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_ENUMERATION); 697 size_t length = 698 pldm_bios_table_attr_value_entry_encode_enum_length(count); 699 BUFFER_SIZE_EXPECT(entry_length, length); 700 struct pldm_bios_attr_val_table_entry *table_entry = entry; 701 table_entry->attr_handle = htole16(attr_handle); 702 table_entry->attr_type = attr_type; 703 table_entry->value[0] = count; 704 if (count != 0) { 705 memcpy(&table_entry->value[1], handles, count); 706 } 707 return PLDM_SUCCESS; 708 } 709 710 static ssize_t attr_value_table_entry_length_enum(const void *entry) 711 { 712 uint8_t number = 713 pldm_bios_table_attr_value_entry_enum_decode_number(entry); 714 size_t len = 715 pldm_bios_table_attr_value_entry_encode_enum_length(number); 716 if (len > SSIZE_MAX) { 717 return -1; 718 } 719 return (ssize_t)len; 720 } 721 722 LIBPLDM_ABI_STABLE 723 size_t 724 pldm_bios_table_attr_value_entry_encode_string_length(uint16_t string_length) 725 { 726 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 + 727 sizeof(string_length) + string_length; 728 } 729 730 LIBPLDM_ABI_STABLE 731 uint16_t pldm_bios_table_attr_value_entry_string_decode_length( 732 const struct pldm_bios_attr_val_table_entry *entry) 733 { 734 uint16_t str_length = 0; 735 memcpy(&str_length, entry->value, sizeof(str_length)); 736 return le16toh(str_length); 737 } 738 739 LIBPLDM_ABI_STABLE 740 void pldm_bios_table_attr_value_entry_string_decode_string( 741 const struct pldm_bios_attr_val_table_entry *entry, 742 struct variable_field *current_string) 743 { 744 current_string->length = 745 pldm_bios_table_attr_value_entry_string_decode_length(entry); 746 current_string->ptr = 747 entry->value + sizeof(uint16_t); // sizeof(CurrentStringLength) 748 } 749 750 LIBPLDM_ABI_STABLE 751 int pldm_bios_table_attr_value_entry_encode_string_check( 752 void *entry, size_t entry_length, uint16_t attr_handle, 753 uint8_t attr_type, uint16_t str_length, const char *str) 754 { 755 POINTER_CHECK(entry); 756 if (str_length != 0 && str == NULL) { 757 return PLDM_ERROR_INVALID_DATA; 758 } 759 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_STRING); 760 size_t length = pldm_bios_table_attr_value_entry_encode_string_length( 761 str_length); 762 BUFFER_SIZE_EXPECT(entry_length, length); 763 struct pldm_bios_attr_val_table_entry *table_entry = entry; 764 table_entry->attr_handle = htole16(attr_handle); 765 table_entry->attr_type = attr_type; 766 if (str_length != 0) { 767 memcpy(table_entry->value + sizeof(str_length), str, 768 str_length); 769 } 770 str_length = htole16(str_length); 771 memcpy(table_entry->value, &str_length, sizeof(str_length)); 772 return PLDM_SUCCESS; 773 } 774 775 static ssize_t attr_value_table_entry_length_string(const void *entry) 776 { 777 uint16_t str_length = 778 pldm_bios_table_attr_value_entry_string_decode_length(entry); 779 size_t len = pldm_bios_table_attr_value_entry_encode_string_length( 780 str_length); 781 if (len > SSIZE_MAX) { 782 return -1; 783 } 784 return (ssize_t)len; 785 } 786 787 LIBPLDM_ABI_STABLE 788 size_t pldm_bios_table_attr_value_entry_encode_integer_length(void) 789 { 790 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 + 791 sizeof(uint64_t); 792 } 793 794 LIBPLDM_ABI_STABLE 795 int pldm_bios_table_attr_value_entry_encode_integer_check(void *entry, 796 size_t entry_length, 797 uint16_t attr_handle, 798 uint8_t attr_type, 799 uint64_t cv) 800 { 801 POINTER_CHECK(entry); 802 size_t length = 803 pldm_bios_table_attr_value_entry_encode_integer_length(); 804 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_INTEGER); 805 BUFFER_SIZE_EXPECT(entry_length, length); 806 struct pldm_bios_attr_val_table_entry *table_entry = entry; 807 table_entry->attr_handle = htole16(attr_handle); 808 table_entry->attr_type = attr_type; 809 cv = htole64(cv); 810 memcpy(table_entry->value, &cv, sizeof(uint64_t)); 811 return PLDM_SUCCESS; 812 } 813 814 LIBPLDM_ABI_STABLE 815 uint64_t pldm_bios_table_attr_value_entry_integer_decode_cv( 816 const struct pldm_bios_attr_val_table_entry *entry) 817 { 818 uint64_t cv = 0; 819 memcpy(&cv, entry->value, sizeof(cv)); 820 cv = le64toh(cv); 821 return cv; 822 } 823 824 static ssize_t attr_value_table_entry_length_integer(const void *entry) 825 { 826 (void)entry; 827 size_t len = pldm_bios_table_attr_value_entry_encode_integer_length(); 828 if (len > SSIZE_MAX) { 829 return -1; 830 } 831 return (ssize_t)len; 832 } 833 834 static const struct table_entry_length attr_value_table_entries[] = { 835 { .attr_type = PLDM_BIOS_ENUMERATION, 836 .entry_length_handler = attr_value_table_entry_length_enum }, 837 { .attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY, 838 .entry_length_handler = attr_value_table_entry_length_enum }, 839 { .attr_type = PLDM_BIOS_STRING, 840 .entry_length_handler = attr_value_table_entry_length_string }, 841 { .attr_type = PLDM_BIOS_STRING_READ_ONLY, 842 .entry_length_handler = attr_value_table_entry_length_string }, 843 { .attr_type = PLDM_BIOS_INTEGER, 844 .entry_length_handler = attr_value_table_entry_length_integer }, 845 { .attr_type = PLDM_BIOS_INTEGER_READ_ONLY, 846 .entry_length_handler = attr_value_table_entry_length_integer }, 847 }; 848 849 static ssize_t attr_value_table_entry_length(const void *table_entry) 850 { 851 const struct pldm_bios_attr_val_table_entry *entry = table_entry; 852 const struct table_entry_length *entry_length = 853 find_table_entry_length_by_type( 854 entry->attr_type, attr_value_table_entries, 855 ARRAY_SIZE(attr_value_table_entries)); 856 assert(entry_length != NULL); 857 if (!entry_length) { 858 return -1; 859 } 860 assert(entry_length->entry_length_handler != NULL); 861 if (!entry_length->entry_length_handler) { 862 return -1; 863 } 864 865 return entry_length->entry_length_handler(entry); 866 } 867 868 LIBPLDM_ABI_STABLE 869 size_t pldm_bios_table_attr_value_entry_length( 870 const struct pldm_bios_attr_val_table_entry *entry) 871 { 872 return attr_value_table_entry_length(entry); 873 } 874 875 LIBPLDM_ABI_STABLE 876 uint16_t pldm_bios_table_attr_value_entry_decode_handle( 877 const struct pldm_bios_attr_val_table_entry *entry) 878 { 879 return le16toh(entry->attr_handle); 880 } 881 882 static size_t pad_size_get(size_t size_without_pad) 883 { 884 return ((size_without_pad % 4) ? (4 - size_without_pad % 4) : 0); 885 } 886 887 static uint8_t *pad_append(uint8_t *table_end, size_t pad_size) 888 { 889 while (pad_size--) { 890 *table_end++ = 0; 891 } 892 893 return table_end; 894 } 895 896 static uint8_t *checksum_append(uint8_t *table_end, uint32_t checksum) 897 { 898 checksum = htole32(checksum); 899 memcpy(table_end, &checksum, sizeof(checksum)); 900 901 return table_end + sizeof(checksum); 902 } 903 904 LIBPLDM_ABI_STABLE 905 size_t pldm_bios_table_pad_checksum_size(size_t size_without_pad) 906 { 907 size_t size = pad_size_get(size_without_pad) + 908 sizeof(uint32_t) /*sizeof(checksum)*/; 909 return size; 910 } 911 912 LIBPLDM_ABI_STABLE 913 int pldm_bios_table_append_pad_checksum_check(void *table, size_t capacity, 914 size_t *size) 915 { 916 if (!table || !size) { 917 return PLDM_ERROR_INVALID_DATA; 918 } 919 920 size_t pad_checksum_size = pldm_bios_table_pad_checksum_size(*size); 921 size_t total_length = *size + pad_checksum_size; 922 if (capacity < total_length) { 923 return PLDM_ERROR_INVALID_LENGTH; 924 } 925 926 uint8_t *table_end = (uint8_t *)table + *size; 927 size_t pad_size = pad_size_get(*size); 928 table_end = pad_append(table_end, pad_size); 929 930 uint32_t checksum = crc32(table, *size + pad_size); 931 checksum_append(table_end, checksum); 932 *size = total_length; 933 934 return PLDM_SUCCESS; 935 } 936 937 struct pldm_bios_table_iter { 938 const uint8_t *table_data; 939 size_t table_len; 940 size_t current_pos; 941 ssize_t (*entry_length_handler)(const void *table_entry); 942 }; 943 944 LIBPLDM_ABI_STABLE 945 struct pldm_bios_table_iter * 946 pldm_bios_table_iter_create(const void *table, size_t length, 947 enum pldm_bios_table_types type) 948 { 949 struct pldm_bios_table_iter *iter = malloc(sizeof(*iter)); 950 assert(iter != NULL); 951 if (!iter) { 952 return NULL; 953 } 954 iter->table_data = table; 955 iter->table_len = length; 956 iter->current_pos = 0; 957 iter->entry_length_handler = NULL; 958 switch (type) { 959 case PLDM_BIOS_STRING_TABLE: 960 iter->entry_length_handler = string_table_entry_length; 961 break; 962 case PLDM_BIOS_ATTR_TABLE: 963 iter->entry_length_handler = attr_table_entry_length; 964 break; 965 case PLDM_BIOS_ATTR_VAL_TABLE: 966 iter->entry_length_handler = attr_value_table_entry_length; 967 break; 968 } 969 970 return iter; 971 } 972 973 LIBPLDM_ABI_STABLE 974 void pldm_bios_table_iter_free(struct pldm_bios_table_iter *iter) 975 { 976 free(iter); 977 } 978 979 #define pad_and_check_max 7 980 LIBPLDM_ABI_STABLE 981 bool pldm_bios_table_iter_is_end(const struct pldm_bios_table_iter *iter) 982 { 983 ssize_t len; 984 985 if (iter->table_len - iter->current_pos <= pad_and_check_max) { 986 return true; 987 } 988 989 len = iter->entry_length_handler(iter->table_data + iter->current_pos); 990 991 return len < 0; 992 } 993 994 LIBPLDM_ABI_STABLE 995 void pldm_bios_table_iter_next(struct pldm_bios_table_iter *iter) 996 { 997 if (pldm_bios_table_iter_is_end(iter)) { 998 return; 999 } 1000 const void *entry = iter->table_data + iter->current_pos; 1001 ssize_t rc = iter->entry_length_handler(entry); 1002 /* Prevent bad behaviour by acting as if we've hit the end of the iterator */ 1003 if (rc < 0) { 1004 return; 1005 } 1006 iter->current_pos += rc; 1007 } 1008 1009 LIBPLDM_ABI_STABLE 1010 const void *pldm_bios_table_iter_value(struct pldm_bios_table_iter *iter) 1011 { 1012 return iter->table_data + iter->current_pos; 1013 } 1014 1015 typedef bool (*equal_handler)(const void *entry, const void *key); 1016 1017 static const void * 1018 pldm_bios_table_entry_find_by_iter(struct pldm_bios_table_iter *iter, 1019 const void *key, equal_handler equal) 1020 { 1021 const void *entry; 1022 while (!pldm_bios_table_iter_is_end(iter)) { 1023 entry = pldm_bios_table_iter_value(iter); 1024 if (equal(entry, key)) { 1025 return entry; 1026 } 1027 pldm_bios_table_iter_next(iter); 1028 } 1029 return NULL; 1030 } 1031 1032 static const void * 1033 pldm_bios_table_entry_find_from_table(const void *table, size_t length, 1034 enum pldm_bios_table_types type, 1035 equal_handler equal, const void *key) 1036 { 1037 struct pldm_bios_table_iter *iter = 1038 pldm_bios_table_iter_create(table, length, type); 1039 const void *entry = 1040 pldm_bios_table_entry_find_by_iter(iter, key, equal); 1041 pldm_bios_table_iter_free(iter); 1042 return entry; 1043 } 1044 1045 static bool string_table_handle_equal(const void *entry, const void *key) 1046 { 1047 const struct pldm_bios_string_table_entry *string_entry = entry; 1048 uint16_t handle = *(uint16_t *)key; 1049 if (pldm_bios_table_string_entry_decode_handle(string_entry) == 1050 handle) { 1051 return true; 1052 } 1053 return false; 1054 } 1055 1056 LIBPLDM_ABI_STABLE 1057 const struct pldm_bios_string_table_entry * 1058 pldm_bios_table_string_find_by_handle(const void *table, size_t length, 1059 uint16_t handle) 1060 { 1061 return pldm_bios_table_entry_find_from_table(table, length, 1062 PLDM_BIOS_STRING_TABLE, 1063 string_table_handle_equal, 1064 &handle); 1065 } 1066 1067 struct string_equal_arg { 1068 uint16_t str_length; 1069 const char *str; 1070 }; 1071 1072 static bool string_table_string_equal(const void *entry, const void *key) 1073 { 1074 const struct pldm_bios_string_table_entry *string_entry = entry; 1075 const struct string_equal_arg *arg = key; 1076 if (arg->str_length != 1077 pldm_bios_table_string_entry_decode_string_length(string_entry)) { 1078 return false; 1079 } 1080 if (memcmp(string_entry->name, arg->str, arg->str_length) != 0) { 1081 return false; 1082 } 1083 return true; 1084 } 1085 1086 LIBPLDM_ABI_STABLE 1087 const struct pldm_bios_string_table_entry * 1088 pldm_bios_table_string_find_by_string(const void *table, size_t length, 1089 const char *str) 1090 { 1091 uint16_t str_length = strlen(str); 1092 struct string_equal_arg arg = { str_length, str }; 1093 return pldm_bios_table_entry_find_from_table(table, length, 1094 PLDM_BIOS_STRING_TABLE, 1095 string_table_string_equal, 1096 &arg); 1097 } 1098 1099 static bool attr_table_handle_equal(const void *entry, const void *key) 1100 { 1101 uint16_t handle = *(uint16_t *)key; 1102 return pldm_bios_table_attr_entry_decode_attribute_handle(entry) == 1103 handle; 1104 } 1105 1106 LIBPLDM_ABI_STABLE 1107 const struct pldm_bios_attr_table_entry * 1108 pldm_bios_table_attr_find_by_handle(const void *table, size_t length, 1109 uint16_t handle) 1110 { 1111 return pldm_bios_table_entry_find_from_table(table, length, 1112 PLDM_BIOS_ATTR_TABLE, 1113 attr_table_handle_equal, 1114 &handle); 1115 } 1116 1117 static bool attr_table_string_handle_equal(const void *entry, const void *key) 1118 { 1119 uint16_t handle = *(uint16_t *)key; 1120 return pldm_bios_table_attr_entry_decode_string_handle(entry) == handle; 1121 } 1122 1123 LIBPLDM_ABI_STABLE 1124 const struct pldm_bios_attr_table_entry * 1125 pldm_bios_table_attr_find_by_string_handle(const void *table, size_t length, 1126 uint16_t handle) 1127 { 1128 return pldm_bios_table_entry_find_from_table( 1129 table, length, PLDM_BIOS_ATTR_TABLE, 1130 attr_table_string_handle_equal, &handle); 1131 } 1132 1133 static bool attr_value_table_handle_equal(const void *entry, const void *key) 1134 { 1135 uint16_t handle = *(uint16_t *)key; 1136 return pldm_bios_table_attr_value_entry_decode_handle(entry) == handle; 1137 } 1138 1139 LIBPLDM_ABI_STABLE 1140 const struct pldm_bios_attr_val_table_entry * 1141 pldm_bios_table_attr_value_find_by_handle(const void *table, size_t length, 1142 uint16_t handle) 1143 { 1144 return pldm_bios_table_entry_find_from_table( 1145 table, length, PLDM_BIOS_ATTR_VAL_TABLE, 1146 attr_value_table_handle_equal, &handle); 1147 } 1148 1149 LIBPLDM_ABI_STABLE 1150 int pldm_bios_table_attr_value_copy_and_update( 1151 const void *src_table, size_t src_length, void *dest_table, 1152 size_t *dest_length, const void *entry, size_t entry_length) 1153 { 1154 struct pldm_bios_table_iter *iter = pldm_bios_table_iter_create( 1155 src_table, src_length, PLDM_BIOS_ATTR_VAL_TABLE); 1156 1157 int rc = PLDM_SUCCESS; 1158 const struct pldm_bios_attr_val_table_entry *tmp; 1159 const struct pldm_bios_attr_val_table_entry *to_update = entry; 1160 size_t buffer_length = *dest_length; 1161 size_t copied_length = 0; 1162 size_t length = 0; 1163 while (!pldm_bios_table_iter_is_end(iter)) { 1164 tmp = pldm_bios_table_iter_attr_value_entry_value(iter); 1165 length = attr_value_table_entry_length(tmp); 1166 1167 /* we need the tmp's entry_length here, iter_next will calculate 1168 * it too, use current_pos directly to avoid calculating it 1169 * twice */ 1170 iter->current_pos += length; 1171 if (tmp->attr_handle == to_update->attr_handle) { 1172 if (tmp->attr_type != to_update->attr_type) { 1173 rc = PLDM_ERROR_INVALID_DATA; 1174 goto out; 1175 } 1176 length = entry_length; 1177 tmp = entry; 1178 } 1179 if (copied_length + length > buffer_length) { 1180 rc = PLDM_ERROR_INVALID_LENGTH; 1181 goto out; 1182 } 1183 memcpy((uint8_t *)dest_table + copied_length, tmp, length); 1184 copied_length += length; 1185 } 1186 1187 size_t pad_checksum_size = 1188 pldm_bios_table_pad_checksum_size(copied_length); 1189 if ((pad_checksum_size + copied_length) > buffer_length) { 1190 rc = PLDM_ERROR_INVALID_LENGTH; 1191 goto out; 1192 } 1193 1194 rc = pldm_bios_table_append_pad_checksum_check( 1195 dest_table, buffer_length, &copied_length); 1196 if (rc == PLDM_SUCCESS) { 1197 *dest_length = copied_length; 1198 } 1199 out: 1200 pldm_bios_table_iter_free(iter); 1201 return rc; 1202 } 1203 1204 LIBPLDM_ABI_STABLE 1205 bool pldm_bios_table_checksum(const uint8_t *table, size_t size) 1206 { 1207 if (table == NULL) { 1208 return false; 1209 } 1210 1211 // 12: BIOSStringHandle(uint16) + BIOSStringLength(uint16) + 1212 // Variable(4) + checksum(uint32) 1213 if (size < 12) { 1214 return false; 1215 } 1216 1217 uint32_t src_crc = le32toh(*(uint32_t *)(table + size - 4)); 1218 uint32_t dst_crc = crc32(table, size - 4); 1219 1220 return src_crc == dst_crc; 1221 } 1222