Merge branch 'next-tpm' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull TPM updates from James Morris: "This release contains only bug fixes. There are no new major features added" * 'next-tpm' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: tpm: fix intermittent failure with self tests tpm: add retry logic tpm: self test failure should not cause suspend to fail tpm2: add longer timeouts for creation commands. tpm_crb: use __le64 annotated variable for response buffer address tpm: fix buffer type in tpm_transmit_cmd tpm: tpm-interface: fix tpm_transmit/_cmd kdoc tpm: cmd_ready command can be issued only after granting locality
This commit is contained in:
commit
4b3f1a1515
6 changed files with 259 additions and 130 deletions
|
@ -369,20 +369,40 @@ static int tpm_validate_command(struct tpm_chip *chip,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int tpm_request_locality(struct tpm_chip *chip)
|
||||||
* tmp_transmit - Internal kernel interface to transmit TPM commands.
|
{
|
||||||
*
|
int rc;
|
||||||
* @chip: TPM chip to use
|
|
||||||
* @buf: TPM command buffer
|
if (!chip->ops->request_locality)
|
||||||
* @bufsiz: length of the TPM command buffer
|
return 0;
|
||||||
* @flags: tpm transmit flags - bitmap
|
|
||||||
*
|
rc = chip->ops->request_locality(chip, 0);
|
||||||
* Return:
|
if (rc < 0)
|
||||||
* 0 when the operation is successful.
|
return rc;
|
||||||
* A negative number for system errors (errno).
|
|
||||||
*/
|
chip->locality = rc;
|
||||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
|
||||||
u8 *buf, size_t bufsiz, unsigned int flags)
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tpm_relinquish_locality(struct tpm_chip *chip)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!chip->ops->relinquish_locality)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rc = chip->ops->relinquish_locality(chip, chip->locality);
|
||||||
|
if (rc)
|
||||||
|
dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
|
||||||
|
|
||||||
|
chip->locality = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t tpm_try_transmit(struct tpm_chip *chip,
|
||||||
|
struct tpm_space *space,
|
||||||
|
u8 *buf, size_t bufsiz,
|
||||||
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct tpm_output_header *header = (void *)buf;
|
struct tpm_output_header *header = (void *)buf;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -422,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||||
mutex_lock(&chip->tpm_mutex);
|
mutex_lock(&chip->tpm_mutex);
|
||||||
|
|
||||||
if (chip->dev.parent)
|
|
||||||
pm_runtime_get_sync(chip->dev.parent);
|
|
||||||
|
|
||||||
if (chip->ops->clk_enable != NULL)
|
if (chip->ops->clk_enable != NULL)
|
||||||
chip->ops->clk_enable(chip, true);
|
chip->ops->clk_enable(chip, true);
|
||||||
|
@ -431,19 +449,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
/* Store the decision as chip->locality will be changed. */
|
/* Store the decision as chip->locality will be changed. */
|
||||||
need_locality = chip->locality == -1;
|
need_locality = chip->locality == -1;
|
||||||
|
|
||||||
if (!(flags & TPM_TRANSMIT_RAW) &&
|
if (!(flags & TPM_TRANSMIT_RAW) && need_locality) {
|
||||||
need_locality && chip->ops->request_locality) {
|
rc = tpm_request_locality(chip);
|
||||||
rc = chip->ops->request_locality(chip, 0);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out_no_locality;
|
goto out_no_locality;
|
||||||
chip->locality = rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chip->dev.parent)
|
||||||
|
pm_runtime_get_sync(chip->dev.parent);
|
||||||
|
|
||||||
rc = tpm2_prepare_space(chip, space, ordinal, buf);
|
rc = tpm2_prepare_space(chip, space, ordinal, buf);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
rc = chip->ops->send(chip, (u8 *) buf, count);
|
rc = chip->ops->send(chip, buf, count);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
if (rc != -EPIPE)
|
if (rc != -EPIPE)
|
||||||
dev_err(&chip->dev,
|
dev_err(&chip->dev,
|
||||||
|
@ -480,7 +499,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
out_recv:
|
out_recv:
|
||||||
len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
len = chip->ops->recv(chip, buf, bufsiz);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
rc = len;
|
rc = len;
|
||||||
dev_err(&chip->dev,
|
dev_err(&chip->dev,
|
||||||
|
@ -499,27 +518,95 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
|
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (need_locality && chip->ops->relinquish_locality) {
|
if (chip->dev.parent)
|
||||||
chip->ops->relinquish_locality(chip, chip->locality);
|
pm_runtime_put_sync(chip->dev.parent);
|
||||||
chip->locality = -1;
|
|
||||||
}
|
if (need_locality)
|
||||||
|
tpm_relinquish_locality(chip);
|
||||||
|
|
||||||
out_no_locality:
|
out_no_locality:
|
||||||
if (chip->ops->clk_enable != NULL)
|
if (chip->ops->clk_enable != NULL)
|
||||||
chip->ops->clk_enable(chip, false);
|
chip->ops->clk_enable(chip, false);
|
||||||
|
|
||||||
if (chip->dev.parent)
|
|
||||||
pm_runtime_put_sync(chip->dev.parent);
|
|
||||||
|
|
||||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||||
mutex_unlock(&chip->tpm_mutex);
|
mutex_unlock(&chip->tpm_mutex);
|
||||||
return rc ? rc : len;
|
return rc ? rc : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tmp_transmit_cmd - send a tpm command to the device
|
* tpm_transmit - Internal kernel interface to transmit TPM commands.
|
||||||
|
*
|
||||||
|
* @chip: TPM chip to use
|
||||||
|
* @space: tpm space
|
||||||
|
* @buf: TPM command buffer
|
||||||
|
* @bufsiz: length of the TPM command buffer
|
||||||
|
* @flags: tpm transmit flags - bitmap
|
||||||
|
*
|
||||||
|
* A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY
|
||||||
|
* returns from the TPM and retransmits the command after a delay up
|
||||||
|
* to a maximum wait of TPM2_DURATION_LONG.
|
||||||
|
*
|
||||||
|
* Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2
|
||||||
|
* only
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* the length of the return when the operation is successful.
|
||||||
|
* A negative number for system errors (errno).
|
||||||
|
*/
|
||||||
|
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
|
u8 *buf, size_t bufsiz, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct tpm_output_header *header = (struct tpm_output_header *)buf;
|
||||||
|
/* space for header and handles */
|
||||||
|
u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
|
||||||
|
unsigned int delay_msec = TPM2_DURATION_SHORT;
|
||||||
|
u32 rc = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
|
||||||
|
bufsiz);
|
||||||
|
/* the command code is where the return code will be */
|
||||||
|
u32 cc = be32_to_cpu(header->return_code);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subtlety here: if we have a space, the handles will be
|
||||||
|
* transformed, so when we restore the header we also have to
|
||||||
|
* restore the handles.
|
||||||
|
*/
|
||||||
|
memcpy(save, buf, save_size);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
rc = be32_to_cpu(header->return_code);
|
||||||
|
if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING)
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* return immediately if self test returns test
|
||||||
|
* still running to shorten boot time.
|
||||||
|
*/
|
||||||
|
if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
|
||||||
|
break;
|
||||||
|
delay_msec *= 2;
|
||||||
|
if (delay_msec > TPM2_DURATION_LONG) {
|
||||||
|
if (rc == TPM2_RC_RETRY)
|
||||||
|
dev_err(&chip->dev, "in retry loop\n");
|
||||||
|
else
|
||||||
|
dev_err(&chip->dev,
|
||||||
|
"self test is still running\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tpm_msleep(delay_msec);
|
||||||
|
memcpy(buf, save, save_size);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* tpm_transmit_cmd - send a tpm command to the device
|
||||||
* The function extracts tpm out header return code
|
* The function extracts tpm out header return code
|
||||||
*
|
*
|
||||||
* @chip: TPM chip to use
|
* @chip: TPM chip to use
|
||||||
|
* @space: tpm space
|
||||||
* @buf: TPM command buffer
|
* @buf: TPM command buffer
|
||||||
* @bufsiz: length of the buffer
|
* @bufsiz: length of the buffer
|
||||||
* @min_rsp_body_length: minimum expected length of response body
|
* @min_rsp_body_length: minimum expected length of response body
|
||||||
|
@ -532,7 +619,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
* A positive number for a TPM error.
|
* A positive number for a TPM error.
|
||||||
*/
|
*/
|
||||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
const void *buf, size_t bufsiz,
|
void *buf, size_t bufsiz,
|
||||||
size_t min_rsp_body_length, unsigned int flags,
|
size_t min_rsp_body_length, unsigned int flags,
|
||||||
const char *desc)
|
const char *desc)
|
||||||
{
|
{
|
||||||
|
@ -540,7 +627,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
int err;
|
int err;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
|
len = tpm_transmit(chip, space, buf, bufsiz, flags);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
return len;
|
return len;
|
||||||
|
|
||||||
|
@ -666,6 +753,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
||||||
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
|
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
|
||||||
chip->duration[TPM_LONG] =
|
chip->duration[TPM_LONG] =
|
||||||
msecs_to_jiffies(TPM2_DURATION_LONG);
|
msecs_to_jiffies(TPM2_DURATION_LONG);
|
||||||
|
chip->duration[TPM_LONG_LONG] =
|
||||||
|
msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
|
||||||
|
|
||||||
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -754,6 +843,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
||||||
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
|
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
|
||||||
chip->duration[TPM_LONG] =
|
chip->duration[TPM_LONG] =
|
||||||
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
|
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
|
||||||
|
chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */
|
||||||
|
|
||||||
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
|
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
|
||||||
* value wrong and apparently reports msecs rather than usecs. So we
|
* value wrong and apparently reports msecs rather than usecs. So we
|
||||||
|
@ -969,6 +1059,10 @@ int tpm_do_selftest(struct tpm_chip *chip)
|
||||||
loops = jiffies_to_msecs(duration) / delay_msec;
|
loops = jiffies_to_msecs(duration) / delay_msec;
|
||||||
|
|
||||||
rc = tpm_continue_selftest(chip);
|
rc = tpm_continue_selftest(chip);
|
||||||
|
if (rc == TPM_ERR_INVALID_POSTINIT) {
|
||||||
|
chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
|
||||||
|
dev_info(&chip->dev, "TPM not ready (%d)\n", rc);
|
||||||
|
}
|
||||||
/* This may fail if there was no TPM driver during a suspend/resume
|
/* This may fail if there was no TPM driver during a suspend/resume
|
||||||
* cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
|
* cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -67,7 +67,9 @@ enum tpm_duration {
|
||||||
TPM_SHORT = 0,
|
TPM_SHORT = 0,
|
||||||
TPM_MEDIUM = 1,
|
TPM_MEDIUM = 1,
|
||||||
TPM_LONG = 2,
|
TPM_LONG = 2,
|
||||||
|
TPM_LONG_LONG = 3,
|
||||||
TPM_UNDEFINED,
|
TPM_UNDEFINED,
|
||||||
|
TPM_NUM_DURATIONS = TPM_UNDEFINED,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TPM_WARN_RETRY 0x800
|
#define TPM_WARN_RETRY 0x800
|
||||||
|
@ -79,15 +81,20 @@ enum tpm_duration {
|
||||||
#define TPM_HEADER_SIZE 10
|
#define TPM_HEADER_SIZE 10
|
||||||
|
|
||||||
enum tpm2_const {
|
enum tpm2_const {
|
||||||
TPM2_PLATFORM_PCR = 24,
|
TPM2_PLATFORM_PCR = 24,
|
||||||
TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
|
TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
|
||||||
TPM2_TIMEOUT_A = 750,
|
};
|
||||||
TPM2_TIMEOUT_B = 2000,
|
|
||||||
TPM2_TIMEOUT_C = 200,
|
enum tpm2_timeouts {
|
||||||
TPM2_TIMEOUT_D = 30,
|
TPM2_TIMEOUT_A = 750,
|
||||||
TPM2_DURATION_SHORT = 20,
|
TPM2_TIMEOUT_B = 2000,
|
||||||
TPM2_DURATION_MEDIUM = 750,
|
TPM2_TIMEOUT_C = 200,
|
||||||
TPM2_DURATION_LONG = 2000,
|
TPM2_TIMEOUT_D = 30,
|
||||||
|
TPM2_DURATION_SHORT = 20,
|
||||||
|
TPM2_DURATION_MEDIUM = 750,
|
||||||
|
TPM2_DURATION_LONG = 2000,
|
||||||
|
TPM2_DURATION_LONG_LONG = 300000,
|
||||||
|
TPM2_DURATION_DEFAULT = 120000,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum tpm2_structures {
|
enum tpm2_structures {
|
||||||
|
@ -104,10 +111,12 @@ enum tpm2_return_codes {
|
||||||
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
|
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
|
||||||
TPM2_RC_HANDLE = 0x008B,
|
TPM2_RC_HANDLE = 0x008B,
|
||||||
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
|
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
|
||||||
|
TPM2_RC_FAILURE = 0x0101,
|
||||||
TPM2_RC_DISABLED = 0x0120,
|
TPM2_RC_DISABLED = 0x0120,
|
||||||
TPM2_RC_COMMAND_CODE = 0x0143,
|
TPM2_RC_COMMAND_CODE = 0x0143,
|
||||||
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
||||||
TPM2_RC_REFERENCE_H0 = 0x0910,
|
TPM2_RC_REFERENCE_H0 = 0x0910,
|
||||||
|
TPM2_RC_RETRY = 0x0922,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum tpm2_algorithms {
|
enum tpm2_algorithms {
|
||||||
|
@ -123,6 +132,7 @@ enum tpm2_algorithms {
|
||||||
|
|
||||||
enum tpm2_command_codes {
|
enum tpm2_command_codes {
|
||||||
TPM2_CC_FIRST = 0x011F,
|
TPM2_CC_FIRST = 0x011F,
|
||||||
|
TPM2_CC_CREATE_PRIMARY = 0x0131,
|
||||||
TPM2_CC_SELF_TEST = 0x0143,
|
TPM2_CC_SELF_TEST = 0x0143,
|
||||||
TPM2_CC_STARTUP = 0x0144,
|
TPM2_CC_STARTUP = 0x0144,
|
||||||
TPM2_CC_SHUTDOWN = 0x0145,
|
TPM2_CC_SHUTDOWN = 0x0145,
|
||||||
|
@ -227,7 +237,7 @@ struct tpm_chip {
|
||||||
unsigned long timeout_c; /* jiffies */
|
unsigned long timeout_c; /* jiffies */
|
||||||
unsigned long timeout_d; /* jiffies */
|
unsigned long timeout_d; /* jiffies */
|
||||||
bool timeout_adjusted;
|
bool timeout_adjusted;
|
||||||
unsigned long duration[3]; /* jiffies */
|
unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
|
||||||
bool duration_adjusted;
|
bool duration_adjusted;
|
||||||
|
|
||||||
struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
|
struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
|
||||||
|
@ -506,7 +516,7 @@ enum tpm_transmit_flags {
|
||||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
u8 *buf, size_t bufsiz, unsigned int flags);
|
u8 *buf, size_t bufsiz, unsigned int flags);
|
||||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||||
const void *buf, size_t bufsiz,
|
void *buf, size_t bufsiz,
|
||||||
size_t min_rsp_body_length, unsigned int flags,
|
size_t min_rsp_body_length, unsigned int flags,
|
||||||
const char *desc);
|
const char *desc);
|
||||||
int tpm_startup(struct tpm_chip *chip);
|
int tpm_startup(struct tpm_chip *chip);
|
||||||
|
|
|
@ -31,10 +31,6 @@ struct tpm2_startup_in {
|
||||||
__be16 startup_type;
|
__be16 startup_type;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct tpm2_self_test_in {
|
|
||||||
u8 full_test;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct tpm2_get_tpm_pt_in {
|
struct tpm2_get_tpm_pt_in {
|
||||||
__be32 cap_id;
|
__be32 cap_id;
|
||||||
__be32 property_id;
|
__be32 property_id;
|
||||||
|
@ -60,7 +56,6 @@ struct tpm2_get_random_out {
|
||||||
|
|
||||||
union tpm2_cmd_params {
|
union tpm2_cmd_params {
|
||||||
struct tpm2_startup_in startup_in;
|
struct tpm2_startup_in startup_in;
|
||||||
struct tpm2_self_test_in selftest_in;
|
|
||||||
struct tpm2_get_tpm_pt_in get_tpm_pt_in;
|
struct tpm2_get_tpm_pt_in get_tpm_pt_in;
|
||||||
struct tpm2_get_tpm_pt_out get_tpm_pt_out;
|
struct tpm2_get_tpm_pt_out get_tpm_pt_out;
|
||||||
struct tpm2_get_random_in getrandom_in;
|
struct tpm2_get_random_in getrandom_in;
|
||||||
|
@ -90,6 +85,8 @@ static struct tpm2_hash tpm2_hash_map[] = {
|
||||||
* of time the chip could take to return the result. The values
|
* of time the chip could take to return the result. The values
|
||||||
* of the SHORT, MEDIUM, and LONG durations are taken from the
|
* of the SHORT, MEDIUM, and LONG durations are taken from the
|
||||||
* PC Client Profile (PTP) specification.
|
* PC Client Profile (PTP) specification.
|
||||||
|
* LONG_LONG is for commands that generates keys which empirically
|
||||||
|
* takes longer time on some systems.
|
||||||
*/
|
*/
|
||||||
static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
|
static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
|
||||||
TPM_UNDEFINED, /* 11F */
|
TPM_UNDEFINED, /* 11F */
|
||||||
|
@ -110,7 +107,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
|
||||||
TPM_UNDEFINED, /* 12e */
|
TPM_UNDEFINED, /* 12e */
|
||||||
TPM_UNDEFINED, /* 12f */
|
TPM_UNDEFINED, /* 12f */
|
||||||
TPM_UNDEFINED, /* 130 */
|
TPM_UNDEFINED, /* 130 */
|
||||||
TPM_UNDEFINED, /* 131 */
|
TPM_LONG_LONG, /* 131 */
|
||||||
TPM_UNDEFINED, /* 132 */
|
TPM_UNDEFINED, /* 132 */
|
||||||
TPM_UNDEFINED, /* 133 */
|
TPM_UNDEFINED, /* 133 */
|
||||||
TPM_UNDEFINED, /* 134 */
|
TPM_UNDEFINED, /* 134 */
|
||||||
|
@ -144,7 +141,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
|
||||||
TPM_UNDEFINED, /* 150 */
|
TPM_UNDEFINED, /* 150 */
|
||||||
TPM_UNDEFINED, /* 151 */
|
TPM_UNDEFINED, /* 151 */
|
||||||
TPM_UNDEFINED, /* 152 */
|
TPM_UNDEFINED, /* 152 */
|
||||||
TPM_UNDEFINED, /* 153 */
|
TPM_LONG_LONG, /* 153 */
|
||||||
TPM_UNDEFINED, /* 154 */
|
TPM_UNDEFINED, /* 154 */
|
||||||
TPM_UNDEFINED, /* 155 */
|
TPM_UNDEFINED, /* 155 */
|
||||||
TPM_UNDEFINED, /* 156 */
|
TPM_UNDEFINED, /* 156 */
|
||||||
|
@ -821,22 +818,12 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
|
||||||
duration = chip->duration[index];
|
duration = chip->duration[index];
|
||||||
|
|
||||||
if (duration <= 0)
|
if (duration <= 0)
|
||||||
duration = 2 * 60 * HZ;
|
duration = msecs_to_jiffies(TPM2_DURATION_DEFAULT);
|
||||||
|
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
|
EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
|
||||||
|
|
||||||
#define TPM2_SELF_TEST_IN_SIZE \
|
|
||||||
(sizeof(struct tpm_input_header) + \
|
|
||||||
sizeof(struct tpm2_self_test_in))
|
|
||||||
|
|
||||||
static const struct tpm_input_header tpm2_selftest_header = {
|
|
||||||
.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
|
|
||||||
.length = cpu_to_be32(TPM2_SELF_TEST_IN_SIZE),
|
|
||||||
.ordinal = cpu_to_be32(TPM2_CC_SELF_TEST)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tpm2_do_selftest() - ensure that all self tests have passed
|
* tpm2_do_selftest() - ensure that all self tests have passed
|
||||||
*
|
*
|
||||||
|
@ -852,27 +839,24 @@ static const struct tpm_input_header tpm2_selftest_header = {
|
||||||
*/
|
*/
|
||||||
static int tpm2_do_selftest(struct tpm_chip *chip)
|
static int tpm2_do_selftest(struct tpm_chip *chip)
|
||||||
{
|
{
|
||||||
|
struct tpm_buf buf;
|
||||||
|
int full;
|
||||||
int rc;
|
int rc;
|
||||||
unsigned int delay_msec = 10;
|
|
||||||
long duration;
|
|
||||||
struct tpm2_cmd cmd;
|
|
||||||
|
|
||||||
duration = jiffies_to_msecs(
|
for (full = 0; full < 2; full++) {
|
||||||
tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST));
|
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
while (1) {
|
tpm_buf_append_u8(&buf, full);
|
||||||
cmd.header.in = tpm2_selftest_header;
|
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
||||||
cmd.params.selftest_in.full_test = 0;
|
"attempting the self test");
|
||||||
|
tpm_buf_destroy(&buf);
|
||||||
|
|
||||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE,
|
if (rc == TPM2_RC_TESTING)
|
||||||
0, 0, "continue selftest");
|
rc = TPM2_RC_SUCCESS;
|
||||||
|
if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
|
||||||
if (rc != TPM2_RC_TESTING || delay_msec >= duration)
|
return rc;
|
||||||
break;
|
|
||||||
|
|
||||||
/* wait longer than before */
|
|
||||||
delay_msec *= 2;
|
|
||||||
tpm_msleep(delay_msec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1058,10 +1042,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
rc = tpm2_do_selftest(chip);
|
rc = tpm2_do_selftest(chip);
|
||||||
if (rc != 0 && rc != TPM2_RC_INITIALIZE) {
|
if (rc && rc != TPM2_RC_INITIALIZE)
|
||||||
dev_err(&chip->dev, "TPM self test failed\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == TPM2_RC_INITIALIZE) {
|
if (rc == TPM2_RC_INITIALIZE) {
|
||||||
rc = tpm_startup(chip);
|
rc = tpm_startup(chip);
|
||||||
|
@ -1069,10 +1051,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
rc = tpm2_do_selftest(chip);
|
rc = tpm2_do_selftest(chip);
|
||||||
if (rc) {
|
if (rc)
|
||||||
dev_err(&chip->dev, "TPM self test failed\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = tpm2_get_pcr_allocation(chip);
|
rc = tpm2_get_pcr_allocation(chip);
|
||||||
|
|
|
@ -112,35 +112,6 @@ struct tpm2_crb_smc {
|
||||||
u32 smc_func_id;
|
u32 smc_func_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* crb_go_idle - request tpm crb device to go the idle state
|
|
||||||
*
|
|
||||||
* @dev: crb device
|
|
||||||
* @priv: crb private data
|
|
||||||
*
|
|
||||||
* Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ
|
|
||||||
* The device should respond within TIMEOUT_C by clearing the bit.
|
|
||||||
* Anyhow, we do not wait here as a consequent CMD_READY request
|
|
||||||
* will be handled correctly even if idle was not completed.
|
|
||||||
*
|
|
||||||
* The function does nothing for devices with ACPI-start method
|
|
||||||
* or SMC-start method.
|
|
||||||
*
|
|
||||||
* Return: 0 always
|
|
||||||
*/
|
|
||||||
static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
|
|
||||||
{
|
|
||||||
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
|
|
||||||
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
|
|
||||||
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
|
|
||||||
/* we don't really care when this settles */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
||||||
unsigned long timeout)
|
unsigned long timeout)
|
||||||
{
|
{
|
||||||
|
@ -157,7 +128,42 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
||||||
usleep_range(50, 100);
|
usleep_range(50, 100);
|
||||||
} while (ktime_before(ktime_get(), stop));
|
} while (ktime_before(ktime_get(), stop));
|
||||||
|
|
||||||
return false;
|
return ((ioread32(reg) & mask) == value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* crb_go_idle - request tpm crb device to go the idle state
|
||||||
|
*
|
||||||
|
* @dev: crb device
|
||||||
|
* @priv: crb private data
|
||||||
|
*
|
||||||
|
* Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ
|
||||||
|
* The device should respond within TIMEOUT_C by clearing the bit.
|
||||||
|
* Anyhow, we do not wait here as a consequent CMD_READY request
|
||||||
|
* will be handled correctly even if idle was not completed.
|
||||||
|
*
|
||||||
|
* The function does nothing for devices with ACPI-start method
|
||||||
|
* or SMC-start method.
|
||||||
|
*
|
||||||
|
* Return: 0 always
|
||||||
|
*/
|
||||||
|
static int crb_go_idle(struct device *dev, struct crb_priv *priv)
|
||||||
|
{
|
||||||
|
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
|
||||||
|
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
|
||||||
|
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
|
||||||
|
|
||||||
|
if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
|
||||||
|
CRB_CTRL_REQ_GO_IDLE/* mask */,
|
||||||
|
0, /* value */
|
||||||
|
TPM2_TIMEOUT_C)) {
|
||||||
|
dev_warn(dev, "goIdle timed out\n");
|
||||||
|
return -ETIME;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -175,8 +181,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
||||||
*
|
*
|
||||||
* Return: 0 on success -ETIME on timeout;
|
* Return: 0 on success -ETIME on timeout;
|
||||||
*/
|
*/
|
||||||
static int __maybe_unused crb_cmd_ready(struct device *dev,
|
static int crb_cmd_ready(struct device *dev, struct crb_priv *priv)
|
||||||
struct crb_priv *priv)
|
|
||||||
{
|
{
|
||||||
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
|
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
|
||||||
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
|
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
|
||||||
|
@ -195,11 +200,11 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int crb_request_locality(struct tpm_chip *chip, int loc)
|
static int __crb_request_locality(struct device *dev,
|
||||||
|
struct crb_priv *priv, int loc)
|
||||||
{
|
{
|
||||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
|
||||||
u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
|
u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
|
||||||
CRB_LOC_STATE_TPM_REG_VALID_STS;
|
CRB_LOC_STATE_TPM_REG_VALID_STS;
|
||||||
|
|
||||||
if (!priv->regs_h)
|
if (!priv->regs_h)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -207,21 +212,45 @@ static int crb_request_locality(struct tpm_chip *chip, int loc)
|
||||||
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
|
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
|
||||||
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
|
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
|
||||||
TPM2_TIMEOUT_C)) {
|
TPM2_TIMEOUT_C)) {
|
||||||
dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
|
dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
|
||||||
return -ETIME;
|
return -ETIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
|
static int crb_request_locality(struct tpm_chip *chip, int loc)
|
||||||
{
|
{
|
||||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||||
|
|
||||||
|
return __crb_request_locality(&chip->dev, priv, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __crb_relinquish_locality(struct device *dev,
|
||||||
|
struct crb_priv *priv, int loc)
|
||||||
|
{
|
||||||
|
u32 mask = CRB_LOC_STATE_LOC_ASSIGNED |
|
||||||
|
CRB_LOC_STATE_TPM_REG_VALID_STS;
|
||||||
|
u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS;
|
||||||
|
|
||||||
if (!priv->regs_h)
|
if (!priv->regs_h)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
|
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
|
||||||
|
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value,
|
||||||
|
TPM2_TIMEOUT_C)) {
|
||||||
|
dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
|
||||||
|
return -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int crb_relinquish_locality(struct tpm_chip *chip, int loc)
|
||||||
|
{
|
||||||
|
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||||
|
|
||||||
|
return __crb_relinquish_locality(&chip->dev, priv, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 crb_status(struct tpm_chip *chip)
|
static u8 crb_status(struct tpm_chip *chip)
|
||||||
|
@ -442,6 +471,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||||
u32 pa_high, pa_low;
|
u32 pa_high, pa_low;
|
||||||
u64 cmd_pa;
|
u64 cmd_pa;
|
||||||
u32 cmd_size;
|
u32 cmd_size;
|
||||||
|
__le64 __rsp_pa;
|
||||||
u64 rsp_pa;
|
u64 rsp_pa;
|
||||||
u32 rsp_size;
|
u32 rsp_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -475,6 +505,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||||
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
|
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = __crb_request_locality(dev, priv, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
|
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||||
sizeof(struct crb_regs_tail));
|
sizeof(struct crb_regs_tail));
|
||||||
if (IS_ERR(priv->regs_t))
|
if (IS_ERR(priv->regs_t))
|
||||||
|
@ -503,8 +537,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
|
memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
|
||||||
rsp_pa = le64_to_cpu(rsp_pa);
|
rsp_pa = le64_to_cpu(__rsp_pa);
|
||||||
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
|
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
|
||||||
ioread32(&priv->regs_t->ctrl_rsp_size));
|
ioread32(&priv->regs_t->ctrl_rsp_size));
|
||||||
|
|
||||||
|
@ -531,6 +565,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||||
|
|
||||||
crb_go_idle(dev, priv);
|
crb_go_idle(dev, priv);
|
||||||
|
|
||||||
|
__crb_relinquish_locality(dev, priv, 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,10 +624,14 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||||
chip->acpi_dev_handle = device->handle;
|
chip->acpi_dev_handle = device->handle;
|
||||||
chip->flags = TPM_CHIP_FLAG_TPM2;
|
chip->flags = TPM_CHIP_FLAG_TPM2;
|
||||||
|
|
||||||
rc = crb_cmd_ready(dev, priv);
|
rc = __crb_request_locality(dev, priv, 0);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
rc = crb_cmd_ready(dev, priv);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
pm_runtime_get_noresume(dev);
|
pm_runtime_get_noresume(dev);
|
||||||
pm_runtime_set_active(dev);
|
pm_runtime_set_active(dev);
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
|
@ -601,12 +641,15 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||||
crb_go_idle(dev, priv);
|
crb_go_idle(dev, priv);
|
||||||
pm_runtime_put_noidle(dev);
|
pm_runtime_put_noidle(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
return rc;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_put(dev);
|
pm_runtime_put_sync(dev);
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
__crb_relinquish_locality(dev, priv, 0);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int crb_acpi_remove(struct acpi_device *device)
|
static int crb_acpi_remove(struct acpi_device *device)
|
||||||
|
|
|
@ -143,11 +143,13 @@ static bool check_locality(struct tpm_chip *chip, int l)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_locality(struct tpm_chip *chip, int l)
|
static int release_locality(struct tpm_chip *chip, int l)
|
||||||
{
|
{
|
||||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||||
|
|
||||||
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int request_locality(struct tpm_chip *chip, int l)
|
static int request_locality(struct tpm_chip *chip, int l)
|
||||||
|
|
|
@ -44,7 +44,7 @@ struct tpm_class_ops {
|
||||||
bool (*update_timeouts)(struct tpm_chip *chip,
|
bool (*update_timeouts)(struct tpm_chip *chip,
|
||||||
unsigned long *timeout_cap);
|
unsigned long *timeout_cap);
|
||||||
int (*request_locality)(struct tpm_chip *chip, int loc);
|
int (*request_locality)(struct tpm_chip *chip, int loc);
|
||||||
void (*relinquish_locality)(struct tpm_chip *chip, int loc);
|
int (*relinquish_locality)(struct tpm_chip *chip, int loc);
|
||||||
void (*clk_enable)(struct tpm_chip *chip, bool value);
|
void (*clk_enable)(struct tpm_chip *chip, bool value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue