* [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules @ 2021-07-28 10:57 Borys Movchan 2021-07-28 21:58 ` Jarkko Sakkinen 0 siblings, 1 reply; 4+ messages in thread From: Borys Movchan @ 2021-07-28 10:57 UTC (permalink / raw) To: Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe Cc: kernel, Borys Movchan, linux-integrity, linux-kernel If something went wrong during the TPM firmware upgrade, like power failure or the firmware image file get corrupted, the TPM might end up in Upgrade or Failure mode upon the next start. The state is persistent between the TPM power cycle/restart. According to TPM specification: * If the TPM is in Upgrade mode, it will answer with TPM2_RC_UPGRADE to all commands except Field Upgrade related ones. * If the TPM is in Failure mode, it will allow performing TPM initialization but will not provide any crypto operations. Will happily respond to Field Upgrade calls. The fix adds the possibility to detect an active state of the TPM and gives the user-space a chance to finish the firmware upgrade/recover the TPM. Signed-off-by: Borys Movchan <borysmn@axis.com> --- Notes: v2: The terms are changed to match the ones used in the TPM specification. Rework the commit message to provide more details regarding TPM behavior in Failure/Upgrade mode. The TPM specification describes TPM behavior in Upgrade mode very clearly. Things are a bit more complex if we are talking about Failure mode. The TPM behavior in this mode is highly vendor-specific. Although, there is one thing clearly described in the TPM specification and can be relied on to detect the Failure state: in Failure mode, the TPM doesn't provide any crypto operations. Including access to attributes and configuration registers. It seems persistent between different TPM manufacturers, at least to the degree I was able to verify. drivers/char/tpm/tpm-chip.c | 23 +++++++++++++++-------- drivers/char/tpm/tpm2-cmd.c | 12 ++++++++++-- include/linux/tpm.h | 1 + 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index ddaeceb7e109..ff2367c447fb 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -574,20 +574,25 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) int tpm_chip_register(struct tpm_chip *chip) { int rc; + bool limited_mode = false; rc = tpm_chip_start(chip); if (rc) return rc; rc = tpm_auto_startup(chip); - if (rc) { + if (rc == -EIO) { + limited_mode = true; + } else if (rc) { tpm_chip_stop(chip); return rc; } - rc = tpm_get_pcr_allocation(chip); - tpm_chip_stop(chip); - if (rc) - return rc; + if (!limited_mode) { + rc = tpm_get_pcr_allocation(chip); + tpm_chip_stop(chip); + if (rc) + return rc; + } tpm_sysfs_add_device(chip); @@ -595,9 +600,11 @@ int tpm_chip_register(struct tpm_chip *chip) tpm_add_ppi(chip); - rc = tpm_add_hwrng(chip); - if (rc) - goto out_ppi; + if (!limited_mode) { + rc = tpm_add_hwrng(chip); + if (rc) + goto out_ppi; + } rc = tpm_add_char_device(chip); if (rc) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index a25815a6f625..7468353ed67d 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -718,7 +718,8 @@ static int tpm2_startup(struct tpm_chip *chip) * sequence * @chip: TPM chip to use * - * Returns 0 on success, < 0 in case of fatal error. + * Returns 0 on success, -ENODEV in case of fatal error, + * -EIO in case of Reduced/Upgrade mode */ int tpm2_auto_startup(struct tpm_chip *chip) { @@ -729,7 +730,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) goto out; rc = tpm2_do_selftest(chip); - if (rc && rc != TPM2_RC_INITIALIZE) + if (rc == TPM2_RC_UPGRADE) { + rc = -EIO; + goto out; + } else if (rc && rc != TPM2_RC_INITIALIZE) goto out; if (rc == TPM2_RC_INITIALIZE) { @@ -743,6 +747,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) } rc = tpm2_get_cc_attrs_tbl(chip); + if (rc) { /* Succeeded until here, but failed -> reduced mode */ + rc = -EIO; + goto out; + } out: if (rc > 0) diff --git a/include/linux/tpm.h b/include/linux/tpm.h index aa11fe323c56..e873c42907f0 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -207,6 +207,7 @@ enum tpm2_return_codes { TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ TPM2_RC_FAILURE = 0x0101, TPM2_RC_DISABLED = 0x0120, + TPM2_RC_UPGRADE = 0x012D, TPM2_RC_COMMAND_CODE = 0x0143, TPM2_RC_TESTING = 0x090A, /* RC_WARN */ TPM2_RC_REFERENCE_H0 = 0x0910, -- 2.20.1 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules 2021-07-28 10:57 [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules Borys Movchan @ 2021-07-28 21:58 ` Jarkko Sakkinen 2021-07-30 14:24 ` Borys Movchan 0 siblings, 1 reply; 4+ messages in thread From: Jarkko Sakkinen @ 2021-07-28 21:58 UTC (permalink / raw) To: Borys Movchan Cc: Peter Huewe, Jason Gunthorpe, kernel, linux-integrity, linux-kernel On Wed, Jul 28, 2021 at 12:57:30PM +0200, Borys Movchan wrote: > If something went wrong during the TPM firmware upgrade, > like power failure or the firmware image file get corrupted, > the TPM might end up in Upgrade or Failure mode upon the > next start. The state is persistent between the TPM power > cycle/restart. > > According to TPM specification: > * If the TPM is in Upgrade mode, it will answer with > TPM2_RC_UPGRADE to all commands except Field Upgrade > related ones. > * If the TPM is in Failure mode, it will allow performing > TPM initialization but will not provide any crypto > operations. Will happily respond to Field Upgrade calls. > > The fix adds the possibility to detect an active state of > the TPM and gives the user-space a chance to finish the > firmware upgrade/recover the TPM. This is different than telling what the patch does. It's just describing a goal, but does not describe how the driver is changed, and reasons for doing that. For instance, you check 'limited_mode' flag in a few sites. How can I know that those are exactly the locations where this needs to be done? > Signed-off-by: Borys Movchan <borysmn@axis.com> > --- > > Notes: > v2: The terms are changed to match the ones used in the TPM specification. > Rework the commit message to provide more details regarding TPM > behavior in Failure/Upgrade mode. > > The TPM specification describes TPM behavior in Upgrade mode very clearly. > Things are a bit more complex if we are talking about Failure mode. > The TPM behavior in this mode is highly vendor-specific. Although, there > is one thing clearly described in the TPM specification and can be relied > on to detect the Failure state: in Failure mode, the TPM doesn't provide > any crypto operations. Including access to attributes and configuration > registers. > It seems persistent between different TPM manufacturers, at least to the > degree I was able to verify. > > drivers/char/tpm/tpm-chip.c | 23 +++++++++++++++-------- > drivers/char/tpm/tpm2-cmd.c | 12 ++++++++++-- > include/linux/tpm.h | 1 + > 3 files changed, 26 insertions(+), 10 deletions(-) > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > index ddaeceb7e109..ff2367c447fb 100644 > --- a/drivers/char/tpm/tpm-chip.c > +++ b/drivers/char/tpm/tpm-chip.c > @@ -574,20 +574,25 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) > int tpm_chip_register(struct tpm_chip *chip) > { > int rc; > + bool limited_mode = false; > > rc = tpm_chip_start(chip); > if (rc) > return rc; > rc = tpm_auto_startup(chip); > - if (rc) { > + if (rc == -EIO) { > + limited_mode = true; > + } else if (rc) { > tpm_chip_stop(chip); > return rc; > } > > - rc = tpm_get_pcr_allocation(chip); > - tpm_chip_stop(chip); > - if (rc) > - return rc; > + if (!limited_mode) { > + rc = tpm_get_pcr_allocation(chip); > + tpm_chip_stop(chip); > + if (rc) > + return rc; > + } > > tpm_sysfs_add_device(chip); > > @@ -595,9 +600,11 @@ int tpm_chip_register(struct tpm_chip *chip) > > tpm_add_ppi(chip); > > - rc = tpm_add_hwrng(chip); > - if (rc) > - goto out_ppi; > + if (!limited_mode) { > + rc = tpm_add_hwrng(chip); > + if (rc) > + goto out_ppi; > + } > > rc = tpm_add_char_device(chip); > if (rc) > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c > index a25815a6f625..7468353ed67d 100644 > --- a/drivers/char/tpm/tpm2-cmd.c > +++ b/drivers/char/tpm/tpm2-cmd.c > @@ -718,7 +718,8 @@ static int tpm2_startup(struct tpm_chip *chip) > * sequence > * @chip: TPM chip to use > * > - * Returns 0 on success, < 0 in case of fatal error. > + * Returns 0 on success, -ENODEV in case of fatal error, > + * -EIO in case of Reduced/Upgrade mode > */ > int tpm2_auto_startup(struct tpm_chip *chip) > { > @@ -729,7 +730,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) > goto out; > > rc = tpm2_do_selftest(chip); > - if (rc && rc != TPM2_RC_INITIALIZE) > + if (rc == TPM2_RC_UPGRADE) { > + rc = -EIO; > + goto out; > + } else if (rc && rc != TPM2_RC_INITIALIZE) > goto out; > > if (rc == TPM2_RC_INITIALIZE) { > @@ -743,6 +747,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) > } > > rc = tpm2_get_cc_attrs_tbl(chip); > + if (rc) { /* Succeeded until here, but failed -> reduced mode */ > + rc = -EIO; > + goto out; > + } > > out: > if (rc > 0) > diff --git a/include/linux/tpm.h b/include/linux/tpm.h > index aa11fe323c56..e873c42907f0 100644 > --- a/include/linux/tpm.h > +++ b/include/linux/tpm.h > @@ -207,6 +207,7 @@ enum tpm2_return_codes { > TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ > TPM2_RC_FAILURE = 0x0101, > TPM2_RC_DISABLED = 0x0120, > + TPM2_RC_UPGRADE = 0x012D, > TPM2_RC_COMMAND_CODE = 0x0143, > TPM2_RC_TESTING = 0x090A, /* RC_WARN */ > TPM2_RC_REFERENCE_H0 = 0x0910, > -- > 2.20.1 > > /Jarkko ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules 2021-07-28 21:58 ` Jarkko Sakkinen @ 2021-07-30 14:24 ` Borys Movchan 2021-08-02 8:35 ` Jarkko Sakkinen 0 siblings, 1 reply; 4+ messages in thread From: Borys Movchan @ 2021-07-30 14:24 UTC (permalink / raw) To: Jarkko Sakkinen, Borys Movchan Cc: Peter Huewe, Jason Gunthorpe, kernel, linux-integrity, linux-kernel On 7/28/21 11:58 PM, Jarkko Sakkinen wrote: > On Wed, Jul 28, 2021 at 12:57:30PM +0200, Borys Movchan wrote: > > If something went wrong during the TPM firmware upgrade, > > like power failure or the firmware image file get corrupted, > > the TPM might end up in Upgrade or Failure mode upon the > > next start. The state is persistent between the TPM power > > cycle/restart. > > > > According to TPM specification: > > * If the TPM is in Upgrade mode, it will answer with > > TPM2_RC_UPGRADE to all commands except Field Upgrade > > related ones. > > * If the TPM is in Failure mode, it will allow performing > > TPM initialization but will not provide any crypto > > operations. Will happily respond to Field Upgrade calls. > > > > The fix adds the possibility to detect an active state of > > the TPM and gives the user-space a chance to finish the > > firmware upgrade/recover the TPM. > > This is different than telling what the patch does. It's just > describing a goal, but does not describe how the driver is > changed, and reasons for doing that. > > For instance, you check 'limited_mode' flag in a few sites. > How can I know that those are exactly the locations where this > needs to be done? > Seems like I got what you are looking for. Let me try to explain the reasoning and doubts regarding what I meant under my change. According to my previous comments the reason of the change is to provide a way to user-space application to safely perform the TPM firmware update and cover all corner cases like power failure, corrupted firmware image, bad communication channel and so on. In original implementation, if TPM is in Upgrade or Failure (some vendors call it Reduced) mode the driver will simple fail during initialization and will not provide any conventional way for the user-space application to address the situation. So the fix changes the behavior of `tpm_chip_start` function in a way that it will try to distinguish between actual TPM start failure and having TPM in Upgrade/Failure mode. The Upgrade mode is easy to distinguish. According to the TPM specification, in this mode the TPM will answer to only 2 calls: `TPM_CC_FieldUpgradeStart` and `TPM_CC_FieldUpgradeData`. It will respond `TPM_RC_UPGRADE` to any other call. Failure mode, as mentioned earlier, a bit trickier, but possible to detect. In this mode very limited functionality is supported as TPM runs some form of fallback firmware or in fallback mode due to failure of upgrade. Different vendors implement it slightly different. The way how the fix tries to address this mode is by making sure that `TPM_CC_SelfTest` and `TPM_CC_Startup` are executed correctly but all the consequent calls to address crypto functionality of the TPM are failing. In other words, functions like `tpm2_get_cc_attrs_tbl`, `tpm_add_hwrng` and `tpm_get_pcr_allocation` will fail. Below you also can find more detailed comments inline regarding every part of the change. Important to note that the fix addresses only TPM2.0 compatible devices as that is what I am working with. > > Signed-off-by: Borys Movchan <borysmn@axis.com> > > --- > > > > Notes: > > v2: The terms are changed to match the ones used in the TPM specification. > > Rework the commit message to provide more details regarding TPM > > behavior in Failure/Upgrade mode. > > > > The TPM specification describes TPM behavior in Upgrade mode very clearly. > > Things are a bit more complex if we are talking about Failure mode. > > The TPM behavior in this mode is highly vendor-specific. Although, there > > is one thing clearly described in the TPM specification and can be relied > > on to detect the Failure state: in Failure mode, the TPM doesn't provide > > any crypto operations. Including access to attributes and configuration > > registers. > > It seems persistent between different TPM manufacturers, at least to the > > degree I was able to verify. > > > > drivers/char/tpm/tpm-chip.c | 23 +++++++++++++++-------- > > drivers/char/tpm/tpm2-cmd.c | 12 ++++++++++-- > > include/linux/tpm.h | 1 + > > 3 files changed, 26 insertions(+), 10 deletions(-) > > > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > > index ddaeceb7e109..ff2367c447fb 100644 > > --- a/drivers/char/tpm/tpm-chip.c > > +++ b/drivers/char/tpm/tpm-chip.c > > @@ -574,20 +574,25 @@ static int tpm_get_pcr_allocation(struct tpm_chip *chip) > > int tpm_chip_register(struct tpm_chip *chip) > > { > > int rc; > > + bool limited_mode = false; Introduce this flat to mark that TPM is in Upgrade/Failure mode and the functionality is limited. > > > > rc = tpm_chip_start(chip); > > if (rc) > > return rc; > > rc = tpm_auto_startup(chip); > > - if (rc) { > > + if (rc == -EIO) { > > + limited_mode = true; > > + } else if (rc) { As described earlier, during startup we can distinguish what mode TPM is running in. So check the return value here and set limited_mode flag accordingly. I am not sure that returning one of the errno values here is good choice as it might correlate with legit error returned by hardware communication layer. Any comments/suggestions are welcome. > > tpm_chip_stop(chip); > > return rc; > > } > > > > - rc = tpm_get_pcr_allocation(chip); > > - tpm_chip_stop(chip); > > - if (rc) > > - return rc; > > + if (!limited_mode) { > > + rc = tpm_get_pcr_allocation(chip); > > + tpm_chip_stop(chip); > > + if (rc) > > + return rc; > > + } As mentioned above, if TPM is in Upgrade/Failure mode, the call to tpm_get_pcr_allocation will 100% fail, so avoid calling it. > > > > tpm_sysfs_add_device(chip); > > > > @@ -595,9 +600,11 @@ int tpm_chip_register(struct tpm_chip *chip) > > > > tpm_add_ppi(chip); > > > > - rc = tpm_add_hwrng(chip); > > - if (rc) > > - goto out_ppi; > > + if (!limited_mode) { > > + rc = tpm_add_hwrng(chip); > > + if (rc) > > + goto out_ppi; > > + } Same as with tpm_get_pcr_allocation. Call to tpm_add_hwrng will fail when limited_mode is set to true; > > > > rc = tpm_add_char_device(chip); > > if (rc) > > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c > > index a25815a6f625..7468353ed67d 100644 > > --- a/drivers/char/tpm/tpm2-cmd.c > > +++ b/drivers/char/tpm/tpm2-cmd.c > > @@ -718,7 +718,8 @@ static int tpm2_startup(struct tpm_chip *chip) > > * sequence > > * @chip: TPM chip to use > > * > > - * Returns 0 on success, < 0 in case of fatal error. > > + * Returns 0 on success, -ENODEV in case of fatal error, > > + * -EIO in case of Reduced/Upgrade mode > > */ > > int tpm2_auto_startup(struct tpm_chip *chip) > > { > > @@ -729,7 +730,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) > > goto out; > > > > rc = tpm2_do_selftest(chip); > > - if (rc && rc != TPM2_RC_INITIALIZE) > > + if (rc == TPM2_RC_UPGRADE) { > > + rc = -EIO; > > + goto out; > > + } else if (rc && rc != TPM2_RC_INITIALIZE) Checking explicitly if TPM is in Upgrade mode. If it is, return immediately as all other calls (except Firmware upgrade ones) to TPM will be failing. > > goto out; > > > > if (rc == TPM2_RC_INITIALIZE) { > > @@ -743,6 +747,10 @@ int tpm2_auto_startup(struct tpm_chip *chip) > > } > > > > rc = tpm2_get_cc_attrs_tbl(chip); > > + if (rc) { /* Succeeded until here, but failed -> reduced mode */ > > + rc = -EIO; > > + goto out; > > + } If execution context reaches this point but fails here, the conclusion can be drawn that that TPM is in Failure mode and needs recovery. Indicate it by settings appropriate return value and exit. > > > > out: > > if (rc > 0) > > diff --git a/include/linux/tpm.h b/include/linux/tpm.h > > index aa11fe323c56..e873c42907f0 100644 > > --- a/include/linux/tpm.h > > +++ b/include/linux/tpm.h > > @@ -207,6 +207,7 @@ enum tpm2_return_codes { > > TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ > > TPM2_RC_FAILURE = 0x0101, > > TPM2_RC_DISABLED = 0x0120, > > + TPM2_RC_UPGRADE = 0x012D, > > TPM2_RC_COMMAND_CODE = 0x0143, > > TPM2_RC_TESTING = 0x090A, /* RC_WARN */ > > TPM2_RC_REFERENCE_H0 = 0x0910, > > -- > > 2.20.1 > > > > Hope that's give a bit more light to what is it all about. Let me know if you have any other questions, I would be glad to help. > > /Jarkko Kind regards, Borys ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules 2021-07-30 14:24 ` Borys Movchan @ 2021-08-02 8:35 ` Jarkko Sakkinen 0 siblings, 0 replies; 4+ messages in thread From: Jarkko Sakkinen @ 2021-08-02 8:35 UTC (permalink / raw) To: Borys Movchan Cc: Borys Movchan, Peter Huewe, Jason Gunthorpe, kernel, linux-integrity, linux-kernel On Fri, Jul 30, 2021 at 04:24:01PM +0200, Borys Movchan wrote: > On 7/28/21 11:58 PM, Jarkko Sakkinen wrote: > > On Wed, Jul 28, 2021 at 12:57:30PM +0200, Borys Movchan wrote: > > > If something went wrong during the TPM firmware upgrade, > > > like power failure or the firmware image file get corrupted, > > > the TPM might end up in Upgrade or Failure mode upon the > > > next start. The state is persistent between the TPM power > > > cycle/restart. > > > > > > According to TPM specification: > > > * If the TPM is in Upgrade mode, it will answer with > > > TPM2_RC_UPGRADE to all commands except Field Upgrade > > > related ones. > > > * If the TPM is in Failure mode, it will allow performing > > > TPM initialization but will not provide any crypto > > > operations. Will happily respond to Field Upgrade calls. > > > > > > The fix adds the possibility to detect an active state of > > > the TPM and gives the user-space a chance to finish the > > > firmware upgrade/recover the TPM. > > > > This is different than telling what the patch does. It's just > > describing a goal, but does not describe how the driver is > > changed, and reasons for doing that. > > > > For instance, you check 'limited_mode' flag in a few sites. > > How can I know that those are exactly the locations where this > > needs to be done? > > > > Seems like I got what you are looking for. Let me try to explain the > reasoning > and doubts regarding what I meant under my change. Please try to nail this in the commit message instead, and I'll then review that. /Jarkko ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2021-08-02 8:35 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-07-28 10:57 [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules Borys Movchan 2021-07-28 21:58 ` Jarkko Sakkinen 2021-07-30 14:24 ` Borys Movchan 2021-08-02 8:35 ` Jarkko Sakkinen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).