acpi: Add IORT helper functions
[coreboot2.git] / util / cbfstool / fit.c
blob8ed24943b836a97af625636571ffe0dee5d0773a
1 /* Firmware Interface Table support */
2 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <inttypes.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
10 #include "fit.h"
12 /* FIXME: This code assumes it is being executed on a little endian machine. */
14 #define FIT_POINTER_LOCATION 0xffffffc0
15 #define FIT_TABLE_LOWEST_ADDRESS ((uint32_t)(-(16 << 20)))
16 #define FIT_ENTRY_CHECKSUM_VALID 0x80
17 #define FIT_HEADER_VERSION 0x0100
18 #define FIT_HEADER_ADDRESS "_FIT_ "
19 #define FIT_MICROCODE_VERSION 0x0100
20 #define FIT_TXT_VERSION 0x0100
22 #define FIT_SIZE_ALIGNMENT 16
24 struct fit_entry {
25 /**
26 * Address is the base address of the firmware component
27 * must be aligned on 16 byte boundary
29 uint64_t address;
30 /**
31 * Size is the span of the component in multiple of 16 bytes
32 * Bits [24:31] are reserved and must be set to 0
34 uint32_t size_reserved;
35 /**
36 * Component's version number in binary coded decimal (BCD) format.
37 * For the FIT header entry, the value in this field will indicate the
38 * revision number of the FIT data structure. The upper byte of the
39 * revision field indicates the major revision and the lower byte
40 * indicates the minor revision.
42 uint16_t version;
43 /**
44 * FIT types 0x00 to 0x7F
45 * Bit 7 (C_V) indicates whether component has valid checksum.
47 uint8_t type_checksum_valid;
48 /**
49 * Component's checksum. The modulo sum of all the bytes in the
50 * component and the value in this field (Chksum) must add up to zero.
51 * This field is only valid if the C_V flag is non-zero.
53 uint8_t checksum;
54 } __packed;
56 struct fit_table {
57 struct fit_entry header;
58 struct fit_entry entries[];
59 } __packed;
61 struct microcode_header {
62 uint32_t version;
63 uint32_t revision;
64 uint32_t date;
65 uint32_t processor_signature;
66 uint32_t checksum;
67 uint32_t loader_revision;
68 uint32_t processor_flags;
69 uint32_t data_size;
70 uint32_t total_size;
71 uint8_t reserved[12];
72 } __packed;
74 struct microcode_entry {
75 int offset;
76 int size;
79 static inline void *rom_buffer_pointer(struct buffer *buffer, int offset)
81 return &buffer->data[offset];
84 static inline size_t fit_entry_size_bytes(const struct fit_entry *entry)
86 return (entry->size_reserved & 0xffffff) << 4;
89 static inline void fit_entry_update_size(struct fit_entry *entry,
90 const int size_bytes)
92 /* Size is multiples of 16 bytes. */
93 entry->size_reserved = (size_bytes >> 4) & 0xffffff;
96 static inline void fit_entry_add_size(struct fit_entry *entry,
97 const int size_bytes)
99 int size = fit_entry_size_bytes(entry);
100 size += size_bytes;
101 fit_entry_update_size(entry, size);
104 static inline int fit_entry_type(struct fit_entry *entry)
106 return entry->type_checksum_valid & ~FIT_ENTRY_CHECKSUM_VALID;
110 * Get an offset from a host pointer. This function assumes the ROM is located
111 * in the host address space at [4G - romsize -> 4G). It also assume all
112 * pointers have values within this address range.
114 static inline int ptr_to_offset(fit_offset_converter_t helper,
115 const struct buffer *region, uint32_t host_ptr)
117 return helper(region, -host_ptr);
121 * Get a pointer from an offset. This function assumes the ROM is located
122 * in the host address space at [4G - romsize -> 4G). It also assume all
123 * pointers have values within this address range.
125 static inline uint32_t offset_to_ptr(fit_offset_converter_t helper,
126 const struct buffer *region, int offset)
128 return -helper(region, offset);
132 * Return the number of FIT entries.
134 static inline size_t fit_table_entries(const struct fit_table *fit)
136 if (!fit)
137 return 0;
139 return (fit_entry_size_bytes(&fit->header) / FIT_SIZE_ALIGNMENT) - 1;
143 * Return the number of unused entries.
145 static inline size_t fit_free_space(struct fit_table *fit,
146 const size_t max_entries)
148 if (!fit)
149 return 0;
151 return max_entries - fit_table_entries(fit);
155 * Sort entries by type and fill gaps (entries with type unused).
156 * To be called after adding or deleting entries.
158 * This one is critical, as mentioned in Chapter 1.2.1 "FIT Ordering Rules"
159 * "Firmware Interface Table BIOS Specification".
161 * We need to use a stable sorting algorithm, as the order of
162 * FIT_TYPE_BIOS_STARTUP matter for measurements.
164 static void sort_fit_table(struct fit_table *fit)
166 struct fit_entry tmp;
167 size_t i, j;
168 int swapped;
170 /* Bubble sort entries */
171 for (j = 0; j < fit_table_entries(fit) - 1; j++) {
172 swapped = 0;
173 for (i = 0; i < fit_table_entries(fit) - j - 1; i++) {
174 if (fit->entries[i].type_checksum_valid <=
175 fit->entries[i + 1].type_checksum_valid)
176 continue;
177 /* SWAP entries */
178 memcpy(&tmp, &fit->entries[i], sizeof(tmp));
179 memcpy(&fit->entries[i], &fit->entries[i + 1],
180 sizeof(fit->entries[i]));
181 memcpy(&fit->entries[i + 1], &tmp,
182 sizeof(fit->entries[i + 1]));
183 swapped = 1;
185 if (!swapped)
186 break;
190 static int fit_table_verified(struct fit_table *table)
192 if (!table)
193 return 0;
195 /* Check that the address field has the proper signature. */
196 if (strncmp((const char *)&table->header.address, FIT_HEADER_ADDRESS,
197 sizeof(table->header.address)))
198 return 0;
200 if (table->header.version != FIT_HEADER_VERSION)
201 return 0;
203 if (fit_entry_type(&table->header) != FIT_TYPE_HEADER)
204 return 0;
206 /* Assume that the FIT table contains at least the header */
207 if (fit_entry_size_bytes(&table->header) < sizeof(struct fit_entry))
208 return 0;
210 return 1;
214 * Update the FIT checksum.
215 * To be called after modifiying the table.
217 static void update_fit_checksum(struct fit_table *fit)
219 int size_bytes;
220 uint8_t *buffer;
221 uint8_t result;
222 int i;
224 if (!fit)
225 return;
227 fit->header.checksum = 0;
228 size_bytes = fit_entry_size_bytes(&fit->header);
229 result = 0;
230 buffer = (void *)fit;
231 for (i = 0; i < size_bytes; i++)
232 result += buffer[i];
233 fit->header.checksum = -result;
237 * Return a pointer to the next free entry.
238 * Caller must take care if enough space is available.
240 static struct fit_entry *get_next_free_entry(struct fit_table *fit)
242 return &fit->entries[fit_table_entries(fit)];
245 static void fit_location_from_cbfs_header(uint32_t *current_offset,
246 uint32_t *file_length, void *ptr)
248 struct buffer buf;
249 struct cbfs_file header;
250 memset(&buf, 0, sizeof(buf));
252 buf.data = ptr;
253 buf.size = sizeof(header);
255 bgets(&buf, header.magic, sizeof(header.magic));
256 header.len = xdr_be.get32(&buf);
257 header.type = xdr_be.get32(&buf);
258 header.attributes_offset = xdr_be.get32(&buf);
259 header.offset = xdr_be.get32(&buf);
261 *current_offset = header.offset;
262 *file_length = header.len;
265 static int
266 parse_microcode_blob(struct cbfs_image *image,
267 const char *blob_name,
268 size_t *mcus_found,
269 struct microcode_entry *mcus,
270 const size_t max_fit_entries)
272 size_t num_mcus;
273 uint32_t current_offset;
274 uint32_t file_length;
275 struct cbfs_file *mcode_file;
277 mcode_file = cbfs_get_entry(image, blob_name);
278 if (!mcode_file) {
279 ERROR("Couldn't find microcode blob.\n");
280 return 1;
283 fit_location_from_cbfs_header(&current_offset, &file_length,
284 mcode_file);
285 current_offset += cbfs_get_entry_addr(image, mcode_file);
287 num_mcus = 0;
288 while (file_length > sizeof(struct microcode_header)) {
289 const struct microcode_header *mcu_header;
291 mcu_header = rom_buffer_pointer(&image->buffer, current_offset);
292 if (!mcu_header) {
293 ERROR("Couldn't parse microcode header.\n");
294 return 1;
297 /* Newer microcode updates include a size field, whereas older
298 * containers set it at 0 and are exactly 2048 bytes long */
299 uint32_t total_size = mcu_header->total_size ?: 2048;
301 /* Quickly sanity check a prospective microcode update. */
302 if (total_size < sizeof(*mcu_header) ||
303 total_size > file_length)
304 break;
306 if (num_mcus == max_fit_entries) {
307 ERROR("Maximum of FIT entries reached.\n");
308 return 1;
311 /* FIXME: Should the checksum be validated? */
312 mcus[num_mcus].offset = current_offset;
313 mcus[num_mcus].size = total_size;
315 /* Proceed to next payload. */
316 current_offset += mcus[num_mcus].size;
317 file_length -= mcus[num_mcus].size;
318 num_mcus++;
319 if (file_length < sizeof(struct microcode_header))
320 break;
323 /* Update how many microcode updates we found. */
324 *mcus_found = num_mcus;
326 return 0;
329 /* There can be zero or more FIT_TYPE_MICROCODE entries */
330 static void update_fit_ucode_entry(struct fit_table *fit,
331 struct fit_entry *entry,
332 const uint64_t mcu_addr)
334 entry->address = mcu_addr;
336 * While loading MCU, its size is not referred from FIT and
337 * rather from the MCU header, hence we can assign zero here.
339 entry->size_reserved = 0;
340 entry->type_checksum_valid = FIT_TYPE_MICROCODE;
341 entry->version = FIT_MICROCODE_VERSION;
342 entry->checksum = 0;
343 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
347 * There can be zero or one FIT_TYPE_BIOS_ACM entry per table.
348 * In case there's a FIT_TYPE_BIOS_ACM entry, at least one
349 * FIT_TYPE_BIOS_STARTUP entry must exist.
351 * The caller has to provide valid arguments as those aren't verfied.
353 static void update_fit_bios_acm_entry(struct fit_table *fit,
354 struct fit_entry *entry,
355 const uint64_t acm_addr)
357 entry->address = acm_addr;
359 * The Address field points to a BIOS ACM. The Address field points to
360 * the first byte of the AC module header. When BIOS ACM is loaded in
361 * Authenticated Code RAM, one MTRR base/limit pair is used to map it.
363 entry->size_reserved = 0;
364 entry->type_checksum_valid = FIT_TYPE_BIOS_ACM;
365 entry->version = FIT_TXT_VERSION;
366 entry->checksum = 0;
367 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
371 * In case there's a FIT_TYPE_BIOS_ACM entry, at least one
372 * FIT_TYPE_BIOS_STARTUP entry must exist.
374 * The caller has to provide valid arguments as those aren't verfied.
376 static void update_fit_bios_startup_entry(struct fit_table *fit,
377 struct fit_entry *entry,
378 const uint64_t sm_addr,
379 const uint32_t sm_size)
381 entry->address = sm_addr;
382 assert(sm_size % 16 == 0);
384 * BIOS Startup code is defined as the code that gets control at the
385 * reset vector and continues the chain of trust in TCG-compliant
386 * fashion. In addition, this code may also configure memory and SMRAM.
388 fit_entry_update_size(entry, sm_size);
389 entry->type_checksum_valid = FIT_TYPE_BIOS_STARTUP;
390 entry->version = FIT_TXT_VERSION;
391 entry->checksum = 0;
392 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
396 * There can be zero or one FIT_TYPE_BIOS_POLICY Record in the FIT.
397 * If the platform uses the hash comparison method and employs a
398 * failsafe bootblock, one FIT_TYPE_BIOS_POLICY entry is needed to
399 * contain the failsafe hash.
400 * If the platform uses the Signature verification method, one
401 * FIT_TYPE_BIOS_POLICY entry is needed. In this case, the entry
402 * contains the OEM key, hash of the BIOS and signature over the hash
403 * using the OEM key.
404 * In all other cases, the FIT_TYPE_BIOS_POLICY record is not required.
406 * The caller has to provide valid arguments as those aren't verfied.
408 static void update_fit_bios_policy_entry(struct fit_table *fit,
409 struct fit_entry *entry,
410 const uint64_t lcp_policy_addr,
411 const uint32_t lcp_policy_size)
413 entry->address = lcp_policy_addr;
414 fit_entry_update_size(entry, lcp_policy_size);
415 entry->type_checksum_valid = FIT_TYPE_BIOS_POLICY;
416 entry->version = FIT_TXT_VERSION;
417 entry->checksum = 0;
418 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
422 * There can be zero or one FIT_TYPE_TXT_POLICY entries
424 * The caller has to provide valid arguments as those aren't verfied.
426 static void update_fit_txt_policy_entry(struct fit_table *fit,
427 struct fit_entry *entry,
428 uint64_t txt_policy_addr)
430 entry->address = txt_policy_addr;
432 * Points to the flag indicating if TXT is enabled on this platform.
433 * If not present, TXT is not disabled by FIT.
435 entry->size_reserved = 0;
436 entry->type_checksum_valid = FIT_TYPE_TXT_POLICY;
437 entry->version = 0x1;
438 entry->checksum = 0;
439 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
443 * There can be zero or one FIT_TYPE_BOOT_POLICY entries
445 * The caller has to provide valid arguments as those aren't verified.
447 static void update_fit_boot_policy_entry(struct fit_table *fit,
448 struct fit_entry *entry,
449 uint64_t boot_policy_addr,
450 uint32_t boot_policy_size)
452 entry->address = boot_policy_addr;
453 entry->type_checksum_valid = FIT_TYPE_BOOT_POLICY;
454 entry->size_reserved = boot_policy_size;
455 entry->version = FIT_TXT_VERSION;
456 entry->checksum = 0;
457 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
461 * There can be zero or one FIT_TYPE_KEY_MANIFEST entries
463 * The caller has to provide valid arguments as those aren't verified.
465 static void update_fit_key_manifest_entry(struct fit_table *fit,
466 struct fit_entry *entry,
467 uint64_t key_manifest_addr,
468 uint32_t key_manifest_size)
470 entry->address = key_manifest_addr;
472 entry->type_checksum_valid = FIT_TYPE_KEY_MANIFEST;
473 entry->size_reserved = key_manifest_size;
474 entry->version = FIT_TXT_VERSION;
475 entry->checksum = 0;
476 fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
479 /* Special case for ucode CBFS file, as it might contain more than one ucode */
480 int fit_add_microcode_file(struct fit_table *fit,
481 struct cbfs_image *image,
482 const char *blob_name,
483 fit_offset_converter_t offset_helper,
484 const size_t max_fit_entries)
486 struct microcode_entry *mcus;
488 size_t i;
489 size_t mcus_found;
491 mcus = malloc(sizeof(*mcus) * max_fit_entries);
492 if (!mcus) {
493 ERROR("Couldn't allocate memory for microcode entries.\n");
494 return 1;
497 if (parse_microcode_blob(image, blob_name, &mcus_found, mcus,
498 max_fit_entries)) {
499 free(mcus);
500 return 1;
503 for (i = 0; i < mcus_found; i++) {
504 if (fit_add_entry(fit,
505 offset_to_ptr(offset_helper, &image->buffer,
506 mcus[i].offset),
508 FIT_TYPE_MICROCODE,
509 max_fit_entries)) {
511 free(mcus);
512 return 1;
516 free(mcus);
517 return 0;
520 static uint32_t *get_fit_ptr(struct buffer *bootblock, fit_offset_converter_t offset_fn,
521 uint32_t topswap_size)
523 return rom_buffer_pointer(bootblock,
524 ptr_to_offset(offset_fn, bootblock,
525 FIT_POINTER_LOCATION - topswap_size));
528 /* Set the FIT pointer to a FIT table. */
529 int set_fit_pointer(struct buffer *bootblock,
530 const uint32_t fit_address,
531 fit_offset_converter_t offset_fn,
532 uint32_t topswap_size)
534 struct fit_table *fit;
535 uint32_t *fit_pointer = get_fit_ptr(bootblock, offset_fn, topswap_size);
537 fit = rom_buffer_pointer(bootblock, ptr_to_offset(offset_fn, bootblock, fit_address));
539 if (fit_address < FIT_TABLE_LOWEST_ADDRESS) {
540 ERROR("FIT must be reside in the top 16MiB.\n");
541 return 1;
544 if (!fit_table_verified(fit)) {
545 ERROR("FIT not found at address.\n");
546 return 1;
549 fit_pointer[0] = fit_address;
550 fit_pointer[1] = 0;
551 return 0;
555 * Return a pointer to the active FIT.
557 struct fit_table *fit_get_table(struct buffer *bootblock,
558 fit_offset_converter_t offset_fn,
559 uint32_t topswap_size)
561 struct fit_table *fit;
562 uint32_t *fit_pointer = get_fit_ptr(bootblock, offset_fn, topswap_size);
564 /* Ensure pointer is below 4GiB and within 16MiB of 4GiB */
565 if (fit_pointer[1] != 0 || fit_pointer[0] < FIT_TABLE_LOWEST_ADDRESS) {
566 ERROR("FIT not found.\n");
567 return NULL;
570 fit = rom_buffer_pointer(bootblock,
571 ptr_to_offset(offset_fn, bootblock, *fit_pointer));
572 if (!fit_table_verified(fit)) {
573 ERROR("FIT not found.\n");
574 return NULL;
577 return fit;
581 * Dump the current FIT in human readable format to stdout.
583 int fit_dump(struct fit_table *fit)
585 size_t i;
587 if (!fit)
588 return 1;
590 printf("\n");
591 printf(" FIT table:\n");
593 if (fit_table_entries(fit) < 1) {
594 printf(" empty\n\n");
595 return 0;
598 printf(" %-6s %-20s %-16s %-8s\n", "Index", "Type", "Addr", "Size");
600 for (i = 0; i < fit_table_entries(fit); i++) {
601 const char *name;
603 switch (fit->entries[i].type_checksum_valid) {
604 case FIT_TYPE_MICROCODE:
605 name = "Microcode";
606 break;
607 case FIT_TYPE_BIOS_ACM:
608 name = "BIOS ACM";
609 break;
610 case FIT_TYPE_BIOS_STARTUP:
611 name = "BIOS Startup Module";
612 break;
613 case FIT_TYPE_TPM_POLICY:
614 name = "TPM Policy";
615 break;
616 case FIT_TYPE_BIOS_POLICY:
617 name = "BIOS Policy";
618 break;
619 case FIT_TYPE_TXT_POLICY:
620 name = "TXT Policy";
621 break;
622 case FIT_TYPE_KEY_MANIFEST:
623 name = "Key Manifest";
624 break;
625 case FIT_TYPE_BOOT_POLICY:
626 name = "Boot Policy";
627 break;
628 case FIT_TYPE_CSE_SECURE_BOOT:
629 name = "CSE SecureBoot";
630 break;
631 case FIT_TYPE_TXTSX_POLICY:
632 name = "TXTSX policy";
633 break;
634 case FIT_TYPE_JMP_DEBUG_POLICY:
635 name = "JMP debug policy";
636 break;
637 case FIT_TYPE_UNUSED:
638 name = "unused";
639 break;
640 default:
641 name = "unknown";
644 printf(" %6zd %-20s 0x%08"PRIx64" 0x%08zx\n", i, name,
645 fit->entries[i].address,
646 fit_entry_size_bytes(&fit->entries[i]));
648 printf("\n");
649 return 0;
653 * Remove all entries from table.
655 int fit_clear_table(struct fit_table *fit)
657 if (!fit)
658 return 1;
660 memset(fit->entries, 0,
661 sizeof(struct fit_entry) * fit_table_entries(fit));
663 /* Reset entry counter in header */
664 fit_entry_update_size(&fit->header, sizeof(fit->header));
666 update_fit_checksum(fit);
668 return 0;
672 * Returns true if the FIT type is know and can be added to the table.
674 int fit_is_supported_type(const enum fit_type type)
676 switch (type) {
677 case FIT_TYPE_MICROCODE:
678 case FIT_TYPE_BIOS_ACM:
679 case FIT_TYPE_BIOS_STARTUP:
680 case FIT_TYPE_BIOS_POLICY:
681 case FIT_TYPE_TXT_POLICY:
682 case FIT_TYPE_KEY_MANIFEST:
683 case FIT_TYPE_BOOT_POLICY:
684 return 1;
685 case FIT_TYPE_TPM_POLICY:
686 default:
687 return 0;
692 * Adds an known entry to the FIT.
693 * len is optional for same types and might be zero.
694 * offset is an absolute address in 32-bit protected mode address space.
696 int fit_add_entry(struct fit_table *fit,
697 const uint32_t offset,
698 const uint32_t len,
699 const enum fit_type type,
700 const size_t max_fit_entries)
702 struct fit_entry *entry;
704 if (!fit) {
705 ERROR("Internal error.");
706 return 1;
709 if (fit_free_space(fit, max_fit_entries) < 1) {
710 ERROR("No space left in FIT.");
711 return 1;
714 if (!fit_is_supported_type(type)) {
715 ERROR("Unsupported FIT type %u\n", type);
716 return 1;
719 DEBUG("Adding new entry type %u at offset %zd\n", type,
720 fit_table_entries(fit));
722 entry = get_next_free_entry(fit);
724 switch (type) {
725 case FIT_TYPE_MICROCODE:
726 update_fit_ucode_entry(fit, entry, offset);
727 break;
728 case FIT_TYPE_BIOS_ACM:
729 update_fit_bios_acm_entry(fit, entry, offset);
730 break;
731 case FIT_TYPE_BIOS_STARTUP:
732 update_fit_bios_startup_entry(fit, entry, offset, len);
733 break;
734 case FIT_TYPE_BIOS_POLICY:
735 update_fit_bios_policy_entry(fit, entry, offset, len);
736 break;
737 case FIT_TYPE_TXT_POLICY:
738 update_fit_txt_policy_entry(fit, entry, offset);
739 break;
740 case FIT_TYPE_KEY_MANIFEST:
741 update_fit_key_manifest_entry(fit, entry, offset, len);
742 break;
743 case FIT_TYPE_BOOT_POLICY:
744 update_fit_boot_policy_entry(fit, entry, offset, len);
745 break;
746 default:
747 return 1;
750 sort_fit_table(fit);
752 update_fit_checksum(fit);
754 return 0;
758 * Delete one entry from table.
760 int fit_delete_entry(struct fit_table *fit,
761 const size_t idx)
763 if (!fit) {
764 ERROR("Internal error.");
765 return 1;
768 if (idx >= fit_table_entries(fit)) {
769 ERROR("Index out of range.");
770 return 1;
773 memset(&fit->entries[idx], 0, sizeof(struct fit_entry));
775 fit->entries[idx].type_checksum_valid = FIT_TYPE_UNUSED;
777 sort_fit_table(fit);
779 /* The unused entry is now the last one */
780 fit_entry_add_size(&fit->header, -(int)sizeof(struct fit_entry));
782 update_fit_checksum(fit);
784 return 0;