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:
Linus Torvalds 2018-04-07 16:46:56 -07:00
commit 4b3f1a1515
6 changed files with 259 additions and 130 deletions

View file

@ -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)
*/ */

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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);
}; };