tpm.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,167 行 · 第 1/2 页

C
1,167
字号
/* * Copyright (C) 2004 IBM Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> * Dave Safford <safford@watson.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Kylene Hall <kjhall@us.ibm.com> * * Maintained by: <tpmdd_devel@lists.sourceforge.net> * * Device driver for TCG/TCPA TPM (trusted platform module). * Specifications at www.trustedcomputinggroup.org	  * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. *  * Note, the TPM chip is not interrupt driven (only polling) * and can have very long timeouts (minutes!). Hence the unusual * calls to msleep. * */#include <linux/sched.h>#include <linux/poll.h>#include <linux/spinlock.h>#include "tpm.h"enum tpm_const {	TPM_MINOR = 224,	/* officially assigned */	TPM_BUFSIZE = 2048,	TPM_NUM_DEVICES = 256,};enum tpm_duration {	TPM_SHORT = 0,	TPM_MEDIUM = 1,	TPM_LONG = 2,	TPM_UNDEFINED,};#define TPM_MAX_ORDINAL 243#define TPM_MAX_PROTECTED_ORDINAL 12#define TPM_PROTECTED_ORDINAL_MASK 0xFFstatic LIST_HEAD(tpm_chip_list);static DEFINE_SPINLOCK(driver_lock);static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);/* * Array with one entry per ordinal defining the maximum amount * of time the chip could take to return the result.  The ordinal * designation of short, medium or long is defined in a table in * TCG Specification TPM Main Part 2 TPM Structures Section 17. The * values of the SHORT, MEDIUM, and LONG durations are retrieved * from the chip during initialization with a call to tpm_get_timeouts. */static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {	TPM_UNDEFINED,		/* 0 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 5 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 10 */	TPM_SHORT,};static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {	TPM_UNDEFINED,		/* 0 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 5 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 10 */	TPM_SHORT,	TPM_MEDIUM,	TPM_LONG,	TPM_LONG,	TPM_MEDIUM,		/* 15 */	TPM_SHORT,	TPM_SHORT,	TPM_MEDIUM,	TPM_LONG,	TPM_SHORT,		/* 20 */	TPM_SHORT,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_SHORT,		/* 25 */	TPM_SHORT,	TPM_MEDIUM,	TPM_SHORT,	TPM_SHORT,	TPM_MEDIUM,		/* 30 */	TPM_LONG,	TPM_MEDIUM,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,		/* 35 */	TPM_MEDIUM,	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_MEDIUM,		/* 40 */	TPM_LONG,	TPM_MEDIUM,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,		/* 45 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_LONG,	TPM_MEDIUM,		/* 50 */	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 55 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_MEDIUM,		/* 60 */	TPM_MEDIUM,	TPM_MEDIUM,	TPM_SHORT,	TPM_SHORT,	TPM_MEDIUM,		/* 65 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 70 */	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 75 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_LONG,		/* 80 */	TPM_UNDEFINED,	TPM_MEDIUM,	TPM_LONG,	TPM_SHORT,	TPM_UNDEFINED,		/* 85 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 90 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,		/* 95 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_MEDIUM,		/* 100 */	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 105 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 110 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,		/* 115 */	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_LONG,		/* 120 */	TPM_LONG,	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_SHORT,	TPM_SHORT,		/* 125 */	TPM_SHORT,	TPM_LONG,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,		/* 130 */	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_SHORT,	TPM_MEDIUM,	TPM_UNDEFINED,		/* 135 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 140 */	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 145 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 150 */	TPM_MEDIUM,	TPM_MEDIUM,	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,		/* 155 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 160 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 165 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_LONG,		/* 170 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 175 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_MEDIUM,		/* 180 */	TPM_SHORT,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_MEDIUM,		/* 185 */	TPM_SHORT,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 190 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 195 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 200 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,	TPM_SHORT,		/* 205 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_MEDIUM,		/* 210 */	TPM_UNDEFINED,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_MEDIUM,	TPM_UNDEFINED,		/* 215 */	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,	TPM_SHORT,		/* 220 */	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_SHORT,	TPM_UNDEFINED,		/* 225 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 230 */	TPM_LONG,	TPM_MEDIUM,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,		/* 235 */	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_UNDEFINED,	TPM_SHORT,		/* 240 */	TPM_UNDEFINED,	TPM_MEDIUM,};static void user_reader_timeout(unsigned long ptr){	struct tpm_chip *chip = (struct tpm_chip *) ptr;	schedule_work(&chip->work);}static void timeout_work(void *ptr){	struct tpm_chip *chip = ptr;	down(&chip->buffer_mutex);	atomic_set(&chip->data_pending, 0);	memset(chip->data_buffer, 0, TPM_BUFSIZE);	up(&chip->buffer_mutex);}/* * Returns max number of jiffies to wait */unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,					   u32 ordinal){	int duration_idx = TPM_UNDEFINED;	int duration = 0;	if (ordinal < TPM_MAX_ORDINAL)		duration_idx = tpm_ordinal_duration[ordinal];	else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <		 TPM_MAX_PROTECTED_ORDINAL)		duration_idx =		    tpm_protected_ordinal_duration[ordinal &						   TPM_PROTECTED_ORDINAL_MASK];	if (duration_idx != TPM_UNDEFINED)		duration = chip->vendor.duration[duration_idx];	if (duration <= 0)		return 2 * 60 * HZ;	else		return duration;}EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);/* * Internal kernel interface to transmit TPM commands */static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,			    size_t bufsiz){	ssize_t rc;	u32 count, ordinal;	unsigned long stop;	count = be32_to_cpu(*((__be32 *) (buf + 2)));	ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));	if (count == 0)		return -ENODATA;	if (count > bufsiz) {		dev_err(chip->dev,			"invalid count value %x %zx \n", count, bufsiz);		return -E2BIG;	}	down(&chip->tpm_mutex);	if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) {		dev_err(chip->dev,			"tpm_transmit: tpm_send: error %zd\n", rc);		goto out;	}	if (chip->vendor.irq)		goto out_recv;	stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);	do {		u8 status = chip->vendor.status(chip);		if ((status & chip->vendor.req_complete_mask) ==		    chip->vendor.req_complete_val)			goto out_recv;		if ((status == chip->vendor.req_canceled)) {			dev_err(chip->dev, "Operation Canceled\n");			rc = -ECANCELED;			goto out;		}		msleep(TPM_TIMEOUT);	/* CHECK */		rmb();	} while (time_before(jiffies, stop));	chip->vendor.cancel(chip);	dev_err(chip->dev, "Operation Timed out\n");	rc = -ETIME;	goto out;out_recv:	rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz);	if (rc < 0)		dev_err(chip->dev,			"tpm_transmit: tpm_recv: error %zd\n", rc);out:	up(&chip->tpm_mutex);	return rc;}#define TPM_DIGEST_SIZE 20#define TPM_ERROR_SIZE 10#define TPM_RET_CODE_IDX 6#define TPM_GET_CAP_RET_SIZE_IDX 10#define TPM_GET_CAP_RET_UINT32_1_IDX 14#define TPM_GET_CAP_RET_UINT32_2_IDX 18#define TPM_GET_CAP_RET_UINT32_3_IDX 22#define TPM_GET_CAP_RET_UINT32_4_IDX 26#define TPM_GET_CAP_PERM_DISABLE_IDX 16#define TPM_GET_CAP_PERM_INACTIVE_IDX 18#define TPM_GET_CAP_RET_BOOL_1_IDX 14#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16#define TPM_CAP_IDX 13#define TPM_CAP_SUBCAP_IDX 21enum tpm_capabilities {	TPM_CAP_FLAG = 4,	TPM_CAP_PROP = 5,};enum tpm_sub_capabilities {	TPM_CAP_PROP_PCR = 0x1,	TPM_CAP_PROP_MANUFACTURER = 0x3,	TPM_CAP_FLAG_PERM = 0x8,	TPM_CAP_FLAG_VOL = 0x9,	TPM_CAP_PROP_OWNER = 0x11,	TPM_CAP_PROP_TIS_TIMEOUT = 0x15,	TPM_CAP_PROP_TIS_DURATION = 0x20,};/* * This is a semi generic GetCapability command for use * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG * and their associated sub_capabilities. */static const u8 tpm_cap[] = {	0, 193,			/* TPM_TAG_RQU_COMMAND */	0, 0, 0, 22,		/* length */	0, 0, 0, 101,		/* TPM_ORD_GetCapability */	0, 0, 0, 0,		/* TPM_CAP_<TYPE> */	0, 0, 0, 4,		/* TPM_CAP_SUB_<TYPE> size */	0, 0, 1, 0		/* TPM_CAP_SUB_<TYPE> */};static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,			    char *desc){	int err;	len = tpm_transmit(chip, data, len);	if (len <  0)		return len;	if (len == TPM_ERROR_SIZE) {		err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));		dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);		return err;	}	return 0;}void tpm_gen_interrupt(struct tpm_chip *chip){	u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];	ssize_t rc;	memcpy(data, tpm_cap, sizeof(tpm_cap));	data[TPM_CAP_IDX] = TPM_CAP_PROP;	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;	rc = transmit_cmd(chip, data, sizeof(data),			"attempting to determine the timeouts");}EXPORT_SYMBOL_GPL(tpm_gen_interrupt);void tpm_get_timeouts(struct tpm_chip *chip){	u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];	ssize_t rc;	u32 timeout;	memcpy(data, tpm_cap, sizeof(tpm_cap));	data[TPM_CAP_IDX] = TPM_CAP_PROP;	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;	rc = transmit_cmd(chip, data, sizeof(data),			"attempting to determine the timeouts");	if (rc)		goto duration;	if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))	    != 4 * sizeof(u32))		goto duration;	/* Don't overwrite default if value is 0 */	timeout =	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));	if (timeout)		chip->vendor.timeout_a = msecs_to_jiffies(timeout);	timeout =	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));	if (timeout)		chip->vendor.timeout_b = msecs_to_jiffies(timeout);	timeout =	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));	if (timeout)		chip->vendor.timeout_c = msecs_to_jiffies(timeout);	timeout =	    be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));	if (timeout)		chip->vendor.timeout_d = msecs_to_jiffies(timeout);duration:	memcpy(data, tpm_cap, sizeof(tpm_cap));	data[TPM_CAP_IDX] = TPM_CAP_PROP;	data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;	rc = transmit_cmd(chip, data, sizeof(data),			"attempting to determine the durations");	if (rc)		return;	if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))	    != 3 * sizeof(u32))		return;	chip->vendor.duration[TPM_SHORT] =	    msecs_to_jiffies(be32_to_cpu			     (*((__be32 *) (data +					    TPM_GET_CAP_RET_UINT32_1_IDX))));	chip->vendor.duration[TPM_MEDIUM] =	    msecs_to_jiffies(be32_to_cpu			     (*((__be32 *) (data +					    TPM_GET_CAP_RET_UINT32_2_IDX))));	chip->vendor.duration[TPM_LONG] =	    msecs_to_jiffies(be32_to_cpu			     (*((__be32 *) (data +					    TPM_GET_CAP_RET_UINT32_3_IDX))));}EXPORT_SYMBOL_GPL(tpm_get_timeouts);void tpm_continue_selftest(struct tpm_chip *chip){	u8 data[] = {		0, 193,			/* TPM_TAG_RQU_COMMAND */		0, 0, 0, 10,		/* length */		0, 0, 0, 83,		/* TPM_ORD_GetCapability */	};	tpm_transmit(chip, data, sizeof(data));}EXPORT_SYMBOL_GPL(tpm_continue_selftest);ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,			char *buf){	u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)];

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?