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