mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	tpm: Provide a generic means to override the chip returned timeouts
Some Atmel TPMs provide completely wrong timeouts from their TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns new correct values via a DID/VID table in the TIS driver. Tested on ARM using an AT97SC3204T FW version 37.16 Cc: <stable@vger.kernel.org> [PHuewe: without this fix these 'broken' Atmel TPMs won't function on older kernels] Signed-off-by: "Berg, Christopher" <Christopher.Berg@atmel.com> Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
This commit is contained in:
		
							parent
							
								
									3e14d83ef9
								
							
						
					
					
						commit
						8e54caf407
					
				
					 3 changed files with 75 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
 | 
			
		|||
int tpm_get_timeouts(struct tpm_chip *chip)
 | 
			
		||||
{
 | 
			
		||||
	struct tpm_cmd_t tpm_cmd;
 | 
			
		||||
	struct timeout_t *timeout_cap;
 | 
			
		||||
	unsigned long new_timeout[4];
 | 
			
		||||
	unsigned long old_timeout[4];
 | 
			
		||||
	struct duration_t *duration_cap;
 | 
			
		||||
	ssize_t rc;
 | 
			
		||||
	u32 timeout;
 | 
			
		||||
	unsigned int scale = 1;
 | 
			
		||||
 | 
			
		||||
	tpm_cmd.header.in = tpm_getcap_header;
 | 
			
		||||
	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
 | 
			
		||||
| 
						 | 
				
			
			@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip)
 | 
			
		|||
	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
 | 
			
		||||
	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
 | 
			
		||||
	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
 | 
			
		||||
	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
 | 
			
		||||
	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
 | 
			
		||||
	memcpy(new_timeout, old_timeout, sizeof(new_timeout));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Provide ability for vendor overrides of timeout values in case
 | 
			
		||||
	 * of misreporting.
 | 
			
		||||
	 */
 | 
			
		||||
	if (chip->ops->update_timeouts != NULL)
 | 
			
		||||
		chip->vendor.timeout_adjusted =
 | 
			
		||||
			chip->ops->update_timeouts(chip, new_timeout);
 | 
			
		||||
 | 
			
		||||
	if (!chip->vendor.timeout_adjusted) {
 | 
			
		||||
		/* Don't overwrite default if value is 0 */
 | 
			
		||||
	timeout = be32_to_cpu(timeout_cap->a);
 | 
			
		||||
	if (timeout && timeout < 1000) {
 | 
			
		||||
		if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
 | 
			
		||||
			int i;
 | 
			
		||||
 | 
			
		||||
			/* timeouts in msec rather usec */
 | 
			
		||||
		scale = 1000;
 | 
			
		||||
			for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
 | 
			
		||||
				new_timeout[i] *= 1000;
 | 
			
		||||
			chip->vendor.timeout_adjusted = true;
 | 
			
		||||
		}
 | 
			
		||||
	if (timeout)
 | 
			
		||||
		chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
 | 
			
		||||
	timeout = be32_to_cpu(timeout_cap->b);
 | 
			
		||||
	if (timeout)
 | 
			
		||||
		chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
 | 
			
		||||
	timeout = be32_to_cpu(timeout_cap->c);
 | 
			
		||||
	if (timeout)
 | 
			
		||||
		chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
 | 
			
		||||
	timeout = be32_to_cpu(timeout_cap->d);
 | 
			
		||||
	if (timeout)
 | 
			
		||||
		chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Report adjusted timeouts */
 | 
			
		||||
	if (chip->vendor.timeout_adjusted) {
 | 
			
		||||
		dev_info(chip->dev,
 | 
			
		||||
			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
 | 
			
		||||
			 old_timeout[0], new_timeout[0],
 | 
			
		||||
			 old_timeout[1], new_timeout[1],
 | 
			
		||||
			 old_timeout[2], new_timeout[2],
 | 
			
		||||
			 old_timeout[3], new_timeout[3]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
 | 
			
		||||
	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
 | 
			
		||||
	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
 | 
			
		||||
	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
 | 
			
		||||
 | 
			
		||||
duration:
 | 
			
		||||
	tpm_cmd.header.in = tpm_getcap_header;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -373,6 +373,36 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 | 
			
		|||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct tis_vendor_timeout_override {
 | 
			
		||||
	u32 did_vid;
 | 
			
		||||
	unsigned long timeout_us[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
 | 
			
		||||
	/* Atmel 3204 */
 | 
			
		||||
	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
 | 
			
		||||
			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
 | 
			
		||||
				    unsigned long *timeout_cap)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u32 did_vid;
 | 
			
		||||
 | 
			
		||||
	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
 | 
			
		||||
		if (vendor_timeout_overrides[i].did_vid != did_vid)
 | 
			
		||||
			continue;
 | 
			
		||||
		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
 | 
			
		||||
		       sizeof(vendor_timeout_overrides[i].timeout_us));
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Early probing for iTPM with STS_DATA_EXPECT flaw.
 | 
			
		||||
 * Try sending command without itpm flag set and if that
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = {
 | 
			
		|||
	.recv = tpm_tis_recv,
 | 
			
		||||
	.send = tpm_tis_send,
 | 
			
		||||
	.cancel = tpm_tis_ready,
 | 
			
		||||
	.update_timeouts = tpm_tis_update_timeouts,
 | 
			
		||||
	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | 
			
		||||
	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
 | 
			
		||||
	.req_canceled = tpm_tis_req_canceled,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,9 @@ struct tpm_class_ops {
 | 
			
		|||
	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
 | 
			
		||||
	void (*cancel) (struct tpm_chip *chip);
 | 
			
		||||
	u8 (*status) (struct tpm_chip *chip);
 | 
			
		||||
	bool (*update_timeouts)(struct tpm_chip *chip,
 | 
			
		||||
				unsigned long *timeout_cap);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue