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