security/tpm: make tis_probe() return tpm_family

Via an out parameter. This is needed to be able to dynamically pick TSS
implementation based on the information discovered on probing.

Change-Id: I5006e0cdfef76ff79ce9e1cf280fcd5515ae01b0
Ticket: https://ticket.coreboot.org/issues/433
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/69159
Reviewed-by: Martin L Roth <gaumless@gmail.com>
Reviewed-by: Jérémy Compostella <jeremy.compostella@intel.com>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-by: Christian Walter <christian.walter@9elements.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/crb/tis.c b/src/drivers/crb/tis.c
index a5d7dcc..766ee25 100644
--- a/src/drivers/crb/tis.c
+++ b/src/drivers/crb/tis.c
@@ -46,7 +46,7 @@
 	return TPM_SUCCESS;
 }
 
-tis_sendrecv_fn tis_probe(void)
+tis_sendrecv_fn tis_probe(enum tpm_family *family)
 {
 	struct tpm2_info info;
 
@@ -54,6 +54,10 @@
 	if (tpm2_init())
 		return NULL;
 
+	/* CRB interface exists only in TPM2 */
+	if (family != NULL)
+		*family = TPM_2;
+
 	tpm2_get_info(&info);
 
 	printk(BIOS_INFO, "Initialized TPM device %s revision %d\n", tis_get_dev_name(&info),
diff --git a/src/drivers/i2c/tpm/cr50.c b/src/drivers/i2c/tpm/cr50.c
index 8462198..b58fbc8 100644
--- a/src/drivers/i2c/tpm/cr50.c
+++ b/src/drivers/i2c/tpm/cr50.c
@@ -424,8 +424,11 @@
 	chip->cancel = &cr50_i2c_tis_ready;
 }
 
-tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr)
+tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr, enum tpm_family *family)
 {
+	/* cr50 is TPM2 */
+	if (family != NULL)
+		*family = TPM_2;
 	return TPM_SUCCESS;
 }
 
diff --git a/src/drivers/i2c/tpm/tis.c b/src/drivers/i2c/tpm/tis.c
index 6cb05f0..fd09240 100644
--- a/src/drivers/i2c/tpm/tis.c
+++ b/src/drivers/i2c/tpm/tis.c
@@ -118,9 +118,9 @@
 	return TPM_SUCCESS;
 }
 
-tis_sendrecv_fn tis_probe(void)
+tis_sendrecv_fn tis_probe(enum tpm_family *family)
 {
-	if (tpm_vendor_probe(CONFIG_DRIVER_TPM_I2C_BUS, CONFIG_DRIVER_TPM_I2C_ADDR))
+	if (tpm_vendor_probe(CONFIG_DRIVER_TPM_I2C_BUS, CONFIG_DRIVER_TPM_I2C_ADDR, family))
 		return NULL;
 
 	if (tpm_vendor_init(&chip, CONFIG_DRIVER_TPM_I2C_BUS, CONFIG_DRIVER_TPM_I2C_ADDR))
diff --git a/src/drivers/i2c/tpm/tis_atmel.c b/src/drivers/i2c/tpm/tis_atmel.c
index 0a29049..a0bbf33 100644
--- a/src/drivers/i2c/tpm/tis_atmel.c
+++ b/src/drivers/i2c/tpm/tis_atmel.c
@@ -107,7 +107,19 @@
 	return TPM_SUCCESS;
 }
 
-tis_sendrecv_fn tis_probe(void)
+tis_sendrecv_fn tis_probe(enum tpm_family *family)
 {
+	/*
+	 * Can't query version or really anything as the device doesn't support
+	 * much through this interface (can't specify address on accesses).
+	 *
+	 * Hence the assumption is that whatever TPM version is enabled at
+	 * compile-time defines what the device supports. The check is written
+	 * in a way to give TPM 1 preference even if support for both versions
+	 * is compiled in.
+	 */
+	if (family != NULL)
+		*family = CONFIG(TPM1) ? TPM_1 : TPM_2;
+
 	return &i2c_tis_sendrecv;
 }
diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c
index 541eb3a..eb27984 100644
--- a/src/drivers/i2c/tpm/tpm.c
+++ b/src/drivers/i2c/tpm/tpm.c
@@ -451,13 +451,29 @@
 
 /* Initialization of I2C TPM */
 
-tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr)
+tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr, enum tpm_family *family)
 {
 	struct stopwatch sw;
 	uint8_t buf = 0;
 	int ret;
 	long sw_run_duration = SLEEP_DURATION_PROBE_MS;
 
+	/*
+	 * Infineon "I2C Protocol Stack Specification v0.20" is supposedly a
+	 * simple adoption of the LPC TIS Protocol to the I2C Bus, but looking
+	 * at "TCG PC Client Specific TIS" doesn't confirm that and Infineon's
+	 * specification isn't publicly available.
+	 *
+	 * Because it's unknown how to access information about TPM version of
+	 * the device in this case, the assumption is that whatever TPM version
+	 * is enabled at compile-time defines what the device supports. And
+	 * since this driver doesn't appear to be used with TPM 2 devices, the
+	 * check is written in a way to give TPM 1 preference even if support
+	 * for both versions is compiled in.
+	 */
+	if (family != NULL)
+		*family = CONFIG(TPM1) ? TPM_1 : TPM_2;
+
 	tpm_dev.chip_type = UNKNOWN;
 	tpm_dev.bus = bus;
 	tpm_dev.addr = addr;
diff --git a/src/drivers/i2c/tpm/tpm.h b/src/drivers/i2c/tpm/tpm.h
index 628ad4d..46935e24 100644
--- a/src/drivers/i2c/tpm/tpm.h
+++ b/src/drivers/i2c/tpm/tpm.h
@@ -12,6 +12,7 @@
 #ifndef __DRIVERS_TPM_SLB9635_I2C_TPM_H__
 #define __DRIVERS_TPM_SLB9635_I2C_TPM_H__
 
+#include <security/tpm/tis.h>
 #include <security/tpm/tss_errors.h>
 #include <stdint.h>
 
@@ -51,7 +52,7 @@
 
 /* ---------- Interface for TPM vendor ------------ */
 
-tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr);
+tpm_result_t tpm_vendor_probe(unsigned int bus, uint32_t addr, enum tpm_family *family);
 
 tpm_result_t tpm_vendor_init(struct tpm_chip *chip, unsigned int bus, uint32_t dev_addr);
 
diff --git a/src/drivers/pc80/tpm/tis.c b/src/drivers/pc80/tpm/tis.c
index 90fa32e..da443d3 100644
--- a/src/drivers/pc80/tpm/tis.c
+++ b/src/drivers/pc80/tpm/tis.c
@@ -78,12 +78,6 @@
  /* 1 second is plenty for anything TPM does.*/
 #define MAX_DELAY_US	USECS_PER_SEC
 
-enum tpm_family {
-	TPM_UNKNOWN = 0,
-	TPM_1 = 1,
-	TPM_2 = 2,
-};
-
 /*
  * Structures defined below allow creating descriptions of TPM vendor/device
  * ID information for run time discovery.
@@ -380,8 +374,10 @@
  * Returns TPM_SUCCESS on success (the device is found or was found during
  * an earlier invocation) or TPM_CB_FAIL if the device is not found.
  */
