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 POINTER_CHECK(entry); 698 POINTER_CHECK(handles); 699 if (count != 0 && handles == NULL) { 700 return PLDM_ERROR_INVALID_DATA; 701 } 702 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_ENUMERATION); 703 size_t length = 704 pldm_bios_table_attr_value_entry_encode_enum_length(count); 705 BUFFER_SIZE_EXPECT(entry_length, length); 706 struct pldm_bios_attr_val_table_entry *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 (count != 0) { 711 memcpy(&table_entry->value[1], handles, count); 712 } 713 return PLDM_SUCCESS; 714 } 715 716 static ssize_t attr_value_table_entry_length_enum(const void *entry) 717 { 718 uint8_t number = 719 pldm_bios_table_attr_value_entry_enum_decode_number(entry); 720 size_t len = 721 pldm_bios_table_attr_value_entry_encode_enum_length(number); 722 if (len > SSIZE_MAX) { 723 return -1; 724 } 725 return (ssize_t)len; 726 } 727 728 LIBPLDM_ABI_STABLE 729 size_t 730 pldm_bios_table_attr_value_entry_encode_string_length(uint16_t string_length) 731 { 732 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 + 733 sizeof(string_length) + string_length; 734 } 735 736 LIBPLDM_ABI_STABLE 737 uint16_t pldm_bios_table_attr_value_entry_string_decode_length( 738 const struct pldm_bios_attr_val_table_entry *entry) 739 { 740 uint16_t str_length = 0; 741 memcpy(&str_length, entry->value, sizeof(str_length)); 742 return le16toh(str_length); 743 } 744 745 LIBPLDM_ABI_STABLE 746 void pldm_bios_table_attr_value_entry_string_decode_string( 747 const struct pldm_bios_attr_val_table_entry *entry, 748 struct variable_field *current_string) 749 { 750 current_string->length = 751 pldm_bios_table_attr_value_entry_string_decode_length(entry); 752 current_string->ptr = 753 entry->value + sizeof(uint16_t); // sizeof(CurrentStringLength) 754 } 755 756 LIBPLDM_ABI_STABLE 757 int pldm_bios_table_attr_value_entry_encode_string( 758 void *entry, size_t entry_length, uint16_t attr_handle, 759 uint8_t attr_type, uint16_t str_length, const char *str) 760 { 761 POINTER_CHECK(entry); 762 if (str_length != 0 && str == NULL) { 763 return PLDM_ERROR_INVALID_DATA; 764 } 765 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_STRING); 766 size_t length = pldm_bios_table_attr_value_entry_encode_string_length( 767 str_length); 768 BUFFER_SIZE_EXPECT(entry_length, length); 769 struct pldm_bios_attr_val_table_entry *table_entry = entry; 770 table_entry->attr_handle = htole16(attr_handle); 771 table_entry->attr_type = attr_type; 772 if (str_length != 0) { 773 memcpy(table_entry->value + sizeof(str_length), str, 774 str_length); 775 } 776 str_length = htole16(str_length); 777 memcpy(table_entry->value, &str_length, sizeof(str_length)); 778 return PLDM_SUCCESS; 779 } 780 781 static ssize_t attr_value_table_entry_length_string(const void *entry) 782 { 783 uint16_t str_length = 784 pldm_bios_table_attr_value_entry_string_decode_length(entry); 785 size_t len = pldm_bios_table_attr_value_entry_encode_string_length( 786 str_length); 787 if (len > SSIZE_MAX) { 788 return -1; 789 } 790 return (ssize_t)len; 791 } 792 793 LIBPLDM_ABI_STABLE 794 size_t pldm_bios_table_attr_value_entry_encode_integer_length(void) 795 { 796 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 + 797 sizeof(uint64_t); 798 } 799 800 LIBPLDM_ABI_STABLE 801 int pldm_bios_table_attr_value_entry_encode_integer(void *entry, 802 size_t entry_length, 803 uint16_t attr_handle, 804 uint8_t attr_type, 805 uint64_t cv) 806 { 807 POINTER_CHECK(entry); 808 size_t length = 809 pldm_bios_table_attr_value_entry_encode_integer_length(); 810 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_INTEGER); 811 BUFFER_SIZE_EXPECT(entry_length, length); 812 struct pldm_bios_attr_val_table_entry *table_entry = entry; 813 table_entry->attr_handle = htole16(attr_handle); 814 table_entry->attr_type = attr_type; 815 cv = htole64(cv); 816 memcpy(table_entry->value, &cv, sizeof(uint64_t)); 817 return PLDM_SUCCESS; 818 } 819 820 LIBPLDM_ABI_STABLE 821 uint64_t pldm_bios_table_attr_value_entry_integer_decode_cv( 822 const struct pldm_bios_attr_val_table_entry *entry) 823 { 824 uint64_t cv = 0; 825 memcpy(&cv, entry->value, sizeof(cv)); 826 cv = le64toh(cv); 827 return cv; 828 } 829 830 static ssize_t attr_value_table_entry_length_integer(const void *entry) 831 { 832 (void)entry; 833 size_t len = pldm_bios_table_attr_value_entry_encode_integer_length(); 834 if (len > SSIZE_MAX) { 835 return -1; 836 } 837 return (ssize_t)len; 838 } 839 840 static const struct table_entry_length attr_value_table_entries[] = { 841 { .attr_type = PLDM_BIOS_ENUMERATION, 842 .entry_length_handler = attr_value_table_entry_length_enum }, 843 { .attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY, 844 .entry_length_handler = attr_value_table_entry_length_enum }, 845 { .attr_type = PLDM_BIOS_STRING, 846 .entry_length_handler = attr_value_table_entry_length_string }, 847 { .attr_type = PLDM_BIOS_STRING_READ_ONLY, 848 .entry_length_handler = attr_value_table_entry_length_string }, 849 { .attr_type = PLDM_BIOS_INTEGER, 850 .entry_length_handler = attr_value_table_entry_length_integer }, 851 { .attr_type = PLDM_BIOS_INTEGER_READ_ONLY, 852 .entry_length_handler = attr_value_table_entry_length_integer }, 853 }; 854 855 static ssize_t attr_value_table_entry_length(const void *table_entry) 856 { 857 const struct pldm_bios_attr_val_table_entry *entry = table_entry; 858 const struct table_entry_length *entry_length = 859 find_table_entry_length_by_type( 860 entry->attr_type, attr_value_table_entries, 861 ARRAY_SIZE(attr_value_table_entries)); 862 assert(entry_length != NULL); 863 if (!entry_length) { 864 return -1; 865 } 866 assert(entry_length->entry_length_handler != NULL); 867 if (!entry_length->entry_length_handler) { 868 return -1; 869 } 870 871 return entry_length->entry_length_handler(entry); 872 } 873 874 LIBPLDM_ABI_STABLE 875 size_t pldm_bios_table_attr_value_entry_length( 876 const struct pldm_bios_attr_val_table_entry *entry) 877 { 878 return attr_value_table_entry_length(entry); 879 } 880 881 LIBPLDM_ABI_STABLE 882 uint16_t pldm_bios_table_attr_value_entry_decode_handle( 883 const struct pldm_bios_attr_val_table_entry *entry) 884 { 885 return le16toh(entry->attr_handle); 886 } 887 888 static size_t pad_size_get(size_t size_without_pad) 889 { 890 return ((size_without_pad % 4) ? (4 - size_without_pad % 4) : 0); 891 } 892 893 static uint8_t *pad_append(uint8_t *table_end, size_t pad_size) 894 { 895 while (pad_size--) { 896 *table_end++ = 0; 897 } 898 899 return table_end; 900 } 901 902 static uint8_t *checksum_append(uint8_t *table_end, uint32_t checksum) 903 { 904 checksum = htole32(checksum); 905 memcpy(table_end, &checksum, sizeof(checksum)); 906 907 return table_end + sizeof(checksum); 908 } 909 910 LIBPLDM_ABI_STABLE 911 size_t pldm_bios_table_pad_checksum_size(size_t size_without_pad) 912 { 913 size_t size = pad_size_get(size_without_pad) + 914 sizeof(uint32_t) /*sizeof(checksum)*/; 915 return size; 916 } 917 918 LIBPLDM_ABI_STABLE 919 int pldm_bios_table_append_pad_checksum(void *table, size_t capacity, 920 size_t *size) 921 { 922 if (!table || !size) { 923 return PLDM_ERROR_INVALID_DATA; 924 } 925 926 size_t pad_checksum_size = pldm_bios_table_pad_checksum_size(*size); 927 size_t total_length = *size + pad_checksum_size; 928 if (capacity < total_length) { 929 return PLDM_ERROR_INVALID_LENGTH; 930 } 931 932 uint8_t *table_end = (uint8_t *)table + *size; 933 size_t pad_size = pad_size_get(*size); 934 table_end = pad_append(table_end, pad_size); 935 936 uint32_t checksum = crc32(table, *size + pad_size); 937 checksum_append(table_end, checksum); 938 *size = total_length; 939 940 return PLDM_SUCCESS; 941 } 942 943 struct pldm_bios_table_iter { 944 const uint8_t *table_data; 945 size_t table_len; 946 size_t current_pos; 947 ssize_t (*entry_length_handler)(const void *table_entry); 948 }; 949 950 LIBPLDM_ABI_STABLE 951 struct pldm_bios_table_iter * 952 pldm_bios_table_iter_create(const void *table, size_t length, 953 enum pldm_bios_table_types type) 954 { 955 struct pldm_bios_table_iter *iter = malloc(sizeof(*iter)); 956 assert(iter != NULL); 957 if (!iter) { 958 return NULL; 959 } 960 iter->table_data = table; 961 iter->table_len = length; 962 iter->current_pos = 0; 963 iter->entry_length_handler = NULL; 964 switch (type) { 965 case PLDM_BIOS_STRING_TABLE: 966 iter->entry_length_handler = string_table_entry_length; 967 break; 968 case PLDM_BIOS_ATTR_TABLE: 969 iter->entry_length_handler = attr_table_entry_length; 970 break; 971 case PLDM_BIOS_ATTR_VAL_TABLE: 972 iter->entry_length_handler = attr_value_table_entry_length; 973 break; 974 } 975 976 return iter; 977 } 978 979 LIBPLDM_ABI_STABLE 980 void pldm_bios_table_iter_free(struct pldm_bios_table_iter *iter) 981 { 982 free(iter); 983 } 984 985 #define pad_and_check_max 7 986 LIBPLDM_ABI_STABLE 987 bool pldm_bios_table_iter_is_end(const struct pldm_bios_table_iter *iter) 988 { 989 ssize_t len; 990 991 if (iter->table_len - iter->current_pos <= pad_and_check_max) { 992 return true; 993 } 994 995 len = iter->entry_length_handler(iter->table_data + iter->current_pos); 996 997 return len < 0; 998 } 999 1000 LIBPLDM_ABI_STABLE 1001 void pldm_bios_table_iter_next(struct pldm_bios_table_iter *iter) 1002 { 1003 if (pldm_bios_table_iter_is_end(iter)) { 1004 return; 1005 } 1006 const void *entry = iter->table_data + iter->current_pos; 1007 ssize_t rc = iter->entry_length_handler(entry); 1008 /* Prevent bad behaviour by acting as if we've hit the end of the iterator */ 1009 if (rc < 0) { 1010 return; 1011 } 1012 iter->current_pos += rc; 1013 } 1014 1015 LIBPLDM_ABI_STABLE 1016 const void *pldm_bios_table_iter_value(struct pldm_bios_table_iter *iter) 1017 { 1018 return iter->table_data + iter->current_pos; 1019 } 1020 1021 typedef bool (*equal_handler)(const void *entry, const void *key); 1022 1023 static const void * 1024 pldm_bios_table_entry_find_by_iter(struct pldm_bios_table_iter *iter, 1025 const void *key, equal_handler equal) 1026 { 1027 const void *entry; 1028 while (!pldm_bios_table_iter_is_end(iter)) { 1029 entry = pldm_bios_table_iter_value(iter); 1030 if (equal(entry, key)) { 1031 return entry; 1032 } 1033 pldm_bios_table_iter_next(iter); 1034 } 1035 return NULL; 1036 } 1037 1038 static const void * 1039 pldm_bios_table_entry_find_from_table(const void *table, size_t length, 1040 enum pldm_bios_table_types type, 1041 equal_handler equal, const void *key) 1042 { 1043 struct pldm_bios_table_iter *iter = 1044 pldm_bios_table_iter_create(table, length, type); 1045 const void *entry = 1046 pldm_bios_table_entry_find_by_iter(iter, key, equal); 1047 pldm_bios_table_iter_free(iter); 1048 return entry; 1049 } 1050 1051 static bool string_table_handle_equal(const void *entry, const void *key) 1052 { 1053 const struct pldm_bios_string_table_entry *string_entry = entry; 1054 uint16_t handle = *(uint16_t *)key; 1055 if (pldm_bios_table_string_entry_decode_handle(string_entry) == 1056 handle) { 1057 return true; 1058 } 1059 return false; 1060 } 1061 1062 LIBPLDM_ABI_STABLE 1063 const struct pldm_bios_string_table_entry * 1064 pldm_bios_table_string_find_by_handle(const void *table, size_t length, 1065 uint16_t handle) 1066 { 1067 return pldm_bios_table_entry_find_from_table(table, length, 1068 PLDM_BIOS_STRING_TABLE, 1069 string_table_handle_equal, 1070 &handle); 1071 } 1072 1073 struct string_equal_arg { 1074 uint16_t str_length; 1075 const char *str; 1076 }; 1077 1078 static bool string_table_string_equal(const void *entry, const void *key) 1079 { 1080 const struct pldm_bios_string_table_entry *string_entry = entry; 1081 const struct string_equal_arg *arg = key; 1082 if (arg->str_length != 1083 pldm_bios_table_string_entry_decode_string_length(string_entry)) { 1084 return false; 1085 } 1086 if (memcmp(string_entry->name, arg->str, arg->str_length) != 0) { 1087 return false; 1088 } 1089 return true; 1090 } 1091 1092 LIBPLDM_ABI_STABLE 1093 const struct pldm_bios_string_table_entry * 1094 pldm_bios_table_string_find_by_string(const void *table, size_t length, 1095 const char *str) 1096 { 1097 uint16_t str_length = strlen(str); 1098 struct string_equal_arg arg = { str_length, str }; 1099 return pldm_bios_table_entry_find_from_table(table, length, 1100 PLDM_BIOS_STRING_TABLE, 1101 string_table_string_equal, 1102 &arg); 1103 } 1104 1105 static bool attr_table_handle_equal(const void *entry, const void *key) 1106 { 1107 uint16_t handle = *(uint16_t *)key; 1108 return pldm_bios_table_attr_entry_decode_attribute_handle(entry) == 1109 handle; 1110 } 1111 1112 LIBPLDM_ABI_STABLE 1113 const struct pldm_bios_attr_table_entry * 1114 pldm_bios_table_attr_find_by_handle(const void *table, size_t length, 1115 uint16_t handle) 1116 { 1117 return pldm_bios_table_entry_find_from_table(table, length, 1118 PLDM_BIOS_ATTR_TABLE, 1119 attr_table_handle_equal, 1120 &handle); 1121 } 1122 1123 static bool attr_table_string_handle_equal(const void *entry, const void *key) 1124 { 1125 uint16_t handle = *(uint16_t *)key; 1126 return pldm_bios_table_attr_entry_decode_string_handle(entry) == handle; 1127 } 1128 1129 LIBPLDM_ABI_STABLE 1130 const struct pldm_bios_attr_table_entry * 1131 pldm_bios_table_attr_find_by_string_handle(const void *table, size_t length, 1132 uint16_t handle) 1133 { 1134 return pldm_bios_table_entry_find_from_table( 1135 table, length, PLDM_BIOS_ATTR_TABLE, 1136 attr_table_string_handle_equal, &handle); 1137 } 1138 1139 static bool attr_value_table_handle_equal(const void *entry, const void *key) 1140 { 1141 uint16_t handle = *(uint16_t *)key; 1142 return pldm_bios_table_attr_value_entry_decode_handle(entry) == handle; 1143 } 1144 1145 LIBPLDM_ABI_STABLE 1146 const struct pldm_bios_attr_val_table_entry * 1147 pldm_bios_table_attr_value_find_by_handle(const void *table, size_t length, 1148 uint16_t handle) 1149 { 1150 return pldm_bios_table_entry_find_from_table( 1151 table, length, PLDM_BIOS_ATTR_VAL_TABLE, 1152 attr_value_table_handle_equal, &handle); 1153 } 1154 1155 LIBPLDM_ABI_STABLE 1156 int pldm_bios_table_attr_value_copy_and_update( 1157 const void *src_table, size_t src_length, void *dest_table, 1158 size_t *dest_length, const void *entry, size_t entry_length) 1159 { 1160 struct pldm_bios_table_iter *iter = pldm_bios_table_iter_create( 1161 src_table, src_length, PLDM_BIOS_ATTR_VAL_TABLE); 1162 1163 int rc = PLDM_SUCCESS; 1164 const struct pldm_bios_attr_val_table_entry *tmp; 1165 const struct pldm_bios_attr_val_table_entry *to_update = entry; 1166 size_t buffer_length = *dest_length; 1167 size_t copied_length = 0; 1168 size_t length = 0; 1169 while (!pldm_bios_table_iter_is_end(iter)) { 1170 tmp = pldm_bios_table_iter_attr_value_entry_value(iter); 1171 length = attr_value_table_entry_length(tmp); 1172 1173 /* we need the tmp's entry_length here, iter_next will calculate 1174 * it too, use current_pos directly to avoid calculating it 1175 * twice */ 1176 iter->current_pos += length; 1177 if (tmp->attr_handle == to_update->attr_handle) { 1178 if (tmp->attr_type != to_update->attr_type) { 1179 rc = PLDM_ERROR_INVALID_DATA; 1180 goto out; 1181 } 1182 length = entry_length; 1183 tmp = entry; 1184 } 1185 if (copied_length + length > buffer_length) { 1186 rc = PLDM_ERROR_INVALID_LENGTH; 1187 goto out; 1188 } 1189 memcpy((uint8_t *)dest_table + copied_length, tmp, length); 1190 copied_length += length; 1191 } 1192 1193 size_t pad_checksum_size = 1194 pldm_bios_table_pad_checksum_size(copied_length); 1195 if ((pad_checksum_size + copied_length) > buffer_length) { 1196 rc = PLDM_ERROR_INVALID_LENGTH; 1197 goto out; 1198 } 1199 1200 rc = pldm_bios_table_append_pad_checksum(dest_table, buffer_length, 1201 &copied_length); 1202 if (rc == PLDM_SUCCESS) { 1203 *dest_length = copied_length; 1204 } 1205 out: 1206 pldm_bios_table_iter_free(iter); 1207 return rc; 1208 } 1209 1210 LIBPLDM_ABI_STABLE 1211 bool pldm_bios_table_checksum(const uint8_t *table, size_t size) 1212 { 1213 if (table == NULL) { 1214 return false; 1215 } 1216 1217 // 12: BIOSStringHandle(uint16) + BIOSStringLength(uint16) + 1218 // Variable(4) + checksum(uint32) 1219 if (size < 12) { 1220 return false; 1221 } 1222 1223 uint32_t src_crc = le32toh(*(uint32_t *)(table + size - 4)); 1224 uint32_t dst_crc = crc32(table, size - 4); 1225 1226 return src_crc == dst_crc; 1227 } 1228