Message ID | 20200121200147.1002075-8-stefanb@linux.ibm.com |
---|---|
State | Accepted |
Headers | show |
Series | Add vTPM 2.0 support to SLOF | expand |
On Tue, Jan 21, 2020 at 03:01:46PM -0500, Stefan Berger wrote: > Implement a TPM 2 menu and enable the user to clear the TPM > and its activate PCR banks. > > The main TPM menu is activated by pressing the 't' key during > firmware startup. > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> Signed-off-by: Kevin O'Connor <kevin@koconnor.net> -Kevin > --- > board-qemu/slof/OF.fs | 3 + > board-qemu/slof/vtpm-sml.fs | 6 + > lib/libtpm/tcgbios.c | 348 ++++++++++++++++++++++++++++++++++++ > lib/libtpm/tcgbios.h | 1 + > lib/libtpm/tpm.code | 9 + > lib/libtpm/tpm.in | 1 + > slof/fs/start-up.fs | 7 + > 7 files changed, 375 insertions(+) > > diff --git a/board-qemu/slof/OF.fs b/board-qemu/slof/OF.fs > index 3e117ad..f0fc9c6 100644 > --- a/board-qemu/slof/OF.fs > +++ b/board-qemu/slof/OF.fs > @@ -175,6 +175,9 @@ CREATE version-str 10 ALLOT > version-str 8 + @ \ end > over - dump-display-write > " Press 's' to enter Open Firmware." dump-display-write > + s" /ibm,vtpm" find-node IF > + " Press 't' to enter TPM menu." dump-display-write > + THEN > cr cr > temp-ptr disp-size > IF > temp-ptr disp-size MOD > diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs > index 8e2ab6d..a51c907 100644 > --- a/board-qemu/slof/vtpm-sml.fs > +++ b/board-qemu/slof/vtpm-sml.fs > @@ -56,6 +56,12 @@ s" ibm,vtpm" 2dup device-name device-type > THEN > ; > > +: vtpm-menu > + tpm-is-working IF > + tpm20-menu > + THEN > +; > + > : open true ; > : close ; > > diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c > index d81d392..c5f8ccf 100644 > --- a/lib/libtpm/tcgbios.c > +++ b/lib/libtpm/tcgbios.c > @@ -63,6 +63,12 @@ static struct { > struct tpml_pcr_selection *tpm20_pcr_selection; > } tpm_state; > > +#define TPM2_ALG_SHA1_FLAG (1 << 0) > +#define TPM2_ALG_SHA256_FLAG (1 << 1) > +#define TPM2_ALG_SHA384_FLAG (1 << 2) > +#define TPM2_ALG_SHA512_FLAG (1 << 3) > +#define TPM2_ALG_SM3_256_FLAG (1 << 4) > + > /* > * TPM 2 logs are written in little endian format. > */ > @@ -110,23 +116,36 @@ struct tpm_log_entry { > > static const struct hash_parameters { > uint16_t hashalg; > + uint8_t hashalg_flag; > uint8_t hash_buffersize; > + const char *name; > } hash_parameters[] = { > { > .hashalg = TPM2_ALG_SHA1, > + .hashalg_flag = TPM2_ALG_SHA1_FLAG, > .hash_buffersize = SHA1_BUFSIZE, > + .name = "SHA1", > }, { > .hashalg = TPM2_ALG_SHA256, > + .hashalg_flag = TPM2_ALG_SHA256_FLAG, > .hash_buffersize = SHA256_BUFSIZE, > + .name = "SHA256", > }, { > .hashalg = TPM2_ALG_SHA384, > + .hashalg_flag = TPM2_ALG_SHA384_FLAG, > .hash_buffersize = SHA384_BUFSIZE, > + .name = "SHA384", > + > }, { > .hashalg = TPM2_ALG_SHA512, > + .hashalg_flag = TPM2_ALG_SHA512_FLAG, > .hash_buffersize = SHA512_BUFSIZE, > + .name = "SHA512", > }, { > .hashalg = TPM2_ALG_SM3_256, > + .hashalg_flag = TPM2_ALG_SM3_256_FLAG, > .hash_buffersize = SM3_256_BUFSIZE, > + .name = "SM3-256", > } > }; > > @@ -141,6 +160,18 @@ static const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg) > return NULL; > } > > +static const struct hash_parameters * > +tpm20_find_by_hashalg_flag(uint16_t hashalg_flag) > +{ > + unsigned i; > + > + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { > + if (hash_parameters[i].hashalg_flag == hashalg_flag) > + return &hash_parameters[i]; > + } > + return NULL; > +} > + > static inline int tpm20_get_hash_buffersize(uint16_t hashAlg) > { > const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg); > @@ -150,6 +181,35 @@ static inline int tpm20_get_hash_buffersize(uint16_t hashAlg) > return -1; > } > > +static inline uint8_t tpm20_hashalg_to_flag(uint16_t hashAlg) > +{ > + const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg); > + > + if (hp) > + return hp->hashalg_flag; > + return 0; > +} > + > +static uint16_t tpm20_hashalg_flag_to_hashalg(uint8_t hashalg_flag) > +{ > + const struct hash_parameters *hp; > + > + hp = tpm20_find_by_hashalg_flag(hashalg_flag); > + if (hp) > + return hp->hashalg; > + return 0; > +} > + > +static const char * tpm20_hashalg_flag_to_name(uint8_t hashalg_flag) > +{ > + const struct hash_parameters *hp; > + > + hp = tpm20_find_by_hashalg_flag(hashalg_flag); > + if (hp) > + return hp->name; > + return NULL; > +} > + > /* > * Build the TPM2 TPML_DIGEST_VALUES data structure from the given hash. > * Follow the PCR bank configuration of the TPM and write the same hash > @@ -923,3 +983,291 @@ void tpm_driver_set_failure_reason(uint32_t errcode) > > spapr_vtpm_set_error(errcode); > } > + > +/**************************************************************** > + * TPM Configuration Menu > + ****************************************************************/ > + > +static int > +tpm20_get_suppt_pcrbanks(uint8_t *suppt_pcrbanks, uint8_t *active_pcrbanks) > +{ > + struct tpms_pcr_selection *sel; > + void *end; > + > + *suppt_pcrbanks = 0; > + *active_pcrbanks = 0; > + > + sel = tpm_state.tpm20_pcr_selection->selections; > + end = (void*)tpm_state.tpm20_pcr_selection + > + tpm_state.tpm20_pcr_selection_size; > + > + while (1) { > + uint16_t hashalg; > + uint8_t hashalg_flag; > + unsigned i; > + uint8_t sizeOfSelect = sel->sizeOfSelect; > + void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; > + > + if (nsel > end) > + return 0; > + > + hashalg = be16_to_cpu(sel->hashAlg); > + hashalg_flag = tpm20_hashalg_to_flag(hashalg); > + > + *suppt_pcrbanks |= hashalg_flag; > + > + for (i = 0; i < sizeOfSelect; i++) { > + if (sel->pcrSelect[i]) { > + *active_pcrbanks |= hashalg_flag; > + break; > + } > + } > + > + sel = nsel; > + } > +} > + > +static int > +tpm20_set_pcrbanks(uint32_t active_banks) > +{ > + struct tpm2_req_pcr_allocate trpa = { > + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate), > + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), > + .authblocksize = cpu_to_be32(sizeof(trpa.authblock)), > + .authblock = { > + .handle = cpu_to_be32(TPM2_RS_PW), > + .noncesize = cpu_to_be16(0), > + .contsession = TPM2_YES, > + .pwdsize = cpu_to_be16(0), > + }, > + }; > + struct tpms_pcr_selection3 { > + uint16_t hashAlg; > + uint8_t sizeOfSelect; > + uint8_t pcrSelect[3]; > + } tps[ARRAY_SIZE(trpa.tpms_pcr_selections)]; > + int i = 0; > + uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG; > + uint8_t dontcare, suppt_banks; > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + uint16_t hashalg; > + int ret; > + > + tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare); > + > + while (hashalg_flag) { > + if ((hashalg_flag & suppt_banks)) { > + hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag); > + > + if (hashalg) { > + uint8_t mask = 0; > + > + tps[i].hashAlg = cpu_to_be16(hashalg); > + tps[i].sizeOfSelect = 3; > + > + if (active_banks & hashalg_flag) > + mask = 0xff; > + > + tps[i].pcrSelect[0] = mask; > + tps[i].pcrSelect[1] = mask; > + tps[i].pcrSelect[2] = mask; > + i++; > + } > + } > + hashalg_flag <<= 1; > + } > + > + trpa.count = cpu_to_be32(i); > + memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0])); > + trpa.hdr.totlen = cpu_to_be32(offset_of(struct tpm2_req_pcr_allocate, > + tpms_pcr_selections) + > + i * sizeof(tps[0])); > + > + ret = spapr_transmit(0, &trpa.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_SHORT); > + ret = ret ? -1 : be32_to_cpu(rsp.errcode); > + > + return ret; > +} > + > +static int tpm20_activate_pcrbanks(uint32_t active_banks) > +{ > + int ret; > + > + ret = tpm20_set_pcrbanks(active_banks); > + if (!ret) > + ret = tpm_simple_cmd(0, TPM2_CC_Shutdown, > + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); > + if (!ret) > + SLOF_reset(); > + return ret; > +} > + > +static int > +tpm20_clearcontrol(uint8_t disable) > +{ > + struct tpm2_req_clearcontrol trc = { > + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trc)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl), > + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), > + .authblocksize = cpu_to_be32(sizeof(trc.authblock)), > + .authblock = { > + .handle = cpu_to_be32(TPM2_RS_PW), > + .noncesize = cpu_to_be16(0), > + .contsession = TPM2_YES, > + .pwdsize = cpu_to_be16(0), > + }, > + .disable = disable, > + }; > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret; > + > + ret = spapr_transmit(0, &trc.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_SHORT); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + ret = -1; > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int > +tpm20_clear(void) > +{ > + struct tpm2_req_clear trq = { > + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trq)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_Clear), > + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), > + .authblocksize = cpu_to_be32(sizeof(trq.authblock)), > + .authblock = { > + .handle = cpu_to_be32(TPM2_RS_PW), > + .noncesize = cpu_to_be16(0), > + .contsession = TPM2_YES, > + .pwdsize = cpu_to_be16(0), > + }, > + }; > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret; > + > + ret = spapr_transmit(0, &trq.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_MEDIUM); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + ret = -1; > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int tpm20_menu_change_active_pcrbanks(void) > +{ > + uint8_t active_banks, suppt_banks, activate_banks; > + > + tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks); > + > + activate_banks = active_banks; > + > + while (1) { > + uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG; > + uint8_t i = 0; > + uint8_t flagnum; > + int show = 0; > + > + printf("\nToggle active PCR banks by pressing number key\n\n"); > + > + while (hashalg_flag) { > + uint8_t flag = hashalg_flag & suppt_banks; > + const char *hashname = tpm20_hashalg_flag_to_name(flag); > + > + i++; > + if (hashname) { > + printf(" %d: %s", i, hashname); > + if (activate_banks & hashalg_flag) > + printf(" (enabled)"); > + printf("\n"); > + } > + > + hashalg_flag <<= 1; > + } > + printf("\n" > + "ESC: return to previous menu without changes\n"); > + if (activate_banks) > + printf("a : activate selection\n"); > + > + while (!show) { > + int key_code = SLOF_get_keystroke(); > + > + switch (key_code) { > + case ~0: > + continue; > + case 27: /* ESC */ > + printf("\n"); > + return -1; > + case '1' ... '5': /* keys 1 .. 5 */ > + flagnum = key_code - '0'; > + if (flagnum > i) > + continue; > + if (suppt_banks & (1 << (flagnum - 1))) { > + activate_banks ^= 1 << (flagnum - 1); > + show = 1; > + } > + break; > + case 'a': /* a */ > + if (activate_banks) > + tpm20_activate_pcrbanks(activate_banks); > + } > + } > + } > +} > + > +void tpm20_menu(void) > +{ > + int key_code; > + int waitkey; > + int ret; > + > + for (;;) { > + printf("1. Clear TPM\n"); > + printf("2. Change active PCR banks\n"); > + > + printf("\nIf not change is desired or if this menu was reached by " > + "mistake, press ESC to\ncontinue the boot.\n"); > + > + waitkey = 1; > + > + while (waitkey) { > + key_code = SLOF_get_keystroke(); > + switch (key_code) { > + case 27: > + // ESC > + return; > + case '1': > + ret = tpm20_clearcontrol(false); > + if (!ret) > + ret = tpm20_clear(); > + if (ret) > + printf("An error occurred clearing " > + "the TPM: 0x%x\n", > + ret); > + break; > + case '2': > + tpm20_menu_change_active_pcrbanks(); > + waitkey = 0; > + continue; > + default: > + continue; > + } > + > + waitkey = 0; > + } > + } > +} > diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h > index e9f9c36..c4a2e71 100644 > --- a/lib/libtpm/tcgbios.h > +++ b/lib/libtpm/tcgbios.h > @@ -28,5 +28,6 @@ uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr); > uint32_t tpm_driver_get_failure_reason(void); > void tpm_driver_set_failure_reason(uint32_t errcode); > bool tpm_is_working(void); > +void tpm20_menu(void); > > #endif /* TCGBIOS_H */ > diff --git a/lib/libtpm/tpm.code b/lib/libtpm/tpm.code > index 05f4547..b8f5669 100644 > --- a/lib/libtpm/tpm.code > +++ b/lib/libtpm/tpm.code > @@ -128,3 +128,12 @@ PRIM(tpm_X2d_measure_X2d_scrtm) > PUSH; > TOS.n = tpm_measure_scrtm(); > MIRP > + > +/*******************************************************************/ > +/* Firmware API */ > +/* SLOF: tpm20-menu ( -- tpm-version ) */ > +/* LIBTPM: tpm20_menu() */ > +/*******************************************************************/ > +PRIM(tpm20_X2d_menu) > + tpm20_menu(); > +MIRP > diff --git a/lib/libtpm/tpm.in b/lib/libtpm/tpm.in > index 22713e4..590fee1 100644 > --- a/lib/libtpm/tpm.in > +++ b/lib/libtpm/tpm.in > @@ -24,3 +24,4 @@ cod(tpm-is-working) > cod(tpm-measure-scrtm) > cod(tpm-driver-get-failure-reason) > cod(tpm-driver-set-failure-reason) > +cod(tpm20-menu) > diff --git a/slof/fs/start-up.fs b/slof/fs/start-up.fs > index c1f931a..45e8926 100644 > --- a/slof/fs/start-up.fs > +++ b/slof/fs/start-up.fs > @@ -55,6 +55,12 @@ > nvramlog-write-string-cr > ; > > +: (t-pressed) ( -- ) > + s" /ibm,vtpm" find-node ?dup IF > + s" vtpm-menu" rot $call-static > + THEN > +; > + > : (boot?) ( -- ) > \ last step before we boot we give up physical presence on the TPM > s" /ibm,vtpm" find-node ?dup IF > @@ -105,6 +111,7 @@ TRUE VALUE use-load-watchdog? > key? IF > key CASE > [char] s OF (s-pressed) ENDOF > + [char] t OF (t-pressed) (boot?) ENDOF > 1b OF > (esc-sequence) CASE > 1 OF > -- > 2.24.1 >
diff --git a/board-qemu/slof/OF.fs b/board-qemu/slof/OF.fs index 3e117ad..f0fc9c6 100644 --- a/board-qemu/slof/OF.fs +++ b/board-qemu/slof/OF.fs @@ -175,6 +175,9 @@ CREATE version-str 10 ALLOT version-str 8 + @ \ end over - dump-display-write " Press 's' to enter Open Firmware." dump-display-write + s" /ibm,vtpm" find-node IF + " Press 't' to enter TPM menu." dump-display-write + THEN cr cr temp-ptr disp-size > IF temp-ptr disp-size MOD diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs index 8e2ab6d..a51c907 100644 --- a/board-qemu/slof/vtpm-sml.fs +++ b/board-qemu/slof/vtpm-sml.fs @@ -56,6 +56,12 @@ s" ibm,vtpm" 2dup device-name device-type THEN ; +: vtpm-menu + tpm-is-working IF + tpm20-menu + THEN +; + : open true ; : close ; diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c index d81d392..c5f8ccf 100644 --- a/lib/libtpm/tcgbios.c +++ b/lib/libtpm/tcgbios.c @@ -63,6 +63,12 @@ static struct { struct tpml_pcr_selection *tpm20_pcr_selection; } tpm_state; +#define TPM2_ALG_SHA1_FLAG (1 << 0) +#define TPM2_ALG_SHA256_FLAG (1 << 1) +#define TPM2_ALG_SHA384_FLAG (1 << 2) +#define TPM2_ALG_SHA512_FLAG (1 << 3) +#define TPM2_ALG_SM3_256_FLAG (1 << 4) + /* * TPM 2 logs are written in little endian format. */ @@ -110,23 +116,36 @@ struct tpm_log_entry { static const struct hash_parameters { uint16_t hashalg; + uint8_t hashalg_flag; uint8_t hash_buffersize; + const char *name; } hash_parameters[] = { { .hashalg = TPM2_ALG_SHA1, + .hashalg_flag = TPM2_ALG_SHA1_FLAG, .hash_buffersize = SHA1_BUFSIZE, + .name = "SHA1", }, { .hashalg = TPM2_ALG_SHA256, + .hashalg_flag = TPM2_ALG_SHA256_FLAG, .hash_buffersize = SHA256_BUFSIZE, + .name = "SHA256", }, { .hashalg = TPM2_ALG_SHA384, + .hashalg_flag = TPM2_ALG_SHA384_FLAG, .hash_buffersize = SHA384_BUFSIZE, + .name = "SHA384", + }, { .hashalg = TPM2_ALG_SHA512, + .hashalg_flag = TPM2_ALG_SHA512_FLAG, .hash_buffersize = SHA512_BUFSIZE, + .name = "SHA512", }, { .hashalg = TPM2_ALG_SM3_256, + .hashalg_flag = TPM2_ALG_SM3_256_FLAG, .hash_buffersize = SM3_256_BUFSIZE, + .name = "SM3-256", } }; @@ -141,6 +160,18 @@ static const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg) return NULL; } +static const struct hash_parameters * +tpm20_find_by_hashalg_flag(uint16_t hashalg_flag) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { + if (hash_parameters[i].hashalg_flag == hashalg_flag) + return &hash_parameters[i]; + } + return NULL; +} + static inline int tpm20_get_hash_buffersize(uint16_t hashAlg) { const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg); @@ -150,6 +181,35 @@ static inline int tpm20_get_hash_buffersize(uint16_t hashAlg) return -1; } +static inline uint8_t tpm20_hashalg_to_flag(uint16_t hashAlg) +{ + const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg); + + if (hp) + return hp->hashalg_flag; + return 0; +} + +static uint16_t tpm20_hashalg_flag_to_hashalg(uint8_t hashalg_flag) +{ + const struct hash_parameters *hp; + + hp = tpm20_find_by_hashalg_flag(hashalg_flag); + if (hp) + return hp->hashalg; + return 0; +} + +static const char * tpm20_hashalg_flag_to_name(uint8_t hashalg_flag) +{ + const struct hash_parameters *hp; + + hp = tpm20_find_by_hashalg_flag(hashalg_flag); + if (hp) + return hp->name; + return NULL; +} + /* * Build the TPM2 TPML_DIGEST_VALUES data structure from the given hash. * Follow the PCR bank configuration of the TPM and write the same hash @@ -923,3 +983,291 @@ void tpm_driver_set_failure_reason(uint32_t errcode) spapr_vtpm_set_error(errcode); } + +/**************************************************************** + * TPM Configuration Menu + ****************************************************************/ + +static int +tpm20_get_suppt_pcrbanks(uint8_t *suppt_pcrbanks, uint8_t *active_pcrbanks) +{ + struct tpms_pcr_selection *sel; + void *end; + + *suppt_pcrbanks = 0; + *active_pcrbanks = 0; + + sel = tpm_state.tpm20_pcr_selection->selections; + end = (void*)tpm_state.tpm20_pcr_selection + + tpm_state.tpm20_pcr_selection_size; + + while (1) { + uint16_t hashalg; + uint8_t hashalg_flag; + unsigned i; + uint8_t sizeOfSelect = sel->sizeOfSelect; + void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + + if (nsel > end) + return 0; + + hashalg = be16_to_cpu(sel->hashAlg); + hashalg_flag = tpm20_hashalg_to_flag(hashalg); + + *suppt_pcrbanks |= hashalg_flag; + + for (i = 0; i < sizeOfSelect; i++) { + if (sel->pcrSelect[i]) { + *active_pcrbanks |= hashalg_flag; + break; + } + } + + sel = nsel; + } +} + +static int +tpm20_set_pcrbanks(uint32_t active_banks) +{ + struct tpm2_req_pcr_allocate trpa = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trpa.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + }; + struct tpms_pcr_selection3 { + uint16_t hashAlg; + uint8_t sizeOfSelect; + uint8_t pcrSelect[3]; + } tps[ARRAY_SIZE(trpa.tpms_pcr_selections)]; + int i = 0; + uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG; + uint8_t dontcare, suppt_banks; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + uint16_t hashalg; + int ret; + + tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare); + + while (hashalg_flag) { + if ((hashalg_flag & suppt_banks)) { + hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag); + + if (hashalg) { + uint8_t mask = 0; + + tps[i].hashAlg = cpu_to_be16(hashalg); + tps[i].sizeOfSelect = 3; + + if (active_banks & hashalg_flag) + mask = 0xff; + + tps[i].pcrSelect[0] = mask; + tps[i].pcrSelect[1] = mask; + tps[i].pcrSelect[2] = mask; + i++; + } + } + hashalg_flag <<= 1; + } + + trpa.count = cpu_to_be32(i); + memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0])); + trpa.hdr.totlen = cpu_to_be32(offset_of(struct tpm2_req_pcr_allocate, + tpms_pcr_selections) + + i * sizeof(tps[0])); + + ret = spapr_transmit(0, &trpa.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + ret = ret ? -1 : be32_to_cpu(rsp.errcode); + + return ret; +} + +static int tpm20_activate_pcrbanks(uint32_t active_banks) +{ + int ret; + + ret = tpm20_set_pcrbanks(active_banks); + if (!ret) + ret = tpm_simple_cmd(0, TPM2_CC_Shutdown, + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); + if (!ret) + SLOF_reset(); + return ret; +} + +static int +tpm20_clearcontrol(uint8_t disable) +{ + struct tpm2_req_clearcontrol trc = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trc)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trc.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .disable = disable, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret; + + ret = spapr_transmit(0, &trc.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_clear(void) +{ + struct tpm2_req_clear trq = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trq)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_Clear), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trq.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret; + + ret = spapr_transmit(0, &trq.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_menu_change_active_pcrbanks(void) +{ + uint8_t active_banks, suppt_banks, activate_banks; + + tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks); + + activate_banks = active_banks; + + while (1) { + uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG; + uint8_t i = 0; + uint8_t flagnum; + int show = 0; + + printf("\nToggle active PCR banks by pressing number key\n\n"); + + while (hashalg_flag) { + uint8_t flag = hashalg_flag & suppt_banks; + const char *hashname = tpm20_hashalg_flag_to_name(flag); + + i++; + if (hashname) { + printf(" %d: %s", i, hashname); + if (activate_banks & hashalg_flag) + printf(" (enabled)"); + printf("\n"); + } + + hashalg_flag <<= 1; + } + printf("\n" + "ESC: return to previous menu without changes\n"); + if (activate_banks) + printf("a : activate selection\n"); + + while (!show) { + int key_code = SLOF_get_keystroke(); + + switch (key_code) { + case ~0: + continue; + case 27: /* ESC */ + printf("\n"); + return -1; + case '1' ... '5': /* keys 1 .. 5 */ + flagnum = key_code - '0'; + if (flagnum > i) + continue; + if (suppt_banks & (1 << (flagnum - 1))) { + activate_banks ^= 1 << (flagnum - 1); + show = 1; + } + break; + case 'a': /* a */ + if (activate_banks) + tpm20_activate_pcrbanks(activate_banks); + } + } + } +} + +void tpm20_menu(void) +{ + int key_code; + int waitkey; + int ret; + + for (;;) { + printf("1. Clear TPM\n"); + printf("2. Change active PCR banks\n"); + + printf("\nIf not change is desired or if this menu was reached by " + "mistake, press ESC to\ncontinue the boot.\n"); + + waitkey = 1; + + while (waitkey) { + key_code = SLOF_get_keystroke(); + switch (key_code) { + case 27: + // ESC + return; + case '1': + ret = tpm20_clearcontrol(false); + if (!ret) + ret = tpm20_clear(); + if (ret) + printf("An error occurred clearing " + "the TPM: 0x%x\n", + ret); + break; + case '2': + tpm20_menu_change_active_pcrbanks(); + waitkey = 0; + continue; + default: + continue; + } + + waitkey = 0; + } + } +} diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h index e9f9c36..c4a2e71 100644 --- a/lib/libtpm/tcgbios.h +++ b/lib/libtpm/tcgbios.h @@ -28,5 +28,6 @@ uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr); uint32_t tpm_driver_get_failure_reason(void); void tpm_driver_set_failure_reason(uint32_t errcode); bool tpm_is_working(void); +void tpm20_menu(void); #endif /* TCGBIOS_H */ diff --git a/lib/libtpm/tpm.code b/lib/libtpm/tpm.code index 05f4547..b8f5669 100644 --- a/lib/libtpm/tpm.code +++ b/lib/libtpm/tpm.code @@ -128,3 +128,12 @@ PRIM(tpm_X2d_measure_X2d_scrtm) PUSH; TOS.n = tpm_measure_scrtm(); MIRP + +/*******************************************************************/ +/* Firmware API */ +/* SLOF: tpm20-menu ( -- tpm-version ) */ +/* LIBTPM: tpm20_menu() */ +/*******************************************************************/ +PRIM(tpm20_X2d_menu) + tpm20_menu(); +MIRP diff --git a/lib/libtpm/tpm.in b/lib/libtpm/tpm.in index 22713e4..590fee1 100644 --- a/lib/libtpm/tpm.in +++ b/lib/libtpm/tpm.in @@ -24,3 +24,4 @@ cod(tpm-is-working) cod(tpm-measure-scrtm) cod(tpm-driver-get-failure-reason) cod(tpm-driver-set-failure-reason) +cod(tpm20-menu) diff --git a/slof/fs/start-up.fs b/slof/fs/start-up.fs index c1f931a..45e8926 100644 --- a/slof/fs/start-up.fs +++ b/slof/fs/start-up.fs @@ -55,6 +55,12 @@ nvramlog-write-string-cr ; +: (t-pressed) ( -- ) + s" /ibm,vtpm" find-node ?dup IF + s" vtpm-menu" rot $call-static + THEN +; + : (boot?) ( -- ) \ last step before we boot we give up physical presence on the TPM s" /ibm,vtpm" find-node ?dup IF @@ -105,6 +111,7 @@ TRUE VALUE use-load-watchdog? key? IF key CASE [char] s OF (s-pressed) ENDOF + [char] t OF (t-pressed) (boot?) ENDOF 1b OF (esc-sequence) CASE 1 OF
Implement a TPM 2 menu and enable the user to clear the TPM and its activate PCR banks. The main TPM menu is activated by pressing the 't' key during firmware startup. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- board-qemu/slof/OF.fs | 3 + board-qemu/slof/vtpm-sml.fs | 6 + lib/libtpm/tcgbios.c | 348 ++++++++++++++++++++++++++++++++++++ lib/libtpm/tcgbios.h | 1 + lib/libtpm/tpm.code | 9 + lib/libtpm/tpm.in | 1 + slof/fs/start-up.fs | 7 + 7 files changed, 375 insertions(+)