-static tpm_result_t pc80_tis_probe(void)
+static tpm_result_t pc80_tis_probe(enum tpm_family *family)
 {
+	static enum tpm_family tpm_family;
+
 	const char *device_name = NULL;
 	const char *vendor_name = NULL;
 	const struct device_name *dev;
@@ -389,11 +385,13 @@
 	u16 vid, did;
 	u8 locality = 0, intf_type;
 	int i;
-	enum tpm_family family;
 	const char *family_str;
 
-	if (vendor_dev_id)
+	if (vendor_dev_id) {
+		if (family != NULL)
+			*family = tpm_family;
 		return TPM_SUCCESS;  /* Already probed. */
+	}
 
 	didvid = tpm_read_did_vid(0);
 	if (!didvid || (didvid == 0xffffffff)) {
@@ -409,10 +407,10 @@
 		switch (intf_version) {
 		case 0:
 		case 2:
-			family = TPM_1;
+			tpm_family = TPM_1;
 			break;
 		case 3:
-			family = TPM_2;
+			tpm_family = TPM_2;
 			break;
 		default:
 			printf("%s: Unexpected TPM interface version: %d\n", __func__,
@@ -420,7 +418,7 @@
 			return TPM_CB_PROBE_FAILURE;
 		}
 	} else if (intf_type == 0) {
-		family = TPM_2;
+		tpm_family = TPM_2;
 	} else {
 		printf("%s: Unexpected TPM interface type: %d\n", __func__, intf_type);
 		return TPM_CB_PROBE_FAILURE;
@@ -439,7 +437,7 @@
 		}
 		dev = &vendor_names[i].dev_names[j];
 		while (dev->dev_id != 0xffff) {
-			if (dev->dev_id == did && dev->family == family) {
+			if (dev->dev_id == did && dev->family == tpm_family) {
 				device_name = dev->dev_name;
 				break;
 			}
@@ -449,7 +447,7 @@
 		break;
 	}
 
-	family_str = (family == TPM_1 ? "TPM 1.2" : "TPM 2.0");
+	family_str = (tpm_family == TPM_1 ? "TPM 1.2" : "TPM 2.0");
 	if (vendor_name == NULL) {
 		printk(BIOS_INFO, "Found %s 0x%04x by 0x%04x\n", family_str, did, vid);
 	} else if (device_name == NULL) {
@@ -460,6 +458,8 @@
 		       device_name, did, vendor_name, vid);
 	}
 
+	if (family != NULL)
+		*family = tpm_family;
 	return TPM_SUCCESS;
 }
 
@@ -720,12 +720,15 @@
 /*
  * tis_probe()
  *
- * Probe for the TPM device and set it up for use within locality 0. Returns
- * pointer to send-receive function on success or NULL on failure.
+ * Probe for the TPM device and set it up for use within locality 0.
+ *
+ * @tpm_family - pointer to tpm_family which is set to TPM family of the device.
+ *
+ * Returns pointer to send-receive function on success or NULL on failure.
  */
-tis_sendrecv_fn tis_probe(void)
+tis_sendrecv_fn tis_probe(enum tpm_family *family)
 {
-	if (pc80_tis_probe())
+	if (pc80_tis_probe(family))
 		return NULL;
 
 	if (pc80_tis_open())
diff --git a/src/drivers/spi/tpm/tis.c b/src/drivers/spi/tpm/tis.c
index 90d7f59..89ea985 100644
--- a/src/drivers/spi/tpm/tis.c
+++ b/src/drivers/spi/tpm/tis.c
@@ -40,7 +40,7 @@
 	return TPM_SUCCESS;
 }
 
-tis_sendrecv_fn tis_probe(void)
+tis_sendrecv_fn tis_probe(enum tpm_family *family)
 {
 	struct spi_slave spi;
 	struct tpm2_info info;
@@ -56,6 +56,10 @@
 		return NULL;
 	}
 
+	/* tpm2_process_command() is used unconditionally in tpm_sendrecv() */
+	if (family != NULL)
+		*family = TPM_2;
+
 	tpm2_get_info(&info);
 
 	printk(BIOS_INFO, "Initialized TPM device %s revision %d\n",
diff --git a/src/security/tpm/tis.h b/src/security/tpm/tis.h
index ac07bfb..4a8dc14 100644
--- a/src/security/tpm/tis.h
+++ b/src/security/tpm/tis.h
@@ -32,6 +32,12 @@
 	TPM_STS_RESPONSE_RETRY = (1 << 1),
 };
 
+enum tpm_family {
+	TPM_UNKNOWN = 0,
+	TPM_1 = 1,
+	TPM_2 = 2,
+};
+
 /*
  * tis_sendrecv()
  *
@@ -50,13 +56,16 @@
 /*
  * tis_probe()
  *
- * Probe for the TPM device and set it up for use within locality 0. Returns
- * pointer to send-receive function on success or NULL on failure.
+ * Probe for the TPM device and set it up for use within locality 0.
+ *
+ * @family - pointer which is set to TPM family of the device
+ *
+ * Returns pointer to send-receive function on success or NULL on failure.
  *
  * Do not call this explicitly, it's meant to be used exclusively by TSS
  * implementation (tlcl_lib_init() function to be specific).
  */
-tis_sendrecv_fn tis_probe(void);
+tis_sendrecv_fn tis_probe(enum tpm_family *family);
 
 /*
  * tis_vendor_write()
diff --git a/src/security/tpm/tss/tcg-1.2/tss.c b/src/security/tpm/tss/tcg-1.2/tss.c
index f0d28df..913f79b 100644
--- a/src/security/tpm/tss/tcg-1.2/tss.c
+++ b/src/security/tpm/tss/tcg-1.2/tss.c
@@ -153,13 +153,20 @@
 
 tpm_result_t tlcl_lib_init(void)
 {
+	enum tpm_family family;
+
 	if (tis_sendrecv != NULL)
 		return TPM_SUCCESS;
 
-	tis_sendrecv = tis_probe();
+	tis_sendrecv = tis_probe(&family);
 	if (tis_sendrecv == NULL)
 		return TPM_CB_NO_DEVICE;
 
+	if (family != TPM_1) {
+		tis_sendrecv = NULL;
+		return TPM_CB_INTERNAL_INCONSISTENCY;
+	}
+
 	return TPM_SUCCESS;
 }
 
diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c
index 135d296..27390a7 100644
--- a/src/security/tpm/tss/tcg-2.0/tss.c
+++ b/src/security/tpm/tss/tcg-2.0/tss.c
@@ -211,15 +211,24 @@
 /* This function is called directly by vboot, uses vboot return types. */
 tpm_result_t tlcl_lib_init(void)
 {
+	enum tpm_family family;
+
 	if (tis_sendrecv != NULL)
 		return TPM_SUCCESS;
 
-	tis_sendrecv = tis_probe();
+	tis_sendrecv = tis_probe(&family);
 	if (tis_sendrecv == NULL) {
 		printk(BIOS_ERR, "%s: tis_probe returned error\n", __func__);
 		return TPM_CB_NO_DEVICE;
 	}
 
+	if (family != TPM_2) {
+		tis_sendrecv = NULL;
+		printk(BIOS_ERR, "%s: tis_probe returned unsupported TPM family: %d\n",
+		       __func__, family);
+		return TPM_CB_INTERNAL_INCONSISTENCY;
+	}
+
 	return TPM_SUCCESS;
 }