Commit aab73d95 authored by James Bottomley's avatar James Bottomley Committed by Jarkko Sakkinen
Browse files

tpm: add sysfs exports for all banks of PCR registers



Create sysfs per hash groups with 24 PCR files in them one group,
named pcr-<hash>, for each agile hash of the TPM.  The files are
plugged in to a PCR read function which is TPM version agnostic, so
this works also for TPM 1.2 but the hash is only sha1 in that case.

Note: the macros used to create the hashes emit spurious checkpatch
warnings.  Do not try to "fix" them as checkpatch recommends, otherwise
they'll break.

Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: default avatarJerry Snitselaar <jsnitsel@redhat.com>
Tested-by: default avatarThiago Jung Bauermann <bauerman@linux.ibm.com>
Tested-by: default avatarJarkko Sakkinen <jarkko@kernel.org>
Reviewed-by: default avatarJarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: default avatarJarkko Sakkinen <jarkko@kernel.org>
parent 40d32b59
Loading
Loading
Loading
Loading
+179 −0
Original line number Diff line number Diff line
@@ -337,11 +337,190 @@ static const struct attribute_group tpm2_dev_group = {
	.attrs = tpm2_dev_attrs,
};

struct tpm_pcr_attr {
	int alg_id;
	int pcr;
	struct device_attribute attr;
};

#define to_tpm_pcr_attr(a) container_of(a, struct tpm_pcr_attr, attr)

static ssize_t pcr_value_show(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
{
	struct tpm_pcr_attr *ha = to_tpm_pcr_attr(attr);
	struct tpm_chip *chip = to_tpm_chip(dev);
	struct tpm_digest digest;
	int i;
	int digest_size = 0;
	int rc;
	char *str = buf;

	for (i = 0; i < chip->nr_allocated_banks; i++)
		if (ha->alg_id == chip->allocated_banks[i].alg_id)
			digest_size = chip->allocated_banks[i].digest_size;
	/* should never happen */
	if (!digest_size)
		return -EINVAL;

	digest.alg_id = ha->alg_id;
	rc = tpm_pcr_read(chip, ha->pcr, &digest);
	if (rc)
		return rc;
	for (i = 0; i < digest_size; i++)
		str += sprintf(str, "%02X", digest.digest[i]);
	str += sprintf(str, "\n");

	return str - buf;
}

/*
 * The following set of defines represents all the magic to build
 * the per hash attribute groups for displaying each bank of PCRs.
 * The only slight problem with this approach is that every PCR is
 * hard coded to be present, so you don't know if an PCR is missing
 * until a cat of the file returns -EINVAL
 *
 * Also note you must ignore checkpatch warnings in this macro
 * code. This is deep macro magic that checkpatch.pl doesn't
 * understand.
 */

/* Note, this must match TPM2_PLATFORM_PCR which is fixed at 24. */
#define _TPM_HELPER(_alg, _hash, F) \
	F(_alg, _hash, 0)	    \
	F(_alg, _hash, 1)	    \
	F(_alg, _hash, 2)	    \
	F(_alg, _hash, 3)	    \
	F(_alg, _hash, 4)	    \
	F(_alg, _hash, 5)	    \
	F(_alg, _hash, 6)	    \
	F(_alg, _hash, 7)	    \
	F(_alg, _hash, 8)	    \
	F(_alg, _hash, 9)	    \
	F(_alg, _hash, 10)	    \
	F(_alg, _hash, 11)	    \
	F(_alg, _hash, 12)	    \
	F(_alg, _hash, 13)	    \
	F(_alg, _hash, 14)	    \
	F(_alg, _hash, 15)	    \
	F(_alg, _hash, 16)	    \
	F(_alg, _hash, 17)	    \
	F(_alg, _hash, 18)	    \
	F(_alg, _hash, 19)	    \
	F(_alg, _hash, 20)	    \
	F(_alg, _hash, 21)	    \
	F(_alg, _hash, 22)	    \
	F(_alg, _hash, 23)

/* ignore checkpatch warning about trailing ; in macro. */
#define PCR_ATTR(_alg, _hash, _pcr)				   \
	static struct tpm_pcr_attr dev_attr_pcr_##_hash##_##_pcr = {	\
		.alg_id = _alg,					   \
		.pcr = _pcr,					   \
		.attr = {					   \
			.attr = {				   \
				.name = __stringify(_pcr),	   \
				.mode = 0444			   \
			},					   \
			.show = pcr_value_show			   \
		}						   \
	};

#define PCR_ATTRS(_alg, _hash)			\
	_TPM_HELPER(_alg, _hash, PCR_ATTR)

