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