2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
*
|
2007-08-23 05:01:04 +08:00
|
|
|
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/fs.h>
|
2007-05-08 15:32:02 +08:00
|
|
|
#include <linux/mutex.h>
|
2006-10-19 01:55:46 +08:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/miscdevice.h>
|
2005-11-01 23:14:05 +08:00
|
|
|
#include <linux/platform_device.h>
|
2006-03-25 19:07:35 +08:00
|
|
|
#include <linux/io.h>
|
2009-02-03 01:23:44 +08:00
|
|
|
#include <linux/tpm.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-06-08 02:47:14 +08:00
|
|
|
enum tpm_const {
|
|
|
|
TPM_MINOR = 224, /* officially assigned */
|
|
|
|
TPM_BUFSIZE = 4096,
|
|
|
|
TPM_NUM_DEVICES = 256,
|
2013-03-18 05:56:39 +08:00
|
|
|
TPM_RETRY = 50, /* 5 seconds */
|
2012-06-08 02:47:14 +08:00
|
|
|
};
|
|
|
|
|
2005-06-24 13:01:48 +08:00
|
|
|
enum tpm_timeout {
|
|
|
|
TPM_TIMEOUT = 5, /* msecs */
|
2013-03-18 05:56:39 +08:00
|
|
|
TPM_TIMEOUT_RETRY = 100 /* msecs */
|
2005-06-24 13:01:48 +08:00
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* TPM addresses */
|
2005-06-24 13:01:48 +08:00
|
|
|
enum tpm_addr {
|
2005-06-26 05:55:39 +08:00
|
|
|
TPM_SUPERIO_ADDR = 0x2E,
|
2005-06-24 13:01:48 +08:00
|
|
|
TPM_ADDR = 0x4E,
|
|
|
|
};
|
|
|
|
|
2013-11-27 04:30:41 +08:00
|
|
|
/* Indexes the duration array */
|
|
|
|
enum tpm_duration {
|
|
|
|
TPM_SHORT = 0,
|
|
|
|
TPM_MEDIUM = 1,
|
|
|
|
TPM_LONG = 2,
|
|
|
|
TPM_UNDEFINED,
|
|
|
|
};
|
|
|
|
|
2013-03-18 05:56:39 +08:00
|
|
|
#define TPM_WARN_RETRY 0x800
|
2011-11-12 01:57:04 +08:00
|
|
|
#define TPM_WARN_DOING_SELFTEST 0x802
|
2012-01-18 11:07:30 +08:00
|
|
|
#define TPM_ERR_DEACTIVATED 0x6
|
|
|
|
#define TPM_ERR_DISABLED 0x7
|
2012-11-22 04:54:33 +08:00
|
|
|
#define TPM_ERR_INVALID_POSTINIT 38
|
2012-01-18 11:07:30 +08:00
|
|
|
|
2011-11-02 03:00:52 +08:00
|
|
|
#define TPM_HEADER_SIZE 10
|
2005-04-17 06:20:36 +08:00
|
|
|
struct tpm_chip;
|
|
|
|
|
|
|
|
struct tpm_vendor_specific {
|
2013-11-27 04:30:43 +08:00
|
|
|
u8 req_complete_mask;
|
|
|
|
u8 req_complete_val;
|
2013-01-23 03:52:35 +08:00
|
|
|
bool (*req_canceled)(struct tpm_chip *chip, u8 status);
|
2005-11-14 08:07:41 +08:00
|
|
|
void __iomem *iobase; /* ioremapped address */
|
|
|
|
unsigned long base; /* TPM base address */
|
|
|
|
|
2006-04-22 17:38:03 +08:00
|
|
|
int irq;
|
2011-03-31 00:13:32 +08:00
|
|
|
int probed_irq;
|
2006-04-22 17:38:03 +08:00
|
|
|
|
2005-11-14 08:07:41 +08:00
|
|
|
int region_size;
|
|
|
|
int have_region;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int (*recv) (struct tpm_chip *, u8 *, size_t);
|
|
|
|
int (*send) (struct tpm_chip *, u8 *, size_t);
|
|
|
|
void (*cancel) (struct tpm_chip *);
|
2005-10-31 07:03:23 +08:00
|
|
|
u8 (*status) (struct tpm_chip *);
|
2008-02-06 17:37:02 +08:00
|
|
|
void (*release) (struct device *);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct miscdevice miscdev;
|
2006-04-22 17:38:03 +08:00
|
|
|
struct list_head list;
|
|
|
|
int locality;
|
2006-04-22 17:38:19 +08:00
|
|
|
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
|
2011-03-31 00:13:28 +08:00
|
|
|
bool timeout_adjusted;
|
2006-04-22 17:38:19 +08:00
|
|
|
unsigned long duration[3]; /* jiffies */
|
2011-03-31 00:13:25 +08:00
|
|
|
bool duration_adjusted;
|
2012-12-06 01:36:20 +08:00
|
|
|
void *priv;
|
2006-04-22 17:38:03 +08:00
|
|
|
|
|
|
|
wait_queue_head_t read_queue;
|
|
|
|
wait_queue_head_t int_queue;
|
2013-01-23 03:50:07 +08:00
|
|
|
|
|
|
|
u16 manufacturer_id;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2012-12-06 01:36:20 +08:00
|
|
|
#define TPM_VPRIV(c) (c)->vendor.priv
|
|
|
|
|
2012-01-21 01:58:49 +08:00
|
|
|
#define TPM_VID_INTEL 0x8086
|
2013-01-23 03:52:35 +08:00
|
|
|
#define TPM_VID_WINBOND 0x1050
|
|
|
|
#define TPM_VID_STM 0x104A
|
2012-01-21 01:58:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct tpm_chip {
|
2005-10-31 07:03:24 +08:00
|
|
|
struct device *dev; /* Device stuff */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int dev_num; /* /dev/tpm# */
|
2013-09-23 04:19:18 +08:00
|
|
|
char devname[7];
|
2008-10-11 06:04:02 +08:00
|
|
|
unsigned long is_open; /* only one allowed */
|
2005-04-17 06:20:36 +08:00
|
|
|
int time_expired;
|
|
|
|
|
|
|
|
/* Data passed to and from the tpm via the read/write calls */
|
|
|
|
u8 *data_buffer;
|
|
|
|
atomic_t data_pending;
|
2007-05-08 15:32:02 +08:00
|
|
|
struct mutex buffer_mutex;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct timer_list user_read_timer; /* user needs to claim result */
|
2005-11-14 08:07:43 +08:00
|
|
|
struct work_struct work;
|
2007-05-08 15:32:02 +08:00
|
|
|
struct mutex tpm_mutex; /* tpm is processing */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-04-22 17:37:15 +08:00
|
|
|
struct tpm_vendor_specific vendor;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-08 17:03:15 +08:00
|
|
|
struct dentry **bios_dir;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct list_head list;
|
2008-02-06 17:37:02 +08:00
|
|
|
void (*release) (struct device *);
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2006-04-22 17:38:03 +08:00
|
|
|
#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
|
|
|
|
|
2010-11-24 06:50:32 +08:00
|
|
|
static inline void tpm_chip_put(struct tpm_chip *chip)
|
|
|
|
{
|
|
|
|
module_put(chip->dev->driver->owner);
|
|
|
|
}
|
|
|
|
|
2005-06-26 05:55:39 +08:00
|
|
|
static inline int tpm_read_index(int base, int index)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-06-26 05:55:39 +08:00
|
|
|
outb(index, base);
|
|
|
|
return inb(base+1) & 0xFF;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-06-26 05:55:39 +08:00
|
|
|
static inline void tpm_write_index(int base, int index, int value)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-06-26 05:55:39 +08:00
|
|
|
outb(index, base);
|
|
|
|
outb(value & 0xFF, base+1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2009-02-03 01:23:43 +08:00
|
|
|
struct tpm_input_header {
|
|
|
|
__be16 tag;
|
|
|
|
__be32 length;
|
|
|
|
__be32 ordinal;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct tpm_output_header {
|
|
|
|
__be16 tag;
|
|
|
|
__be32 length;
|
|
|
|
__be32 return_code;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
2013-11-27 04:30:41 +08:00
|
|
|
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
|
|
|
|
|
2009-02-03 01:23:43 +08:00
|
|
|
struct stclear_flags_t {
|
|
|
|
__be16 tag;
|
|
|
|
u8 deactivated;
|
|
|
|
u8 disableForceClear;
|
|
|
|
u8 physicalPresence;
|
|
|
|
u8 physicalPresenceLock;
|
|
|
|
u8 bGlobalLock;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct tpm_version_t {
|
|
|
|
u8 Major;
|
|
|
|
u8 Minor;
|
|
|
|
u8 revMajor;
|
|
|
|
u8 revMinor;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct tpm_version_1_2_t {
|
|
|
|
__be16 tag;
|
|
|
|
u8 Major;
|
|
|
|
u8 Minor;
|
|
|
|
u8 revMajor;
|
|
|
|
u8 revMinor;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct timeout_t {
|
|
|
|
__be32 a;
|
|
|
|
__be32 b;
|
|
|
|
__be32 c;
|
|
|
|
__be32 d;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct duration_t {
|
|
|
|
__be32 tpm_short;
|
|
|
|
__be32 tpm_medium;
|
|
|
|
__be32 tpm_long;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct permanent_flags_t {
|
|
|
|
__be16 tag;
|
|
|
|
u8 disable;
|
|
|
|
u8 ownership;
|
|
|
|
u8 deactivated;
|
|
|
|
u8 readPubek;
|
|
|
|
u8 disableOwnerClear;
|
|
|
|
u8 allowMaintenance;
|
|
|
|
u8 physicalPresenceLifetimeLock;
|
|
|
|
u8 physicalPresenceHWEnable;
|
|
|
|
u8 physicalPresenceCMDEnable;
|
|
|
|
u8 CEKPUsed;
|
|
|
|
u8 TPMpost;
|
|
|
|
u8 TPMpostLock;
|
|
|
|
u8 FIPS;
|
|
|
|
u8 operator;
|
|
|
|
u8 enableRevokeEK;
|
|
|
|
u8 nvLocked;
|
|
|
|
u8 readSRKPub;
|
|
|
|
u8 tpmEstablished;
|
|
|
|
u8 maintenanceDone;
|
|
|
|
u8 disableFullDALogicInfo;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
typedef union {
|
|
|
|
struct permanent_flags_t perm_flags;
|
|
|
|
struct stclear_flags_t stclear_flags;
|
|
|
|
bool owned;
|
|
|
|
__be32 num_pcrs;
|
|
|
|
struct tpm_version_t tpm_version;
|
|
|
|
struct tpm_version_1_2_t tpm_version_1_2;
|
|
|
|
__be32 manufacturer_id;
|
|
|
|
struct timeout_t timeout;
|
|
|
|
struct duration_t duration;
|
|
|
|
} cap_t;
|
|
|
|
|
2013-11-27 04:30:41 +08:00
|
|
|
enum tpm_capabilities {
|
|
|
|
TPM_CAP_FLAG = cpu_to_be32(4),
|
|
|
|
TPM_CAP_PROP = cpu_to_be32(5),
|
|
|
|
CAP_VERSION_1_1 = cpu_to_be32(0x06),
|
|
|
|
CAP_VERSION_1_2 = cpu_to_be32(0x1A)
|
|
|
|
};
|
|
|
|
|
|
|
|
enum tpm_sub_capabilities {
|
|
|
|
TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
|
|
|
|
TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
|
|
|
|
TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
|
|
|
|
TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
|
|
|
|
TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
|
|
|
|
TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
|
|
|
|
TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2009-02-03 01:23:43 +08:00
|
|
|
struct tpm_getcap_params_in {
|
|
|
|
__be32 cap;
|
|
|
|
__be32 subcap_size;
|
|
|
|
__be32 subcap;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct tpm_getcap_params_out {
|
|
|
|
__be32 cap_size;
|
|
|
|
cap_t cap;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
struct tpm_readpubek_params_out {
|
|
|
|
u8 algorithm[4];
|
|
|
|
u8 encscheme[2];
|
|
|
|
u8 sigscheme[2];
|
2010-06-15 00:58:22 +08:00
|
|
|
__be32 paramsize;
|
2009-02-03 01:23:43 +08:00
|
|
|
u8 parameters[12]; /*assuming RSA*/
|
|
|
|
__be32 keysize;
|
|
|
|
u8 modulus[256];
|
|
|
|
u8 checksum[20];
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
typedef union {
|
|
|
|
struct tpm_input_header in;
|
|
|
|
struct tpm_output_header out;
|
|
|
|
} tpm_cmd_header;
|
|
|
|
|
2009-02-03 01:23:44 +08:00
|
|
|
struct tpm_pcrread_out {
|
|
|
|
u8 pcr_result[TPM_DIGEST_SIZE];
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:44 +08:00
|
|
|
|
|
|
|
struct tpm_pcrread_in {
|
|
|
|
__be32 pcr_idx;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:44 +08:00
|
|
|
|
|
|
|
struct tpm_pcrextend_in {
|
|
|
|
__be32 pcr_idx;
|
|
|
|
u8 hash[TPM_DIGEST_SIZE];
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:44 +08:00
|
|
|
|
2012-06-08 02:47:14 +08:00
|
|
|
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
|
|
|
|
* bytes, but 128 is still a relatively large number of random bytes and
|
|
|
|
* anything much bigger causes users of struct tpm_cmd_t to start getting
|
|
|
|
* compiler warnings about stack frame size. */
|
|
|
|
#define TPM_MAX_RNG_DATA 128
|
|
|
|
|
|
|
|
struct tpm_getrandom_out {
|
|
|
|
__be32 rng_data_len;
|
|
|
|
u8 rng_data[TPM_MAX_RNG_DATA];
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2012-06-08 02:47:14 +08:00
|
|
|
|
|
|
|
struct tpm_getrandom_in {
|
|
|
|
__be32 num_bytes;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2012-06-08 02:47:14 +08:00
|
|
|
|
2012-11-22 04:54:33 +08:00
|
|
|
struct tpm_startup_in {
|
|
|
|
__be16 startup_type;
|
|
|
|
} __packed;
|
|
|
|
|
2009-02-03 01:23:43 +08:00
|
|
|
typedef union {
|
|
|
|
struct tpm_getcap_params_out getcap_out;
|
|
|
|
struct tpm_readpubek_params_out readpubek_out;
|
|
|
|
u8 readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
|
|
|
|
struct tpm_getcap_params_in getcap_in;
|
2009-02-03 01:23:44 +08:00
|
|
|
struct tpm_pcrread_in pcrread_in;
|
|
|
|
struct tpm_pcrread_out pcrread_out;
|
|
|
|
struct tpm_pcrextend_in pcrextend_in;
|
2012-06-08 02:47:14 +08:00
|
|
|
struct tpm_getrandom_in getrandom_in;
|
|
|
|
struct tpm_getrandom_out getrandom_out;
|
2012-11-22 04:54:33 +08:00
|
|
|
struct tpm_startup_in startup_in;
|
2009-02-03 01:23:43 +08:00
|
|
|
} tpm_cmd_params;
|
|
|
|
|
|
|
|
struct tpm_cmd_t {
|
|
|
|
tpm_cmd_header header;
|
|
|
|
tpm_cmd_params params;
|
2012-11-22 04:56:45 +08:00
|
|
|
} __packed;
|
2009-02-03 01:23:43 +08:00
|
|
|
|
|
|
|
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-11-27 04:30:40 +08:00
|
|
|
ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
|
|
|
|
size_t bufsiz);
|
2011-11-12 01:57:02 +08:00
|
|
|
extern int tpm_get_timeouts(struct tpm_chip *);
|
2006-04-22 17:37:50 +08:00
|
|
|
extern void tpm_gen_interrupt(struct tpm_chip *);
|
2011-11-12 01:57:04 +08:00
|
|
|
extern int tpm_do_selftest(struct tpm_chip *);
|
2006-04-22 17:37:38 +08:00
|
|
|
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
|
2006-04-22 17:37:26 +08:00
|
|
|
extern struct tpm_chip* tpm_register_hardware(struct device *,
|
2013-11-27 04:30:43 +08:00
|
|
|
const struct tpm_class_ops *ops);
|
2008-10-11 06:04:39 +08:00
|
|
|
extern void tpm_dev_vendor_release(struct tpm_chip *);
|
2005-10-31 07:03:24 +08:00
|
|
|
extern void tpm_remove_hardware(struct device *);
|
2012-07-07 01:09:01 +08:00
|
|
|
extern int tpm_pm_suspend(struct device *);
|
2005-10-31 07:03:25 +08:00
|
|
|
extern int tpm_pm_resume(struct device *);
|
2011-09-17 01:39:40 +08:00
|
|
|
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
2013-01-23 03:57:53 +08:00
|
|
|
wait_queue_head_t *, bool);
|
2012-08-22 18:47:22 +08:00
|
|
|
|
2013-11-27 04:30:40 +08:00
|
|
|
int tpm_dev_add_device(struct tpm_chip *chip);
|
|
|
|
void tpm_dev_del_device(struct tpm_chip *chip);
|
2013-11-27 04:30:42 +08:00
|
|
|
int tpm_sysfs_add_device(struct tpm_chip *chip);
|
|
|
|
void tpm_sysfs_del_device(struct tpm_chip *chip);
|
2013-11-27 04:30:40 +08:00
|
|
|
|
2013-11-27 04:30:41 +08:00
|
|
|
int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
|
|
|
|
|
2012-08-22 18:47:22 +08:00
|
|
|
#ifdef CONFIG_ACPI
|
2012-10-09 17:35:22 +08:00
|
|
|
extern int tpm_add_ppi(struct kobject *);
|
|
|
|
extern void tpm_remove_ppi(struct kobject *);
|
2012-08-22 18:47:22 +08:00
|
|
|
#else
|
2012-10-09 17:35:22 +08:00
|
|
|
static inline int tpm_add_ppi(struct kobject *parent)
|
2012-08-22 18:47:22 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-09 17:35:22 +08:00
|
|
|
|
|
|
|
static inline void tpm_remove_ppi(struct kobject *parent)
|
|
|
|
{
|
|
|
|
}
|
2012-08-22 18:47:22 +08:00
|
|
|
#endif
|