diff mbox series

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

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

Commit Message

Stefan Berger Jan. 11, 2020, 1:21 a.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        | 324 ++++++++++++++++++++++++++++++++++++
 lib/libtpm/tcgbios.h        |   1 +
 lib/libtpm/tpm.code         |   9 +
 lib/libtpm/tpm.in           |   1 +
 slof/fs/start-up.fs         |   9 +
 7 files changed, 353 insertions(+)

Comments

Segher Boessenkool Jan. 11, 2020, 11:02 a.m. UTC | #1
Hi!

On Fri, Jan 10, 2020 at 08:21:55PM -0500, Stefan Berger wrote:
> --- 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." terminal-write drop
> +    THEN

Why  terminal-write  , when the code just above uses  dump-display-write
instead?

> +: (t-pressed) ( -- )
> +   s" /ibm,vtpm" find-node dup IF
> +      s" vtpm-menu" rot $call-static
> +   ELSE
> +      drop
> +   THEN
> +;

: (t-pressed) ( -- )
   s" /ibm,vtpm" find-node ?dup IF
      s" vtpm-menu" rot $call-static
   THEN
;

(This is the prime reason  ?dup  exists at all :-) )


Segher
Stefan Berger Jan. 11, 2020, 3:49 p.m. UTC | #2
On 1/11/20 6:02 AM, Segher Boessenkool wrote:
> Hi!
>
> On Fri, Jan 10, 2020 at 08:21:55PM -0500, Stefan Berger wrote:
>> --- 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." terminal-write drop
>> +    THEN
> Why  terminal-write  , when the code just above uses  dump-display-write
> instead?
>
>> +: (t-pressed) ( -- )
>> +   s" /ibm,vtpm" find-node dup IF
>> +      s" vtpm-menu" rot $call-static
>> +   ELSE
>> +      drop
>> +   THEN
>> +;
> : (t-pressed) ( -- )
>     s" /ibm,vtpm" find-node ?dup IF
>        s" vtpm-menu" rot $call-static
>     THEN
> ;
>
> (This is the prime reason  ?dup  exists at all :-) )


Didn't know. Will fix. Thanks.


>
>
> Segher
diff mbox series

Patch

diff --git a/board-qemu/slof/OF.fs b/board-qemu/slof/OF.fs
index 3e117ad..7bdd6ea 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." terminal-write drop
+    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 a140276..53d9eae 100644
--- a/board-qemu/slof/vtpm-sml.fs
+++ b/board-qemu/slof/vtpm-sml.fs
@@ -116,6 +116,12 @@  log-base LOG-SIZE tpm-set-log-parameters
     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 ba5cc46..c493bb7 100644
--- a/lib/libtpm/tcgbios.c
+++ b/lib/libtpm/tcgbios.c
@@ -117,19 +117,30 @@  static const struct hash_parameters {
 } 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",
 	}
 };
 
@@ -145,6 +156,42 @@  tpm20_get_hash_buffersize(uint16_t hashAlg)
 	return -1;
 }
 
+static uint8_t
+tpm20_hashalg_to_flag(uint16_t hashAlg)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg == hashAlg)
+			return hash_parameters[i].hashalg_flag;
+	}
+	return 0;
+}
+
+static uint16_t
+tpm20_hashalg_flag_to_hashalg(uint8_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].hashalg;
+	}
+	return 0;
+}
+
+static const char *
+tpm20_hashalg_flag_to_name(uint8_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].name;
+	}
+	return NULL;
+}
+
 /*
  * Build the TPM2 tpm2_digest_values data structure from the given hash.
  * Follow the PCR bank configuration of the TPM and write the same hash
@@ -914,3 +961,280 @@  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)
+{
+	*suppt_pcrbanks = 0;
+	*active_pcrbanks = 0;
+
+	if (!tpm20_pcr_selection)
+		return -1;
+
+	struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+	void *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+	while (1) {
+		uint8_t sizeOfSelect = sel->sizeOfSelect;
+		void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+		if (nsel > end)
+			return 0;
+
+		uint16_t hashalg = be16_to_cpu(sel->hashAlg);
+		uint8_t hashalg_flag = tpm20_hashalg_to_flag(hashalg);
+
+		*suppt_pcrbanks |= hashalg_flag;
+
+		unsigned i;
+		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;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare);
+
+	while (hashalg_flag) {
+		if ((hashalg_flag & suppt_banks)) {
+			uint16_t 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]));
+
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+
+	int ret = tpmhw_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 = 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 = tpmhw_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 = tpmhw_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;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks);
+
+	uint8_t activate_banks = active_banks;
+
+	while (1) {
+		uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+		uint8_t i = 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");
+
+		uint8_t flagnum;
+		int show = 0;
+		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 a9fbbb8..ef792c5 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 8254eba..1ec91ca 100644
--- a/slof/fs/start-up.fs
+++ b/slof/fs/start-up.fs
@@ -55,6 +55,14 @@ 
    nvramlog-write-string-cr
 ;
 
+: (t-pressed) ( -- )
+   s" /ibm,vtpm" find-node dup IF
+      s" vtpm-menu" rot $call-static
+   ELSE
+      drop
+   THEN
+;
+
 : (boot?) ( -- )
    \ last step before we boot we give up physical presence on the TPM
    s" /ibm,vtpm" find-node dup IF
@@ -107,6 +115,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