diff --git a/Makefile b/Makefile index d6c71c9..06ed91a 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,9 @@ endif CFLAGS += -Os -g -MMD -MP -mno-sse -mno-mmx -fpie -fomit-frame-pointer CFLAGS += -Iinclude -ffreestanding -fno-common -Wall -Werror LDFLAGS += -nostdlib -no-pie -Wl,--build-id=none,--fatal-warnings,--defsym=BITS=$(BITS) +# Keep the code and the small .bss of link.lds in separate LOAD segments, so +# ld 2.39 and later do not warn about an RWX segment. +LDFLAGS += -Wl,-z,max-page-size=0x10 CFLAGS_TPMLIB := -include boot.h -include errno-base.h -include byteswap.h -DEBADRQC=EINVAL @@ -65,8 +68,8 @@ skl.bin: skl Makefile objcopy -O binary -S -R '.note.*' -R '.got.plt' -R '.skl_sig' -R '.skl_pubkey_hdr' -R '.skl_pubkey_modulus' $< $@ @./sanity_check.sh ifeq ($(AMDSL), y) - # create signature - openssl dgst -sha384 -sign testPrivateKey.pem -out signature_rev.bin -binary -sigopt rsa_padding_mode:pss $@ + # create signature, RSA-PSS with a salt of the digest length as the PSP expects + openssl dgst -sha384 -sign testPrivateKey.pem -out signature_rev.bin -binary -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest $@ # reverse byte order xxd -c1 -p signature_rev.bin | tac | xxd -r -p > signature.bin # get the modulus for pubkey header, reverse byte order diff --git a/event_log.c b/event_log.c index 483697c..7afae47 100644 --- a/event_log.c +++ b/event_log.c @@ -17,11 +17,13 @@ */ #include +#include #include #include #include #include #include "tpmlib/tpm.h" +#include "tpmlib/tpm_common.h" #include "tpmlib/tpm2_constants.h" static u8 *evtlog_base; @@ -43,7 +45,38 @@ static int log_write(const void *data, unsigned size) #define EV_NO_ACTION 0x3 #define EV_TYPE_SLAUNCH 0x502 -#define HASH_COUNT 2 +/* + * One bank per algorithm the SKL knows the digest size of: SHA-1, SHA-256, + * SHA-384, SHA-512 and SM3-256. + */ +#define MAX_BANKS 5 + +/* + * The TPM's allocated banks in its order, which the TPM 2.0 log declares + * and every record of it carries. Where the SKL cannot compute a digest, + * the record carries 01 followed by zeros, as Linux and Xen log for such + * a bank. The Intel TXT Software Development Guide calls it OneDigest. + */ +static struct __packed { + u16 alg; + u16 size; +} banks[MAX_BANKS]; +static u32 nbanks; + +static const u8 one_digest[SHA512_SIZE] = { 1 }; + +static u16 digest_size(u16 alg) +{ + switch ( alg ) + { + case TPM_ALG_SHA1: return SHA1_SIZE; + case TPM_ALG_SHA256: return SHA256_SIZE; + case TPM_ALG_SHA384: return SHA384_SIZE; + case TPM_ALG_SHA512: return SHA512_SIZE; + case TPM_ALG_SM3_256: return SM3256_SIZE; + default: return 0; + } +} /* For compatibility with TXT and easier operations */ @@ -69,6 +102,16 @@ typedef struct __packed { u32 next_record_offset; } txt_event_log_pointer2_1_element; +/* Event log entries */ + +typedef struct __packed { + u32 pcr; + u32 event_type; + u8 digest[SHA1_DIGEST_SIZE]; + u32 event_size; + /* u8 event[]; */ +} tpm12_event_t; + /* Event log headers */ typedef struct __packed { @@ -86,48 +129,38 @@ typedef struct __packed { tpm12_event_log_header hdr; /* AKA u8 vendor_info[]; */ } tpm12_spec_id_ev_t; -typedef struct __packed { - u32 number_of_algorithms; - /* Hardcode table size so we can use sizeof */ - struct { - u16 id; - u16 size; - } digest_sizes[HASH_COUNT]; -} tpm20_digest_sizes_t; - -typedef struct __packed { - common_spec_id_ev_t c; - tpm20_digest_sizes_t sizes; - u8 vendor_info_size; - txt_event_log_pointer2_1_element el; /* AKA u8 vendor_info[]; */ -} tpm20_spec_id_ev_t; +/* + * The TPM 2.0 header is common_spec_id_ev_t, the number of algorithms, + * an id and size pair per bank, and the vendor info with its size. Its + * size depends on the number of banks. + */ +static u32 tpm20_header_size(void) +{ + return sizeof(common_spec_id_ev_t) + sizeof(u32) + nbanks * sizeof(banks[0]) + + 1 + sizeof(txt_event_log_pointer2_1_element); +} -/* Event log entries */ +/* The vendor info of the TPM 2.0 header in the log */ +static txt_event_log_pointer2_1_element *tpm20_el(void) +{ + return (txt_event_log_pointer2_1_element *) + (evtlog_base + sizeof(tpm12_event_t) + tpm20_header_size() + - sizeof(txt_event_log_pointer2_1_element)); +} -typedef struct __packed { - u32 pcr; - u32 event_type; - u8 digest[SHA1_DIGEST_SIZE]; - u32 event_size; - /* u8 event[]; */ -} tpm12_event_t; +/* + * A TPM 2.0 record is the PCR, the type, the digest count, an id and + * digest per bank, the event size and the event: TCG_PCR_EVENT2. + */ +static u32 tpm20_record_size(u32 event_size) +{ + u32 i, size = 3 * sizeof(u32) + sizeof(u32) + event_size; -/* The same as TPML_DIGEST_VALUES but little endian, as event log expects it */ -typedef struct __packed { - u32 count; - u16 sha1_id; - u8 sha1_hash[SHA1_DIGEST_SIZE]; - u16 sha256_id; - u8 sha256_hash[SHA256_DIGEST_SIZE]; -} ev_log_hash_t; + for ( i = 0; i < nbanks; i++ ) + size += sizeof(u16) + banks[i].size; -typedef struct __packed { - u32 pcr; - u32 event_type; - ev_log_hash_t digests; - u32 event_size; - /* u8 event[]; */ -} tpm20_event_t; + return size; +} static const tpm12_spec_id_ev_t tpm12_id_struct = { .c.signature = "Spec ID Event00", @@ -150,20 +183,12 @@ static const tpm12_spec_id_ev_t tpm12_id_struct = { .hdr.next_event_offset = sizeof(tpm12_event_log_header) }; -static const tpm20_spec_id_ev_t tpm20_id_struct = { - .c.signature = "Spec ID Event03", - .c.spec_ver_minor = 0, - .c.spec_ver_major = 2, - .c.errata = 0, - .c.uintn_size = 2, - .sizes.number_of_algorithms = HASH_COUNT, - .sizes.digest_sizes[0].id = TPM_ALG_SHA1, - .sizes.digest_sizes[0].size = 20, - .sizes.digest_sizes[1].id = TPM_ALG_SHA256, - .sizes.digest_sizes[1].size = 32, - .vendor_info_size = sizeof(txt_event_log_pointer2_1_element), - .el.first_record_offset = 0, - .el.next_record_offset = sizeof(tpm20_spec_id_ev_t) + sizeof(tpm12_event_t) +static const common_spec_id_ev_t tpm20_id_common = { + .signature = "Spec ID Event03", + .spec_ver_minor = 0, + .spec_ver_major = 2, + .errata = 0, + .uintn_size = 2, }; int log_event_tpm12(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], char *event) @@ -187,30 +212,198 @@ int log_event_tpm12(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], char *event) return 1; } +/* + * Writes a TPM 2.0 record with digests[i] in bank i, or the placeholder + * where that is NULL. Returns non-zero when it does not fit. + */ +static int write_event_tpm20(u32 pcr, u32 type, const u8 *const digests[], + const void *event, u32 event_size) +{ + u32 i, head[3] = { pcr, type, nbanks }; + + if ( !HAS_ENOUGH_SPACE(tpm20_record_size(event_size)) ) + return 1; + + log_write(head, sizeof(head)); + for ( i = 0; i < nbanks; i++ ) + { + log_write(&banks[i].alg, sizeof(banks[i].alg)); + log_write(digests[i] ? digests[i] : one_digest, banks[i].size); + } + log_write(&event_size, sizeof(event_size)); + log_write(event, event_size); + tpm20_el()->next_record_offset += tpm20_record_size(event_size); + + return 0; +} + int log_event_tpm20(u32 pcr, u8 sha1[SHA1_DIGEST_SIZE], u8 sha256[SHA256_DIGEST_SIZE], char *event) { - tpm20_event_t ev; - tpm20_spec_id_ev_t *base = (tpm20_spec_id_ev_t *) - (evtlog_base + sizeof(tpm12_event_t)); + const u8 *digests[MAX_BANKS] = { NULL }; + u32 i; - ev.event_size = strlen(event); + for ( i = 0; i < nbanks; i++ ) + digests[i] = banks[i].alg == TPM_ALG_SHA1 ? sha1 + : banks[i].alg == TPM_ALG_SHA256 ? sha256 : NULL; - if ( HAS_ENOUGH_SPACE(sizeof(ev) + ev.event_size) ) + return write_event_tpm20(pcr, EV_TYPE_SLAUNCH, digests, event, + strlen(event)); +} + +void event_log_extend_placeholders(struct tpm *tpm, u32 pcr) +{ + u32 i; + + for ( i = 0; i < nbanks; i++ ) + if ( banks[i].alg != TPM_ALG_SHA1 && banks[i].alg != TPM_ALG_SHA256 ) + tpm_extend_pcr(tpm, pcr, banks[i].alg, (u8 *)one_digest); +} + +/* + * Appends the records of another TCG2 log to this one, bar those of + * skip_type, each rewritten to this log's banks: a digest the record + * carries for a bank is kept, a bank it lacks gets the placeholder and + * the PCR is extended with it at this locality, so the log replays + * there too. Digests of banks this log lacks are dropped. A record that + * does not parse or fit stops the copy with what was copied left in + * place. Returns non-zero when the copy stopped short. + */ +int event_log_append_tcg2(struct tpm *tpm, const void *log, u32 size, + u32 skip_type) +{ + const u8 *p = log; + const u8 *end = p + size; + const tpm12_event_t *hdr = log; + const common_spec_id_ev_t *id; + const u16 *algs; + u32 nalgs, i; + + if ( tpm->family != TPM20 || limit == ptr_current ) + return 1; + + /* The header event, EV_NO_ACTION in PCR 0 carrying Spec ID Event03 */ + if ( size < sizeof(*hdr) + sizeof(*id) + sizeof(u32) + || hdr->pcr != 0 + || hdr->event_type != EV_NO_ACTION + || hdr->event_size < sizeof(*id) + sizeof(u32) + || hdr->event_size > size - sizeof(*hdr) ) + return 1; + + id = (const common_spec_id_ev_t *)(hdr + 1); + for ( i = 0; i < sizeof(id->signature); i++ ) + if ( id->signature[i] != tpm20_id_common.signature[i] ) + return 1; + + /* The algorithms of the log's records, id and size pairs, size its digests */ + nalgs = *(const u32 *)(id + 1); + algs = (const u16 *)((const u32 *)(id + 1) + 1); + if ( nalgs == 0 || nalgs > (hdr->event_size - sizeof(*id) - 4) / 4 ) + return 1; + + p += sizeof(*hdr) + hdr->event_size; + + while ( p + 3 * sizeof(u32) <= end ) { - ev.pcr = pcr; - ev.event_type = EV_TYPE_SLAUNCH; - ev.digests.count = 2; - ev.digests.sha1_id = TPM_ALG_SHA1; - memcpy(ev.digests.sha1_hash, sha1, 20); - ev.digests.sha256_id = TPM_ALG_SHA256; - memcpy(ev.digests.sha256_hash, sha256, 32); - base->el.next_record_offset += sizeof(ev) + ev.event_size; - log_write(&ev, sizeof(ev)); - return log_write(event, ev.event_size); + const u8 *digests[MAX_BANKS] = { NULL }; + u32 pcr, type, count, len, j; + + pcr = *(const u32 *)p; + type = *(const u32 *)(p + 4); + count = *(const u32 *)(p + 8); + if ( count == 0 ) + break; + p += 3 * sizeof(u32); + + for ( i = 0; i < count; i++ ) + { + u16 alg, dsize = 0; + + if ( p + sizeof(u16) > end ) + return 1; + alg = *(const u16 *)p; + for ( j = 0; j < nalgs; j++ ) + if ( algs[2 * j] == alg ) + dsize = algs[2 * j + 1]; + if ( dsize == 0 || dsize > end - p - sizeof(u16) ) + return 1; + for ( j = 0; j < nbanks; j++ ) + if ( banks[j].alg == alg && banks[j].size == dsize ) + digests[j] = p + sizeof(u16); + p += sizeof(u16) + dsize; + } + + if ( p + sizeof(u32) > end ) + return 1; + len = *(const u32 *)p; + p += sizeof(u32); + if ( len > end - p ) + return 1; + + if ( type != skip_type ) + { + if ( write_event_tpm20(pcr, type, digests, p, len) ) + return 1; + for ( j = 0; j < nbanks; j++ ) + if ( digests[j] == NULL ) + tpm_extend_pcr(tpm, pcr, banks[j].alg, (u8 *)one_digest); + } + p += len; } - return 1; + return 0; +} + +/* + * Asks the TPM 2.0 for its allocated banks, leaving out algorithms the SKL + * does not know. If the query fails, or finds no bank the SKL knows, the + * log declares SHA-1 and SHA-256, the banks most discrete TPMs have + * allocated by default. The query fails behind a locality the SKL does + * not hold, and on every CRB TPM, because crb_recv() does not read the + * reply. + */ +static void find_banks(struct tpm *tpm) +{ + u16 algs[MAX_BANKS]; + u32 n = MAX_BANKS, i; + int rc; + + nbanks = 0; + rc = tpm_get_pcr_banks(tpm, algs, &n); + if ( rc == 0 ) + { + for ( i = 0; i < n; i++ ) + { + if ( digest_size(algs[i]) == 0 ) + continue; + banks[nbanks].alg = algs[i]; + banks[nbanks].size = digest_size(algs[i]); + nbanks++; + } + } + else + { + print("Bank query failed: "); + print_u64((u32)rc); + print("\n"); + } + + if ( nbanks == 0 ) + { + banks[0].alg = TPM_ALG_SHA1; + banks[0].size = SHA1_SIZE; + banks[1].alg = TPM_ALG_SHA256; + banks[1].size = SHA256_SIZE; + nbanks = 2; + } + + print("Event log banks:"); + for ( i = 0; i < nbanks; i++ ) + { + print(" "); + print_u64(banks[i].alg); + } + print("\n"); } int event_log_init(struct tpm *tpm) @@ -233,8 +426,9 @@ int event_log_init(struct tpm *tpm) } else if ( tpm->family == TPM20 ) { - min_size += sizeof(tpm20_id_struct); - min_size += 2 * sizeof(tpm20_event_t); /* SKL and kernel hashes */ + find_banks(tpm); + min_size += tpm20_header_size(); + min_size += 2 * tpm20_record_size(0); /* SKL and kernel hashes */ } else { @@ -277,7 +471,7 @@ int event_log_init(struct tpm *tpm) if ( tpm->family == TPM12 ) ev.event_size = sizeof(tpm12_id_struct); else - ev.event_size = sizeof(tpm20_id_struct); + ev.event_size = tpm20_header_size(); log_write(&ev, sizeof(ev)); } @@ -288,10 +482,19 @@ int event_log_init(struct tpm *tpm) log_write(&tpm12_id_struct, sizeof(tpm12_id_struct)); id->hdr.container_size = info->size; } else { - tpm20_spec_id_ev_t *id = (tpm20_spec_id_ev_t *)ptr_current; - log_write(&tpm20_id_struct, sizeof(tpm20_id_struct)); - id->el.allocated_event_container_size = info->size; - id->el.phys_addr = _u(evtlog_base); + u8 vendor_info_size = sizeof(txt_event_log_pointer2_1_element); + txt_event_log_pointer2_1_element el = { + .phys_addr = _u(evtlog_base), + .allocated_event_container_size = info->size, + .first_record_offset = 0, + .next_record_offset = sizeof(tpm12_event_t) + tpm20_header_size(), + }; + + log_write(&tpm20_id_common, sizeof(tpm20_id_common)); + log_write(&nbanks, sizeof(nbanks)); + log_write(banks, nbanks * sizeof(banks[0])); + log_write(&vendor_info_size, sizeof(vendor_info_size)); + log_write(&el, sizeof(el)); } /* Log what was done by SKINIT */ @@ -305,6 +508,10 @@ int event_log_init(struct tpm *tpm) u8 sha256_hash[SHA256_DIGEST_SIZE]; sha256sum(sha256_hash, _start, _end_of_measured - _start); + /* + * SKINIT extended PCR 17 with the real digest in every bank, so + * this record does not replay in a bank that gets the placeholder. + */ return log_event_tpm20(17, hash, sha256_hash, "SKINIT"); } diff --git a/head.S b/head.S index 70d6e60..3a4847f 100644 --- a/head.S +++ b/head.S @@ -48,17 +48,20 @@ GLOBAL(sl_header) .long 0xffffffff /* Reserved */ .long _end_of_signed + 0x640 /* Total Length including signature and pubkey raw data */ .word soc_flag /* Offset to SOCFLAG Structure, see below */ - .word skl_info /* Offset to SKL info with UUID and version */ - .word bootloader_data /* Offset to SLRT filled by the bootloader */ soc_flag: /* * This structure isn't described in the spec, but it is assumed to be a * number of entries, followed by family and model of supported CPUs, in * form of EAX of CPUID leaf 1 with stepping set to 0. + * + * The structure has a fixed size of eight entries: GRUB and boot.h both + * locate skl_info and bootloader_data at offsets 0x42 and 0x44, right + * after it. Family 1Ah model 00h (EPYC 9005) takes the slot of family + * 17h model 60h. */ .long 0x00000008 - .long 0x00860f00 + .long 0x00b00f00 .long 0x00a50f00 .long 0x00a40f00 .long 0x00a70f00 @@ -66,6 +69,8 @@ soc_flag: .long 0x00a10f00 .long 0x00a80f00 .long 0x00aa0f00 + .word skl_info /* Offset to SKL info with UUID and version */ + .word bootloader_data /* Offset to SLRT filled by the bootloader */ ENDDATA(sl_header) .text @@ -359,7 +364,7 @@ ENDDATA(gdt) #ifdef __x86_64__ /* 64bit Pagetables, identity map of the first 4G of RAM. */ - .section .bss.page_data, "aw", @nobits + .section .bss..page_data, "aw", @nobits .align PAGE_SIZE l1_identmap: /* 1x L1 page, mapping 2M of RAM. */ diff --git a/include/defs.h b/include/defs.h index 3a614d2..fd6d2fb 100644 --- a/include/defs.h +++ b/include/defs.h @@ -64,7 +64,7 @@ /* Due to the 64k total size limit, group all page aligned data together. */ #define __page_data \ - __attribute__ ((__section__(".bss.page_data"), __aligned__(PAGE_SIZE))) + __attribute__ ((__section__(".bss..page_data"), __aligned__(PAGE_SIZE))) #define unreachable() __builtin_unreachable() #define offsetof(a, b) __builtin_offsetof(a, b) diff --git a/include/event_log.h b/include/event_log.h index 2a75f9a..bea7892 100644 --- a/include/event_log.h +++ b/include/event_log.h @@ -22,4 +22,13 @@ int event_log_init(struct tpm *tpm); int log_event_tpm12(u32 pcr, u8 sha1[20], char *event); int log_event_tpm20(u32 pcr, u8 sha1[20], u8 sha256[32], char *event); +/* + * Extends the placeholder digest into the banks of the PCR that the SKL + * cannot hash, so that those banks replay. + */ +void event_log_extend_placeholders(struct tpm *tpm, u32 pcr); + +int event_log_append_tcg2(struct tpm *tpm, const void *log, u32 size, + u32 skip_type); + #endif /* __EVENT_LOG_H__ */ diff --git a/include/psp.h b/include/psp.h index f6d2b6d..8ce6e83 100644 --- a/include/psp.h +++ b/include/psp.h @@ -91,6 +91,11 @@ bool drtm_launch(void); bool drtm_get_cap(void); +bool drtm_get_tcg_logs(u64 *addr, u64 *size); + +/* The first record of the PSP's TCG log: SKINIT's measurement of the SLB */ +#define DRTM_TCG_EV_SL_IMAGE 0x8001 + #endif /* AMDSL */ #endif /* __SKINIT_PSP_H__ */ diff --git a/link.lds b/link.lds index af49c46..4838f9d 100644 --- a/link.lds +++ b/link.lds @@ -29,21 +29,23 @@ SECTIONS *(.headers) *(.text*) } - .rodata : { + .rodata : ALIGN(16) { *(SORT_BY_ALIGNMENT(.rodata*)) /* * This is unwritten (as of AMD APM revision 3.42) behavior of SKINIT. * PCR 17 is extended with SKL padded with zeros up to a size that is - * aligned up to the multiply of 4B. To keep functions in util.sh - * simple, _end_of_measured is explicitly aligned. This may increase - * skl.bin size by up to 3 bytes, but it doesn't increase the real size - * of data sent to TPM to be measured. + * aligned up to the multiply of 4B. AMD's SKL signing tools round the + * measured length up to a multiple of 16B and place the signature + * header at that offset, so _end_of_measured is explicitly aligned to + * 16B. This keeps functions in util.sh simple and gives the PSP the + * layout it expects, at the cost of up to 15 bytes of zero padding + * that SKINIT measures along with the code. * * Note that this padding must be part of .rodata to be included in * skl.bin, both for 'dd' used in util.sh and for checks performed by * GRUB. */ - . = ALIGN(4); + . = ALIGN(16); } _end_of_measured = .; @@ -66,9 +68,18 @@ SECTIONS *(.skl_pubkey_modulus) } - .bss : { - . = ALIGN(4096); + /* + * The small variables come first, in a section of their own, so they + * fill the space that the page alignment of .bss would otherwise waste. + * Page aligned data is in .bss..page_data, which the second pattern + * leaves out. + */ + .bss_small : { _bss = .; + *(SORT_BY_ALIGNMENT(.bss) SORT_BY_ALIGNMENT(.bss.[!.]*)) + } + + .bss : { /* * Due to the 64k total size constraint, we link all page size/aligned * data together in a single section, to avoid wasting space in the @@ -111,17 +122,28 @@ SECTIONS *(.data*) } + /* + * Toolchain notes (e.g. .note.gnu.property) are orphan sections that ld + * would otherwise place right after .rodata, between the measured region + * and the signature header. + */ /DISCARD/ : { *(.eh_frame*) + *(.note.*) } } ASSERT(_end <= 0x10000, "Landing Zone exceeds 64k"); +/* GRUB puts a 4 KiB SLRT at bootloader_data and refuses an SKL without room. */ +ASSERT(bootloader_data <= 0x10000 - 0x1000, "no room for a 4 KiB SLRT"); ASSERT(SIZEOF(.got) == 0, ".got section not empty - non-hidden symbols used?"); ASSERT(SIZEOF(.data) == 0, ".data section not empty - initialized data used?"); +ASSERT((_end_of_measured & 0xf) == 0, "_end_of_measured not aligned to 16 bytes"); ASSERT(!SIZEOF(.skl_sig_hdr) || SIZEOF(.skl_sig_hdr) == 0x100, ".skl_sig_hdr size not 256 bytes"); +ASSERT(!SIZEOF(.skl_sig_hdr) || ADDR(.skl_sig_hdr) == _end_of_measured, + ".skl_sig_hdr does not directly follow the measured region"); ASSERT(!SIZEOF(.skl_pubkey_hdr) || SIZEOF(.skl_pubkey_hdr) == 0x240, ".skl_pubkey_hdr size not 0x240 bytes"); diff --git a/main.c b/main.c index 9425d7a..71b5761 100644 --- a/main.c +++ b/main.c @@ -61,6 +61,7 @@ static void extend_pcr(struct tpm *tpm, void *data, u32 size, u32 pcr, char *ev) print("shasum calculated:\n"); hexdump(sha256_hash, SHA256_DIGEST_SIZE); tpm_extend_pcr(tpm, pcr, TPM_ALG_SHA256, &sha256_hash[0]); + event_log_extend_placeholders(tpm, pcr); log_event_tpm20(pcr, hash, sha256_hash, ev); } @@ -258,14 +259,12 @@ static asm_return_t amdsl_launch() struct slr_entry_amd_info *amd_info; struct tpm *tpm; asm_return_t ret; + u64 log_addr, log_size; print("Enter amdsl_launch()\n"); tpm = enable_tpm(); tpm_request_locality(tpm, 2); - event_log_init(tpm); - - print("TPM enabled and logging initialized\n"); dl_info = next_entry_with_tag(NULL, SLR_ENTRY_DL_INFO); amd_info = next_entry_with_tag(NULL, SLR_ENTRY_AMD_INFO); @@ -293,16 +292,50 @@ static asm_return_t amdsl_launch() print("AMDSL: PSP NOT found\n"); } + /* + * The DRTM service opens locality 3 to read and extend PCR 17. TIS grants + * a higher locality only when no lower one is active, so the host holds + * no locality while the command runs. Locality 2 is locked by the DRTM + * service until the launch succeeds, so it is requested afterwards. + */ + tpm_relinquish_locality(tpm); + if (!drtm_launch()) { print("AMDSL: skl_linux: DRTM launch failed\n"); } else { print("AMDSL: skl_linux: DRTM launch successful\n"); } + tpm_request_locality(tpm, 2); + + /* + * The log declares the TPM's banks, which the TPM tells at a locality + * the DRTM service unlocked with the launch, so the log starts here. + */ + event_log_init(tpm); + if (!drtm_extend_ossl_digest((u64)dl_info->dlme_base, dl_info->dlme_size)) { print("DRTM: skl_linux: failed to extend OSSL digest\n"); } + /* + * The PSP's extends of PCR 17 and 18, at the launch and just now, are + * in a log of its own. Append its records to ours, so the log the + * DLME gets replays to the PCRs. Its first record is SKINIT's + * measurement, which event_log_init() already logged. The banks the + * service did not log get the placeholder extends, at locality 2. + */ + if (!drtm_get_tcg_logs(&log_addr, &log_size)) { + print("DRTM: skl_linux: failed to get the TCG logs\n"); + } else if (log_addr + log_size > 0xffffffffULL) { + print("DRTM: skl_linux: the TCG logs are above 4 GiB, not appended\n"); + } else if (event_log_append_tcg2(tpm, _p((u32)log_addr), (u32)log_size, + DRTM_TCG_EV_SL_IMAGE)) { + print("DRTM: skl_linux: failed to append the TCG logs\n"); + } else { + print("DRTM: skl_linux: the TCG logs are appended to the event log\n"); + } + ret.dlme_entry = _p(dl_info->dlme_base + dl_info->dlme_entry); ret.dlme_arg = _p(amd_info->boot_params_base); diff --git a/pci.c b/pci.c index 0b33bf2..4d33e79 100644 --- a/pci.c +++ b/pci.c @@ -23,6 +23,7 @@ #include #include #include +#include int (*pci_read)(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value); @@ -193,20 +194,41 @@ u32 pci_locate(unsigned int bus, unsigned int devfn) return pci_cap_ptr; } +/* MSR C001_0058, MMIO configuration space base address (AMD64 APM vol. 2). */ +#define MSR_MMIO_CFG_BASE_ADDR 0xc0010058 +#define MMIO_CFG_BASE_ENABLE (1ULL << 0) +#define MMIO_CFG_BASE_BUS_RANGE(v) (((v) >> 2) & 0xf) +#define MMIO_CFG_BASE_ADDR_MASK 0x000ffffffff00000ULL /* bits 51:20 */ + void pci_init(void) { u32 eax, edx; - - asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(0xc0010058)); - - if ( eax & 1 ) /* MMIO configuration space is enabled */ + u64 msr, base, size; + + asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(MSR_MMIO_CFG_BASE_ADDR)); + msr = ((u64)edx << 32) | eax; + + base = msr & MMIO_CFG_BASE_ADDR_MASK; + /* One 1 MiB segment per bus, 2^BusRange buses. */ + size = 1ULL << (20 + MMIO_CFG_BASE_BUS_RANGE(msr)); + + /* + * SKL can address only the first 4 GiB of physical memory: the 32-bit + * build runs without paging and the 64-bit build identity-maps 4 GiB. + * Use type 1 accesses when the MMIO configuration space lies outside + * that range. + */ + if ( (msr & MMIO_CFG_BASE_ENABLE) && base + size <= 0x100000000ULL ) { - mmio_base_addr = (eax & 0xfff00000); + mmio_base_addr = (u32)base; pci_read = &pci_mmio_read; pci_write = &pci_mmio_write; } else { + if ( msr & MMIO_CFG_BASE_ENABLE ) + print("PCI: MMIO configuration space above 4G, using type 1 accesses\n"); + pci_read = &pci_conf1_read; pci_write = &pci_conf1_write; } diff --git a/psp.c b/psp.c index 8cefa9e..139b3a6 100644 --- a/psp.c +++ b/psp.c @@ -54,7 +54,8 @@ typedef enum { PSP_NONE = 0, PSP_V1, PSP_V2, - PSP_V3 + PSP_V3, + ASP, } psp_version_t; struct pci_psp_device { @@ -71,7 +72,8 @@ const struct pci_psp_device psp_devs_list[] = { {0x1022, 0x15DF, PSP_V3}, {0x1022, 0x1649, PSP_V2}, {0x1022, 0x14CA, PSP_V3}, - {0x1022, 0x15C7, PSP_NONE} + {0x1022, 0x15C7, PSP_V3}, + {0x1022, 0x156E, ASP}, }; static bool init_drtm_interface(u64 base_addr, psp_version_t psp_version); @@ -226,6 +228,7 @@ static bool init_drtm_interface(u64 base_addr, psp_version_t psp_version) switch(psp_version) { case PSP_V3: case PSP_V2: + case ASP: g_psp_drtm.c2pmsg_72 = (volatile u32 *)(u32)(base + 0x10a20); g_psp_drtm.c2pmsg_93 = (volatile u32 *)(u32)(base + 0x10a74); g_psp_drtm.c2pmsg_94 = (volatile u32 *)(u32)(base + 0x10a78); @@ -243,9 +246,12 @@ static bool init_drtm_interface(u64 base_addr, psp_version_t psp_version) return true; } -static bool drtm_wait_for_psp_ready(u32 *status) +/* 100 ms per retry. Hashing a multi-megabyte DLME takes the PSP seconds. */ +#define DRTM_WAIT_RETRIES 50 +#define DRTM_EXTEND_WAIT_RETRIES 1200 + +static bool drtm_wait_for_psp_ready_retries(u32 *status, int retry) { - int retry = 50; u32 reg_val = 0; while (--retry) { @@ -270,6 +276,11 @@ static bool drtm_wait_for_psp_ready(u32 *status) return true; } +static bool drtm_wait_for_psp_ready(u32 *status) +{ + return drtm_wait_for_psp_ready_retries(status, DRTM_WAIT_RETRIES); +} + #ifndef UINT32_MAX #define UINT32_MAX 0xFFFFFFFF #endif @@ -310,7 +321,7 @@ bool drtm_extend_ossl_digest(u64 addr, u64 size) *g_psp_drtm.c2pmsg_95 = (u32)(reloc_addr >> 32); *g_psp_drtm.c2pmsg_72 = (DRTM_CMD_EXTEND_OSSL_DIGEST << DRTM_MBOX_CMD_SHIFT); - if (!drtm_wait_for_psp_ready(&status)) { + if (!drtm_wait_for_psp_ready_retries(&status, DRTM_EXTEND_WAIT_RETRIES)) { print("DRTM: drtm_extend_ossl_digest: command failed to complete\n"); return false; } @@ -393,4 +404,73 @@ bool drtm_get_cap(void) return true; } +/* + * One command without arguments, its status and c2pmsg_93 to _95 as the + * PSP left them printed, and true on DRTM_NO_ERROR. + */ +static bool drtm_command_no_args(const char *name, u32 cmd) +{ + u32 status = 0; + + if (!(*g_psp_drtm.c2pmsg_72 & DRTM_MBOX_READY_MASK)) { + print("DRTM: "); + print(name); + print(": PSP not ready\n"); + return false; + } + + *g_psp_drtm.c2pmsg_93 = 0; + *g_psp_drtm.c2pmsg_94 = 0; + *g_psp_drtm.c2pmsg_95 = 0; + *g_psp_drtm.c2pmsg_72 = (cmd << DRTM_MBOX_CMD_SHIFT); + if (!drtm_wait_for_psp_ready(&status)) { + print("DRTM: "); + print(name); + print(": command failed to complete\n"); + return false; + } + + print("DRTM: "); + print(name); + print(": status "); + drtm_print_status(status); + print(", c2pmsg_93 = "); + print_u64(*g_psp_drtm.c2pmsg_93); + print(", c2pmsg_94 = "); + print_u64(*g_psp_drtm.c2pmsg_94); + print(", c2pmsg_95 = "); + print_u64(*g_psp_drtm.c2pmsg_95); + print("\n"); + + return status == 0; +} + +/* + * The log of the PSP's extends, as DRTM_CMD_GET_TCG_LOGS names it: the + * size in c2pmsg_93, the address in _94 and _95, as + * DRTM_CMD_EXTEND_OSSL_DIGEST takes one. The guide places + * DRTM_CMD_ALLOCATE_SHARED_MEMORY before the command, so a failed first + * attempt is retried once after it. + */ +bool drtm_get_tcg_logs(u64 *addr, u64 *size) +{ + print("DRTM: drtm_get_tcg_logs: Entering\n"); + + if (!drtm_command_no_args("drtm_get_tcg_logs", DRTM_CMD_GET_TCG_LOGS)) { + print("DRTM: drtm_get_tcg_logs: retrying after " + "DRTM_CMD_ALLOCATE_SHARED_MEMORY\n"); + drtm_command_no_args("drtm_allocate_shared_memory", + DRTM_CMD_ALLOCATE_SHARED_MEMORY); + if (!drtm_command_no_args("drtm_get_tcg_logs", + DRTM_CMD_GET_TCG_LOGS)) { + return false; + } + } + + *size = *g_psp_drtm.c2pmsg_93; + *addr = *g_psp_drtm.c2pmsg_94 | ((u64)*g_psp_drtm.c2pmsg_95 << 32); + + return true; +} + #endif /* AMDSL */ diff --git a/sanity_check.sh b/sanity_check.sh index 2697005..f993417 100755 --- a/sanity_check.sh +++ b/sanity_check.sh @@ -1,7 +1,7 @@ #!/bin/bash . util.sh -SKL_INFO=`hexdump "$SLB_FILE" -s30 -n2 -e '/2 "%u"'` +SKL_INFO=`hexdump "$SLB_FILE" -s66 -n2 -e '/2 "%u"'` if ! od --format=x8 --skip-bytes=$SKL_INFO --read-bytes=16 $SLB_FILE | grep -q "e91192048e26f178 02ccc4765bc82a83"; then echo "ERROR: SKL UUID missing or misplaced in $SLB_FILE" >&2 diff --git a/test-event_log.c b/test-event_log.c new file mode 100644 index 0000000..7ea7106 --- /dev/null +++ b/test-event_log.c @@ -0,0 +1,295 @@ +/* + * A hosted test of the TPM 2.0 event log: the header and records follow + * the TPM's banks, with the placeholder where the SKL has no digest. + */ +#include +#include +#include +#include +#include + +/* The host's C runtime owns _start, so the SKL's symbols go by other names */ +#define _start skl_start +#define _end_of_measured skl_end_of_measured + +#include "sha1sum.c" +#include "sha256.c" +#include "event_log.c" + +#ifdef DEBUG +void print(const char *unused) { } +void print_u64(u64 unused) { } +#endif + +/* An SLB-sized image whose first 16 bytes are what SKINIT measured */ +const char skl_start[SLB_SIZE] = "SKINIT measured"; +__asm__(".globl skl_end_of_measured\n.set skl_end_of_measured, skl_start + 16"); + +/* + * The SLRT that gives the SKL its log region: one log info entry, then + * the end tag. Its flexible array is initialised with room for both. + */ +#define SLRT_ENTRIES \ + ((sizeof(struct slr_entry_log_info) + sizeof(struct slr_entry_hdr)) / \ + sizeof(struct slr_entry_hdr)) + +struct slr_table bootloader_data = { + .entries = { [SLRT_ENTRIES - 1] = { 0 } }, +}; +static struct slr_entry_log_info *const slrt_log = + (struct slr_entry_log_info *)bootloader_data.entries; +static struct slr_entry_hdr *const slrt_end = + (struct slr_entry_hdr *)bootloader_data.entries + SLRT_ENTRIES - 1; + +static u8 log_region[4096]; + +/* What the TPM answers for its banks */ +static u16 tpm_algs[MAX_BANKS]; +static u32 tpm_nalgs; +static int tpm_banks_rc; + +int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + if ( tpm_banks_rc ) + return tpm_banks_rc; + memcpy(algs, tpm_algs, tpm_nalgs * sizeof(u16)); + *count = tpm_nalgs; + return 0; +} + +static void tpm_has(u32 n, ...) +{ + va_list ap; + u32 i; + + va_start(ap, n); + for ( i = 0; i < n; i++ ) + tpm_algs[i] = va_arg(ap, int); + va_end(ap); + tpm_nalgs = n; + tpm_banks_rc = 0; +} + +/* The extends made: (pcr, alg, whether the placeholder) */ +static struct { + u32 pcr; + u16 alg; + bool placeholder; +} extends[32]; +static unsigned nextends; + +static bool is_placeholder(const u8 *digest, u32 size) +{ + u32 i; + + if ( digest[0] != 1 ) + return false; + for ( i = 1; i < size; i++ ) + if ( digest[i] != 0 ) + return false; + return true; +} + +int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, u8 *digest) +{ + if ( nextends < 32 ) + { + extends[nextends].pcr = pcr; + extends[nextends].alg = algo; + extends[nextends].placeholder = + is_placeholder(digest, digest_size(algo)); + } + nextends++; + return 0; +} + +static struct tpm tpm20 = { .family = TPM20 }; + +static int failures; + +#define CHECK(cond) do { \ + if ( !(cond) ) { \ + printf("%s:%d: %s\n", __FILE__, __LINE__, #cond); \ + failures++; \ + } \ +} while ( 0 ) + +static void fresh_log(u32 size) +{ + nextends = 0; + bootloader_data.size = sizeof(bootloader_data) + + SLRT_ENTRIES * sizeof(struct slr_entry_hdr); + slrt_log->hdr.tag = SLR_ENTRY_LOG_INFO; + slrt_log->hdr.size = sizeof(*slrt_log); + slrt_log->format = SLR_DRTM_TPM20_LOG; + slrt_log->size = size; + slrt_log->addr = _u(log_region); + slrt_end->tag = SLR_ENTRY_END; + slrt_end->size = sizeof(*slrt_end); + memset(log_region, 0xa5, sizeof(log_region)); + CHECK(event_log_init(&tpm20) == 0); +} + +/* The header as a reader parses it: the algorithm table and the vendor info */ +static struct { + u32 nalgs; + u16 alg[MAX_BANKS]; + u16 size[MAX_BANKS]; + txt_event_log_pointer2_1_element *el; + const u8 *records; +} hdr; + +static void parse_header(void) +{ + const u8 *p = log_region + sizeof(tpm12_event_t); + u32 i; + + CHECK(memcmp(p, "Spec ID Event03", 16) == 0); + p += sizeof(common_spec_id_ev_t); + memcpy(&hdr.nalgs, p, 4); + p += 4; + CHECK(hdr.nalgs >= 1 && hdr.nalgs <= MAX_BANKS); + for ( i = 0; i < hdr.nalgs && i < MAX_BANKS; i++ ) + { + memcpy(&hdr.alg[i], p, 2); + memcpy(&hdr.size[i], p + 2, 2); + p += 4; + } + CHECK(*p == sizeof(*hdr.el)); + hdr.el = (txt_event_log_pointer2_1_element *)(p + 1); + hdr.records = p + 1 + sizeof(*hdr.el); +} + +/* + * The records after the header, parsed as a reader would: each carries + * the header's banks in its order, and for every bank the first digest + * byte and whether it is the placeholder. + */ +struct record { + u32 pcr, type; + u8 first[MAX_BANKS]; + bool placeholder[MAX_BANKS]; +}; + +static unsigned records(struct record *out, unsigned max) +{ + const u8 *p; + unsigned n = 0; + + parse_header(); + p = hdr.records; + while ( n < max ) + { + u32 count, i, len; + + memcpy(&out[n].pcr, p, 4); + memcpy(&out[n].type, p + 4, 4); + memcpy(&count, p + 8, 4); + if ( count == 0 ) + break; + p += 12; + CHECK(count == hdr.nalgs); + for ( i = 0; i < count && i < MAX_BANKS; i++ ) + { + u16 alg; + memcpy(&alg, p, 2); + CHECK(alg == hdr.alg[i]); + out[n].first[i] = p[2]; + out[n].placeholder[i] = is_placeholder(p + 2, hdr.size[i]); + p += 2 + hdr.size[i]; + } + memcpy(&len, p, 4); + p += 4 + len; + n++; + } + return n; +} + +int main(void) +{ + u8 skinit_sha1[20], skinit_sha256[32]; + struct record recs[16]; + unsigned n; + + sha1sum(skinit_sha1, skl_start, 16); + sha256sum(skinit_sha256, skl_start, 16); + + /* A TPM with the two banks a discrete one ships with */ + tpm_has(2, TPM_ALG_SHA1, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 2 && hdr.alg[0] == TPM_ALG_SHA1 && hdr.size[0] == 20); + CHECK(hdr.alg[1] == TPM_ALG_SHA256 && hdr.size[1] == 32); + CHECK(hdr.el->phys_addr == _u(log_region)); + CHECK(hdr.el->allocated_event_container_size == sizeof(log_region)); + CHECK(hdr.el->first_record_offset == 0); + /* The header event, the header with its two banks, then SKINIT's record */ + CHECK(hdr.records == log_region + 32 + 24 + 4 + 8 + 1 + 20); + CHECK(hdr.el->next_record_offset == 32 + 24 + 4 + 8 + 1 + 20 + 72 + 6); + CHECK(n == 1); + CHECK(recs[0].pcr == 17 && recs[0].type == EV_TYPE_SLAUNCH); + CHECK(recs[0].first[0] == skinit_sha1[0] && !recs[0].placeholder[0]); + CHECK(recs[0].first[1] == skinit_sha256[0] && !recs[0].placeholder[1]); + /* Nothing to extend the placeholder into */ + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 0); + + /* A TPM with only SHA-256: one digest per record and no placeholder */ + tpm_has(1, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 1 && hdr.alg[0] == TPM_ALG_SHA256); + CHECK(hdr.records == log_region + 32 + 24 + 4 + 4 + 1 + 20); + CHECK(n == 1 && recs[0].first[0] == skinit_sha256[0]); + CHECK(!recs[0].placeholder[0]); + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 0); + + /* + * A TPM with SHA-384 as well, in its own order: the SKL's records get + * the placeholder there, which is extended. + */ + tpm_has(3, TPM_ALG_SHA256, TPM_ALG_SHA1, TPM_ALG_SHA384); + fresh_log(sizeof(log_region)); + n = records(recs, 16); + CHECK(hdr.nalgs == 3 && hdr.alg[0] == TPM_ALG_SHA256); + CHECK(hdr.alg[1] == TPM_ALG_SHA1); + CHECK(hdr.alg[2] == TPM_ALG_SHA384 && hdr.size[2] == 48); + CHECK(n == 1); + CHECK(recs[0].first[0] == skinit_sha256[0] && !recs[0].placeholder[0]); + CHECK(recs[0].first[1] == skinit_sha1[0] && !recs[0].placeholder[1]); + CHECK(recs[0].placeholder[2]); + event_log_extend_placeholders(&tpm20, 17); + CHECK(nextends == 1); + CHECK(extends[0].pcr == 17 && extends[0].alg == TPM_ALG_SHA384); + CHECK(extends[0].placeholder); + + /* A bank of an algorithm the SKL cannot size is left out */ + tpm_has(2, 0x99, TPM_ALG_SHA256); + fresh_log(sizeof(log_region)); + records(recs, 16); + CHECK(hdr.nalgs == 1 && hdr.alg[0] == TPM_ALG_SHA256); + + /* When the TPM does not answer, the log declares SHA-1 and SHA-256 */ + tpm_banks_rc = -1; + fresh_log(sizeof(log_region)); + records(recs, 16); + CHECK(hdr.nalgs == 2 && hdr.alg[0] == TPM_ALG_SHA1); + CHECK(hdr.alg[1] == TPM_ALG_SHA256); + + /* A region too small for the header and two bare records is refused */ + tpm_has(2, TPM_ALG_SHA1, TPM_ALG_SHA256); + nextends = 0; + slrt_log->size = 32 + 57 + 2 * 72 - 1; + CHECK(event_log_init(&tpm20) != 0); + slrt_log->size = 32 + 57 + 2 * 72; + CHECK(event_log_init(&tpm20) == 0); + + if ( failures ) + { + printf("%d failures\n", failures); + return 1; + } + printf("ok\n"); + return 0; +} diff --git a/tpmlib/tis.c b/tpmlib/tis.c index b405f88..63ca86c 100644 --- a/tpmlib/tis.c +++ b/tpmlib/tis.c @@ -58,6 +58,8 @@ void tis_relinquish_locality(void) u8 tis_request_locality(u8 l) { + u8 access; + if (l > TPM_MAX_LOCALITY) return TPM_NO_LOCALITY; @@ -68,8 +70,12 @@ u8 tis_request_locality(u8 l) tpm_write8(ACCESS_REQUEST_USE, ACCESS(l)); - /* wait for locality to be granted */ - if (tpm_read8(ACCESS(l)) & ACCESS_ACTIVE_LOCALITY) + /* + * A locality the chipset does not decode for the host reads as all + * ones, which has the reserved bit 6 set. Treat that as not granted. + */ + access = tpm_read8(ACCESS(l)); + if (access != 0xff && (access & ACCESS_ACTIVE_LOCALITY)) locality = l; return locality; @@ -157,25 +163,26 @@ static size_t recv_data(unsigned char *buf, size_t len) return size; } -size_t tis_recv(enum tpm_family f, struct tpmbuff *buf) +size_t tis_recv(__attribute__((unused)) enum tpm_family f, + struct tpmbuff *buf) { u32 expected; u8 *buf_ptr; struct tpm_header *hdr; + int i; if (locality > TPM_MAX_LOCALITY) return 0; - /* ensure that there is data available */ - if (!tis_data_available(locality)) { - if (f == TPM12) - tpm1_timeout_d(); - else - tpm2_timeout_d(); + /* + * Poll for the response for up to 2 s. TIMEOUT_D bounds a register + * access, and a command can take much longer to complete. + */ + for (i = 0; !tis_data_available(locality) && i < 2000; i++) + tpm_mdelay(1); - if (!tis_data_available(locality)) - return 0; - } + if (!tis_data_available(locality)) + return 0; /* read header */ hdr = (struct tpm_header *)buf->head; @@ -206,8 +213,8 @@ size_t tis_recv(enum tpm_family f, struct tpmbuff *buf) if (!tis_data_available(locality)) return 0; - /* read last byte */ - if (recv_data(buf_ptr, 1) != 1) + /* read last byte into its own slot after the bytes already received */ + if (recv_data(buf_ptr + expected - 1, 1) != 1) return 0; /* make sure we read everything */ diff --git a/tpmlib/tpm.c b/tpmlib/tpm.c index 64b3995..e4a5d91 100644 --- a/tpmlib/tpm.c +++ b/tpmlib/tpm.c @@ -152,6 +152,17 @@ int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, return ret; } +int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + if (t->buff == NULL) + return -EINVAL; + + if (t->family != TPM20) + return -EINVAL; + + return tpm2_get_pcr_banks(t, algs, count); +} + void free_tpm(struct tpm *t) { tpm_relinquish_locality(t); diff --git a/tpmlib/tpm.h b/tpmlib/tpm.h index a0b57e1..ca75a83 100644 --- a/tpmlib/tpm.h +++ b/tpmlib/tpm.h @@ -56,5 +56,6 @@ extern u8 tpm_request_locality(struct tpm *t, u8 l); extern void tpm_relinquish_locality(struct tpm *t); extern int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, u8 *digest); +extern int tpm_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count); extern void free_tpm(struct tpm *t); #endif diff --git a/tpmlib/tpm2.h b/tpmlib/tpm2.h index 96ed81c..1d1a3cd 100644 --- a/tpmlib/tpm2.h +++ b/tpmlib/tpm2.h @@ -85,4 +85,15 @@ struct tpm2_resp { int tpm2_extend_pcr(struct tpm *t, u32 pcr, struct tpml_digest_values *digests); +/* + * Reads the banks with PCRs allocated, in the TPM's order. On entry + * *count is the room in algs[], and on success it is the number of banks + * found. Returns 0 on success, or: + * - -ENOSPC when the TPM has more banks than that + * - the TPM response code when the TPM fails the command + * - -EIO when the reply is malformed + * - another negative errno when the command cannot be sent or received + */ +int tpm2_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count); + #endif diff --git a/tpmlib/tpm2_cmds.c b/tpmlib/tpm2_cmds.c index 73355af..d76adc7 100644 --- a/tpmlib/tpm2_cmds.c +++ b/tpmlib/tpm2_cmds.c @@ -82,7 +82,7 @@ static u16 convert_digest_list(struct tpml_digest_values *digests) case TPM_ALG_SM3_256: h->alg = cpu_to_be16(h->alg); h = (struct tpmt_ha *)((u8 *)h + SM3256_SIZE); - size += sizeof(u16) + SHA1_SIZE; + size += sizeof(u16) + SM3256_SIZE; break; default: return 0; @@ -158,3 +158,113 @@ int tpm2_extend_pcr(struct tpm *t, u32 pcr, out: return ret; } + +int tpm2_get_pcr_banks(struct tpm *t, u16 *algs, u32 *count) +{ + struct tpmbuff *b = t->buff; + struct tpm2_cmd cmd; + struct tpm_header *hdr; + u8 *p, *end; + u32 nsel, i, n = 0; + int ret = 0; + + if (b == NULL || algs == NULL || count == NULL) + return -EINVAL; + + ret = tpm2_alloc_cmd(b, &cmd, TPM_ST_NO_SESSIONS, + TPM_CC_GET_CAPABILITY); + if (ret < 0) + goto out; + + /* capability, property, propertyCount */ + cmd.params = (u8 *)tpmb_put(b, 3 * sizeof(u32)); + if (cmd.params == NULL) { + ret = -ENOMEM; + goto free; + } + + p = cmd.params; + *(u32 *)p = cpu_to_be32(TPM_CAP_PCRS); + p += sizeof(u32); + *(u32 *)p = cpu_to_be32(0); + p += sizeof(u32); + *(u32 *)p = cpu_to_be32(1); + + cmd.header->size = cpu_to_be32(tpmb_size(b)); + + if (t->ops.send(b) != tpmb_size(b)) { + ret = -EAGAIN; + goto free; + } + + tpmb_trim(b, tpmb_size(b)); + hdr = (struct tpm_header *)b->head; + tpmb_put(b, sizeof(struct tpm_header)); + + if (t->ops.recv(t->family, b) != tpmb_size(b)) { + ret = -EAGAIN; + goto free; + } + + if (hdr->code != 0) { + ret = (int)hdr->code; + goto free; + } + + /* + * Response: moreData, TPMS_CAPABILITY_DATA{capability, + * TPML_PCR_SELECTION{count, TPMS_PCR_SELECTION{hash, sizeofSelect, + * pcrSelect[]}[]}}. A bank with any PCR selected is allocated. + */ + p = b->head + sizeof(struct tpm_header); + end = b->head + hdr->size; + + if (end - p < 1 + sizeof(u32) + sizeof(u32)) { + ret = -EIO; + goto free; + } + p += 1; + if (be32_to_cpu(*(u32 *)p) != TPM_CAP_PCRS) { + ret = -EIO; + goto free; + } + p += sizeof(u32); + nsel = be32_to_cpu(*(u32 *)p); + p += sizeof(u32); + + for (i = 0; i < nsel; i++) { + u16 alg; + u8 size, j, selected = 0; + + if (end - p < sizeof(u16) + 1) { + ret = -EIO; + goto free; + } + alg = be16_to_cpu(*(u16 *)p); + size = p[sizeof(u16)]; + p += sizeof(u16) + 1; + if (end - p < size) { + ret = -EIO; + goto free; + } + for (j = 0; j < size; j++) + selected |= p[j]; + p += size; + + if (selected) { + if (n < *count) + algs[n] = alg; + n++; + } + } + + if (n > *count) + ret = -ENOSPC; + else + *count = n; + +free: + tpmb_free(b); +out: + return ret; +} diff --git a/tpmlib/tpm2_constants.h b/tpmlib/tpm2_constants.h index 35bdc1e..2893cde 100644 --- a/tpmlib/tpm2_constants.h +++ b/tpmlib/tpm2_constants.h @@ -58,8 +58,12 @@ #define TPM_ALG_LAST _AT(u16, 0x0044) /* Table 12 Definition of (UINT32) TPM_CC Constants (Numeric Order) */ +#define TPM_CC_GET_CAPABILITY _AT(u32, 0x0000017A) #define TPM_CC_PCR_EXTEND _AT(u32, 0x00000182) +/* Table 22 Definition of (UINT32) TPM_CAP Constants */ +#define TPM_CAP_PCRS _AT(u32, 0x00000005) + /* Table 19 Definition of (UINT16) TPM_ST Constants */ #define TPM_ST_NO_SESSIONS _AT(u16, 0x8001) #define TPM_ST_SESSIONS _AT(u16, 0x8002)