diff mbox series

[v7,7/8] tcgbios: Implement menu to clear TPM 2 and activate its PCR banks

Message ID 20200121200147.1002075-8-stefanb@linux.ibm.com
State Accepted
Headers show
Series Add vTPM 2.0 support to SLOF | expand

Commit Message

Stefan Berger Jan. 21, 2020, 8:01 p.m. UTC
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(+)

Comments

Kevin O'Connor Feb. 14, 2020, 3:37 p.m. UTC | #1
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 mbox series

Patch

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