2005-04-16 22:20:36 +00: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-22 21:01:04 +00:00
|
|
|
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
2005-04-16 22:20:36 +00: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 07:32:02 +00:00
|
|
|
#include <linux/mutex.h>
|
2006-10-18 17:55:46 +00:00
|
|
|
#include <linux/sched.h>
|
2005-11-01 15:14:05 +00:00
|
|
|
#include <linux/platform_device.h>
|
2006-03-25 11:07:35 +00:00
|
|
|
#include <linux/io.h>
|
2009-02-02 17:23:44 +00:00
|
|
|
#include <linux/tpm.h>
|
2014-12-12 19:46:35 +00:00
|
|
|
#include <linux/acpi.h>
|
2014-12-12 19:46:37 +00:00
|
|
|
#include <linux/cdev.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-06-07 18:47:14 +00:00
|
|
|
enum tpm_const {
|
|
|
|
TPM_MINOR = 224, /* officially assigned */
|
|
|
|
TPM_BUFSIZE = 4096,
|
|
|
|
TPM_NUM_DEVICES = 256,
|
2013-03-17 21:56:39 +00:00
|
|
|
TPM_RETRY = 50, /* 5 seconds */
|
2012-06-07 18:47:14 +00:00
|
|
|
};
|
|
|
|
|
2005-06-24 05:01:48 +00:00
|
|
|
enum tpm_timeout {
|
|
|
|
TPM_TIMEOUT = 5, /* msecs */
|
2013-03-17 21:56:39 +00:00
|
|
|
TPM_TIMEOUT_RETRY = 100 /* msecs */
|
2005-06-24 05:01:48 +00:00
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* TPM addresses */
|
2005-06-24 05:01:48 +00:00
|
|
|
enum tpm_addr {
|
2005-06-25 21:55:39 +00:00
|
|
|
TPM_SUPERIO_ADDR = 0x2E,
|
2005-06-24 05:01:48 +00:00
|
|
|
TPM_ADDR = 0x4E,
|
|
|
|
};
|
|
|
|
|
2013-11-26 20:30:41 +00:00
|
|
|
/* Indexes the duration array */
|
|
|
|
enum tpm_duration {
|
|
|
|
TPM_SHORT = 0,
|
|
|
|
TPM_MEDIUM = 1,
|
|
|
|
TPM_LONG = 2,
|
|
|
|
TPM_UNDEFINED,
|
|
|
|
};
|
|
|
|
|
2013-03-17 21:56:39 +00:00
|
|
|
#define TPM_WARN_RETRY 0x800
|
2011-11-11 17:57:04 +00:00
|
|
|
#define TPM_WARN_DOING_SELFTEST 0x802
|
2012-01-18 03:07:30 +00:00
|
|
|
#define TPM_ERR_DEACTIVATED 0x6
|
|
|
|
#define TPM_ERR_DISABLED 0x7
|
2012-11-21 20:54:33 +00:00
|
|
|
#define TPM_ERR_INVALID_POSTINIT 38
|
2012-01-18 03:07:30 +00:00
|
|
|
|
2011-11-01 19:00:52 +00:00
|
|
|
#define TPM_HEADER_SIZE 10
|
2005-04-16 22:20:36 +00:00
|
|
|
struct tpm_chip;
|
|
|
|
|
|
|
|
struct tpm_vendor_specific {
|
2005-11-14 00:07:41 +00:00
|
|
|
void __iomem *iobase; /* ioremapped address */
|
|
|
|
unsigned long base; /* TPM base address */
|
|
|
|
|
2006-04-22 09:38:03 +00:00
|
|
|
int irq;
|
2011-03-30 16:13:32 +00:00
|
|
|
int probed_irq;
|
2006-04-22 09:38:03 +00:00
|
|
|
|
2005-11-14 00:07:41 +00:00
|
|
|
int region_size;
|
|
|
|
int have_region;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-22 09:38:03 +00:00
|
|
|
struct list_head list;
|
|
|
|
int locality;
|
2006-04-22 09:38:19 +00:00
|
|
|
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
|
2011-03-30 16:13:28 +00:00
|
|
|
bool timeout_adjusted;
|
2006-04-22 09:38:19 +00:00
|
|
|
unsigned long duration[3]; /* jiffies */
|
2011-03-30 16:13:25 +00:00
|
|
|
bool duration_adjusted;
|
2012-12-05 17:36:20 +00:00
|
|
|
void *priv;
|
2006-04-22 09:38:03 +00:00
|
|
|
|
|
|
|
wait_queue_head_t read_queue;
|
|
|
|
wait_queue_head_t int_queue;
|
2013-01-22 19:50:07 +00:00
|
|
|
|
|
|
|
u16 manufacturer_id;
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2012-12-05 17:36:20 +00:00
|
|
|
#define TPM_VPRIV(c) (c)->vendor.priv
|
|
|
|
|
2012-01-20 17:58:49 +00:00
|
|
|
#define TPM_VID_INTEL 0x8086
|
2013-01-22 19:52:35 +00:00
|
|
|
#define TPM_VID_WINBOND 0x1050
|
|
|
|
#define TPM_VID_STM 0x104A
|
2012-01-20 17:58:49 +00:00
|
|
|
|
2014-12-12 19:46:35 +00:00
|
|
|
#define TPM_PPI_VERSION_LEN 3
|
|
|
|
|
2014-12-12 19:46:34 +00:00
|
|
|
enum tpm_chip_flags {
|
|
|
|
TPM_CHIP_FLAG_REGISTERED = BIT(0),
|
2014-12-12 19:46:35 +00:00
|
|
|
TPM_CHIP_FLAG_PPI = BIT(1),
|
2014-12-12 19:46:34 +00:00
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
struct tpm_chip {
|
2014-12-12 19:46:36 +00:00
|
|
|
struct device *pdev; /* Device stuff */
|
2014-12-12 19:46:37 +00:00
|
|
|
struct device dev;
|
|
|
|
struct cdev cdev;
|
|
|
|
|
2013-11-26 20:30:44 +00:00
|
|
|
const struct tpm_class_ops *ops;
|
2014-12-12 19:46:34 +00:00
|
|
|
unsigned int flags;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
int dev_num; /* /dev/tpm# */
|
2013-09-22 20:19:18 +00:00
|
|
|
char devname[7];
|
2008-10-10 22:04:02 +00:00
|
|
|
unsigned long is_open; /* only one allowed */
|
2005-04-16 22:20:36 +00:00
|
|
|
int time_expired;
|
|
|
|
|
2007-05-08 07:32:02 +00:00
|
|
|
struct mutex tpm_mutex; /* tpm is processing */
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-22 09:37:15 +00:00
|
|
|
struct tpm_vendor_specific vendor;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-01-08 09:03:15 +00:00
|
|
|
struct dentry **bios_dir;
|
|
|
|
|
2014-12-12 19:46:35 +00:00
|
|
|
#ifdef CONFIG_ACPI
|
|
|
|
acpi_handle acpi_dev_handle;
|
|
|
|
char ppi_version[TPM_PPI_VERSION_LEN + 1];
|
|
|
|
#endif /* CONFIG_ACPI */
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
struct list_head list;
|
|
|
|
};
|
|
|
|
|
2006-04-22 09:38:03 +00:00
|
|
|
#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
|
|
|
|
|
2010-11-23 22:50:32 +00:00
|
|
|
static inline void tpm_chip_put(struct tpm_chip *chip)
|
|
|
|
{
|
2014-12-12 19:46:36 +00:00
|
|
|
module_put(chip->pdev->driver->owner);
|
2010-11-23 22:50:32 +00:00
|
|
|
}
|
|
|
|
|
2005-06-25 21:55:39 +00:00
|
|
|
static inline int tpm_read_index(int base, int index)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-06-25 21:55:39 +00:00
|
|
|
outb(index, base);
|
|
|
|
return inb(base+1) & 0xFF;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-06-25 21:55:39 +00:00
|
|
|
static inline void tpm_write_index(int base, int index, int value)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-06-25 21:55:39 +00:00
|
|
|
outb(index, base);
|
|
|
|
outb(value & 0xFF, base+1);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-02-02 17:23:43 +00:00
|
|
|
struct tpm_input_header {
|
|
|
|
__be16 tag;
|
|
|
|
__be32 length;
|
|
|
|
__be32 ordinal;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct tpm_output_header {
|
|
|
|
__be16 tag;
|
|
|
|
__be32 length;
|
|
|
|
__be32 return_code;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
2013-11-26 20:30:41 +00:00
|
|
|
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
|
|
|
|
|
2009-02-02 17:23:43 +00:00
|
|
|
struct stclear_flags_t {
|
|
|
|
__be16 tag;
|
|
|
|
u8 deactivated;
|
|
|
|
u8 disableForceClear;
|
|
|
|
u8 physicalPresence;
|
|
|
|
u8 physicalPresenceLock;
|
|
|
|
u8 bGlobalLock;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct tpm_version_t {
|
|
|
|
u8 Major;
|
|
|
|
u8 Minor;
|
|
|
|
u8 revMajor;
|
|
|
|
u8 revMinor;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct tpm_version_1_2_t {
|
|
|
|
__be16 tag;
|
|
|
|
u8 Major;
|
|
|
|
u8 Minor;
|
|
|
|
u8 revMajor;
|
|
|
|
u8 revMinor;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct timeout_t {
|
|
|
|
__be32 a;
|
|
|
|
__be32 b;
|
|
|
|
__be32 c;
|
|
|
|
__be32 d;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct duration_t {
|
|
|
|
__be32 tpm_short;
|
|
|
|
__be32 tpm_medium;
|
|
|
|
__be32 tpm_long;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00: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-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00: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-26 20:30:41 +00: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-02 17:23:43 +00:00
|
|
|
struct tpm_getcap_params_in {
|
|
|
|
__be32 cap;
|
|
|
|
__be32 subcap_size;
|
|
|
|
__be32 subcap;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct tpm_getcap_params_out {
|
|
|
|
__be32 cap_size;
|
|
|
|
cap_t cap;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
struct tpm_readpubek_params_out {
|
|
|
|
u8 algorithm[4];
|
|
|
|
u8 encscheme[2];
|
|
|
|
u8 sigscheme[2];
|
2010-06-14 16:58:22 +00:00
|
|
|
__be32 paramsize;
|
2009-02-02 17:23:43 +00:00
|
|
|
u8 parameters[12]; /*assuming RSA*/
|
|
|
|
__be32 keysize;
|
|
|
|
u8 modulus[256];
|
|
|
|
u8 checksum[20];
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
|
|
|
typedef union {
|
|
|
|
struct tpm_input_header in;
|
|
|
|
struct tpm_output_header out;
|
|
|
|
} tpm_cmd_header;
|
|
|
|
|
2009-02-02 17:23:44 +00:00
|
|
|
struct tpm_pcrread_out {
|
|
|
|
u8 pcr_result[TPM_DIGEST_SIZE];
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:44 +00:00
|
|
|
|
|
|
|
struct tpm_pcrread_in {
|
|
|
|
__be32 pcr_idx;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:44 +00:00
|
|
|
|
|
|
|
struct tpm_pcrextend_in {
|
|
|
|
__be32 pcr_idx;
|
|
|
|
u8 hash[TPM_DIGEST_SIZE];
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:44 +00:00
|
|
|
|
2012-06-07 18:47:14 +00: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-21 20:56:45 +00:00
|
|
|
} __packed;
|
2012-06-07 18:47:14 +00:00
|
|
|
|
|
|
|
struct tpm_getrandom_in {
|
|
|
|
__be32 num_bytes;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2012-06-07 18:47:14 +00:00
|
|
|
|
2012-11-21 20:54:33 +00:00
|
|
|
struct tpm_startup_in {
|
|
|
|
__be16 startup_type;
|
|
|
|
} __packed;
|
|
|
|
|
2009-02-02 17:23:43 +00: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-02 17:23:44 +00:00
|
|
|
struct tpm_pcrread_in pcrread_in;
|
|
|
|
struct tpm_pcrread_out pcrread_out;
|
|
|
|
struct tpm_pcrextend_in pcrextend_in;
|
2012-06-07 18:47:14 +00:00
|
|
|
struct tpm_getrandom_in getrandom_in;
|
|
|
|
struct tpm_getrandom_out getrandom_out;
|
2012-11-21 20:54:33 +00:00
|
|
|
struct tpm_startup_in startup_in;
|
2009-02-02 17:23:43 +00:00
|
|
|
} tpm_cmd_params;
|
|
|
|
|
|
|
|
struct tpm_cmd_t {
|
|
|
|
tpm_cmd_header header;
|
|
|
|
tpm_cmd_params params;
|
2012-11-21 20:56:45 +00:00
|
|
|
} __packed;
|
2009-02-02 17:23:43 +00:00
|
|
|
|
2014-12-12 19:46:37 +00:00
|
|
|
extern struct class *tpm_class;
|
|
|
|
extern dev_t tpm_devt;
|
|
|
|
extern const struct file_operations tpm_fops;
|
|
|
|
|
2009-02-02 17:23:43 +00:00
|
|
|
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
|
2013-11-26 20:30:40 +00:00
|
|
|
ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
|
|
|
|
size_t bufsiz);
|
2014-12-12 19:46:33 +00:00
|
|
|
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
|
|
|
|
const char *desc);
|
2011-11-11 17:57:02 +00:00
|
|
|
extern int tpm_get_timeouts(struct tpm_chip *);
|
2006-04-22 09:37:50 +00:00
|
|
|
extern void tpm_gen_interrupt(struct tpm_chip *);
|
2011-11-11 17:57:04 +00:00
|
|
|
extern int tpm_do_selftest(struct tpm_chip *);
|
2006-04-22 09:37:38 +00:00
|
|
|
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
|
2012-07-06 17:09:01 +00:00
|
|
|
extern int tpm_pm_suspend(struct device *);
|
2005-10-30 23:03:25 +00:00
|
|
|
extern int tpm_pm_resume(struct device *);
|
2011-09-16 17:39:40 +00:00
|
|
|
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
2013-01-22 19:57:53 +00:00
|
|
|
wait_queue_head_t *, bool);
|
2012-08-22 10:47:22 +00:00
|
|
|
|
2014-12-12 19:46:34 +00:00
|
|
|
struct tpm_chip *tpm_chip_find_get(int chip_num);
|
|
|
|
extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
|
|
|
|
const struct tpm_class_ops *ops);
|
|
|
|
extern int tpm_chip_register(struct tpm_chip *chip);
|
|
|
|
extern void tpm_chip_unregister(struct tpm_chip *chip);
|
|
|
|
|
2013-11-26 20:30:42 +00:00
|
|
|
int tpm_sysfs_add_device(struct tpm_chip *chip);
|
|
|
|
void tpm_sysfs_del_device(struct tpm_chip *chip);
|
2013-11-26 20:30:40 +00:00
|
|
|
|
2013-11-26 20:30:41 +00:00
|
|
|
int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
|
|
|
|
|
2012-08-22 10:47:22 +00:00
|
|
|
#ifdef CONFIG_ACPI
|
2014-12-12 19:46:35 +00:00
|
|
|
extern int tpm_add_ppi(struct tpm_chip *chip);
|
|
|
|
extern void tpm_remove_ppi(struct tpm_chip *chip);
|
2012-08-22 10:47:22 +00:00
|
|
|
#else
|
2014-12-12 19:46:35 +00:00
|
|
|
static inline int tpm_add_ppi(struct tpm_chip *chip)
|
2012-08-22 10:47:22 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-09 09:35:22 +00:00
|
|
|
|
2014-12-12 19:46:35 +00:00
|
|
|
static inline void tpm_remove_ppi(struct tpm_chip *chip)
|
2012-10-09 09:35:22 +00:00
|
|
|
{
|
|
|
|
}
|
2012-08-22 10:47:22 +00:00
|
|
|
#endif
|