/* ignore checkpatch warning about trailing , in macro. */
#define PCR_ATTR_VAL(_alg, _hash, _pcr)		\
	&dev_attr_pcr_##_hash##_##_pcr.attr.attr,

#define PCR_ATTR_GROUP_ARRAY(_alg, _hash)		       \
	static struct attribute *pcr_group_attrs_##_hash[] = { \
		_TPM_HELPER(_alg, _hash, PCR_ATTR_VAL)	       \
		NULL					       \
	}

#define PCR_ATTR_GROUP(_alg, _hash)			    \
	static struct attribute_group pcr_group_##_hash = { \
		.name = "pcr-" __stringify(_hash),	    \
		.attrs = pcr_group_attrs_##_hash	    \
	}

#define PCR_ATTR_BUILD(_alg, _hash)	   \
	PCR_ATTRS(_alg, _hash)		   \
	PCR_ATTR_GROUP_ARRAY(_alg, _hash); \
	PCR_ATTR_GROUP(_alg, _hash)
/*
 * End of macro structure to build an attribute group containing 24
 * PCR value files for each supported hash algorithm
 */

/*
 * The next set of macros implements the cleverness for each hash to
 * build a static attribute group called pcr_group_<hash> which can be
 * added to chip->groups[].
 *
 * The first argument is the TPM algorithm id and the second is the
 * hash used as both the suffix and the group name.  Note: the group
 * name is a directory in the top level tpm class with the name
 * pcr-<hash>, so it must not clash with any other names already
 * in the sysfs directory.
 */
PCR_ATTR_BUILD(TPM_ALG_SHA1, sha1);
PCR_ATTR_BUILD(TPM_ALG_SHA256, sha256);
PCR_ATTR_BUILD(TPM_ALG_SHA384, sha384);
PCR_ATTR_BUILD(TPM_ALG_SHA512, sha512);
PCR_ATTR_BUILD(TPM_ALG_SM3_256, sm3);


void tpm_sysfs_add_device(struct tpm_chip *chip)
{
	int i;

	WARN_ON(chip->groups_cnt != 0);

	if (chip->flags & TPM_CHIP_FLAG_TPM2)
		chip->groups[chip->groups_cnt++] = &tpm2_dev_group;
	else
		chip->groups[chip->groups_cnt++] = &tpm1_dev_group;

	/* add one group for each bank hash */
	for (i = 0; i < chip->nr_allocated_banks; i++) {
		switch (chip->allocated_banks[i].alg_id) {
		case TPM_ALG_SHA1:
			chip->groups[chip->groups_cnt++] = &pcr_group_sha1;
			break;
		case TPM_ALG_SHA256:
			chip->groups[chip->groups_cnt++] = &pcr_group_sha256;
			break;
		case TPM_ALG_SHA384:
			chip->groups[chip->groups_cnt++] = &pcr_group_sha384;
			break;
		case TPM_ALG_SHA512:
			chip->groups[chip->groups_cnt++] = &pcr_group_sha512;
			break;
		case TPM_ALG_SM3_256:
			chip->groups[chip->groups_cnt++] = &pcr_group_sm3;
			break;
		default:
			/*
			 * If triggers, send a patch to add both a
			 * PCR_ATTR_BUILD() macro above for the
			 * missing algorithm as well as an additional
			 * case in this switch statement.
			 */
			dev_err(&chip->dev,
				"TPM with unsupported bank algorithm 0x%04x",
				chip->allocated_banks[i].alg_id);
			break;
		}
	}

	/*
	 * This will only trigger if someone has added an additional
	 * hash to the tpm_algorithms enum without incrementing
	 * TPM_MAX_HASHES.
	 */
	WARN_ON(chip->groups_cnt > TPM_MAX_HASHES + 1);
}
+8 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ struct tpm_chip;
struct trusted_key_payload;
struct trusted_key_options;

/* if you add a new hash to this, increment TPM_MAX_HASHES below */
enum tpm_algorithms {
	TPM_ALG_ERROR		= 0x0000,
	TPM_ALG_SHA1		= 0x0004,
@@ -42,6 +43,12 @@ enum tpm_algorithms {
	TPM_ALG_SM3_256		= 0x0012,
};

/*
 * maximum number of hashing algorithms a TPM can have.  This is
 * basically a count of every hash in tpm_algorithms above
 */
#define TPM_MAX_HASHES	5

struct tpm_digest {
	u16 alg_id;
	u8 digest[TPM_MAX_DIGEST_SIZE];
@@ -146,7 +153,7 @@ struct tpm_chip {

	struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];

	const struct attribute_group *groups[3];
	const struct attribute_group *groups[3 + TPM_MAX_HASHES];
	unsigned int groups_cnt;

	u32 nr_allocated_banks;