[PATCH 2/8] drivers: rollback: Add TPM2 implementation of rollback devices

seanedmond
2023-09-12T09:48:27Z

From: Stephen Carlson <stcarlso@linux.microsoft.com>

This implementation of the rollback uclass driver allows existing TPM2
devices declared in the device tree to be referenced for storing the OS
anti-rollback counter, using the TPM2 non-volatile storage API. The
rollback device must be a child of the TPM device. For example:

tpm2 {
compatible = "sandbox,tpm2";

rollback@1 {
compatible = "tpm,rollback";
rollback-nv-index = <0x1001007>;
};
};

Signed-off-by: Stephen Carlson <stcarlso@linux.microsoft.com>
Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
---
MAINTAINERS | 1 +
drivers/rollback/Makefile | 1 +
drivers/rollback/rollback-tpm.c | 117 ++++++++++++++++++++++++++++++++
include/tpm-v2.h | 17 +++++
lib/tpm-v2.c | 48 +++++++++++++
5 files changed, 184 insertions(+)
create mode 100644 drivers/rollback/rollback-tpm.c

diff --git a/MAINTAINERS b/MAINTAINERS
index de14724c27..e5ac889db4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1446,6 +1446,7 @@ F: drivers/rollback/Kconfig
F: drivers/rollback/Makefile
F: drivers/rollback/rollback-sandbox.c
F: drivers/rollback/rollback-uclass.c
+F: drivers/security/rollback-tpm.c

SEMIHOSTING
R: Sean Anderson <sean.anderson@seco.com>
diff --git a/drivers/rollback/Makefile b/drivers/rollback/Makefile
index 4e7fa46041..63c08863ca 100644
--- a/drivers/rollback/Makefile
+++ b/drivers/rollback/Makefile
@@ -4,3 +4,4 @@

obj-$(CONFIG_DM_ROLLBACK) += rollback-uclass.o
obj-$(CONFIG_ROLLBACK_SANDBOX) += rollback-sandbox.o
+obj-$(CONFIG_ROLLBACK_TPM) += rollback-tpm.o
\ No newline at end of file
diff --git a/drivers/rollback/rollback-tpm.c b/drivers/rollback/rollback-tpm.c
new file mode 100644
index 0000000000..3bb6214042
--- /dev/null
+++ b/drivers/rollback/rollback-tpm.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021 Microsoft, Inc
+ * Written by Stephen Carlson <stcarlso@microsoft.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <rollback.h>
+#include <tpm_api.h>
+
+struct rollback_state {
+ u32 nv_index;
+ struct udevice *tpm_dev;
+};
+
+static int tpm_rollback_idx_get(struct udevice *dev, u64 *rollback_idx)
+{
+ struct rollback_state *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!rollback_idx)
+ return -EINVAL;
+
+ ret = tpm2_nv_read_value(priv->tpm_dev, priv->nv_index, rollback_idx, sizeof(u64));
+ if (ret) {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "Unable to read rollback number from TPM (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int tpm_rollback_idx_set(struct udevice *dev, u64 rollback_idx)
+{
+ int ret;
+ struct rollback_state *priv = dev_get_priv(dev);
+
+ ret = tpm2_nv_write_value(priv->tpm_dev, priv->nv_index, &rollback_idx, sizeof(u64));
+ if (ret) {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "Unable to write anti-rollback version to TPM (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rollback_ops tpm_rollback_ops = {
+ .rollback_idx_get = tpm_rollback_idx_get,
+ .rollback_idx_set = tpm_rollback_idx_set,
+};
+
+static int tpm_rollback_probe(struct udevice *dev)
+{
+ struct rollback_state *priv = dev_get_priv(dev);
+ int ret;
+
+ /* initialize the TPM rollback counter NV index
+ * and initial to 0. Note, this driver provides
+ * a NULL policy.
+ */
+ ret = tpm_rollback_counter_init(priv->tpm_dev, priv->nv_index,
+ NULL, 0);
+ if (ret) {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "TPM rollback init failed (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tpm_rollback_remove(struct udevice *dev)
+{
+ struct rollback_state *priv = dev_get_priv(dev);
+
+ return tpm_close(priv->tpm_dev);
+}
+
+static int tpm_rollback_ofdata_to_platdata(struct udevice *dev)
+{
+ struct udevice *parent_dev;
+ struct rollback_state *priv = dev_get_priv(dev);
+
+ priv->nv_index = (u32)dev_read_u32_default(dev, "rollback-nv-index", 0);
+
+ parent_dev = dev_get_parent(dev);
+
+ if (parent_dev->driver->id == UCLASS_TPM) {
+ priv->tpm_dev = parent_dev;
+ } else {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "TPM rollback must be a child node of a TPM\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id tpm_rollback_ids[] = {
+ { .compatible = "tpm,rollback" },
+ { }
+};
+
+U_BOOT_DRIVER(rollback_tpm) = {
+ .name = "rollback_tpm",
+ .id = UCLASS_ROLLBACK,
+ .priv_auto = sizeof(struct rollback_state),
+ .of_match = tpm_rollback_ids,
+ .of_to_plat = tpm_rollback_ofdata_to_platdata,
+ .probe = tpm_rollback_probe,
+ .remove = tpm_rollback_remove,
+ .ops = &tpm_rollback_ops,
+};
diff --git a/include/tpm-v2.h b/include/tpm-v2.h
index 2b6980e441..8c441066a0 100644
--- a/include/tpm-v2.h
+++ b/include/tpm-v2.h
@@ -321,6 +321,7 @@ enum tpm2_return_codes {
TPM2_RC_COMMAND_CODE = TPM2_RC_VER1 + 0x0043,
TPM2_RC_AUTHSIZE = TPM2_RC_VER1 + 0x0044,
TPM2_RC_AUTH_CONTEXT = TPM2_RC_VER1 + 0x0045,
+ TPM2_RC_NV_UNINITIALIZED = TPM2_RC_VER1 + 0x04a,
TPM2_RC_NV_DEFINED = TPM2_RC_VER1 + 0x004c,
TPM2_RC_NEEDS_TEST = TPM2_RC_VER1 + 0x0053,
TPM2_RC_WARN = 0x0900,
@@ -706,4 +707,20 @@ u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
*/
u32 tpm2_auto_start(struct udevice *dev);

+/**
+ * tpm_rollback_counter_init() - Initialize a TPM rollback counter
+ * (used by the TPM-backed rollback device)
+ *
+ * Will define the non-volitile index and initialize to 0 if it hasn't
+ * been created previously.
+ *
+ * @dev: TPM device
+ * @nv_index: NV index to initialize
+ * @nv_policy: NV index policy (pass NULL for no policy)
+ * @nv_policy_size: NV index policy size (pass 0 for no policy)
+ * Return: result of the operation
+ */
+int tpm_rollback_counter_init(struct udevice *dev, u32 nv_index,
+ const u8 *nv_policy, size_t nv_policy_size);
+
#endif /* __TPM_V2_H */
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c
index 9ab5b46df1..c3c469eb35 100644
--- a/lib/tpm-v2.c
+++ b/lib/tpm-v2.c
@@ -742,3 +742,51 @@ u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,

return 0;
}
+
+int tpm_rollback_counter_init(struct udevice *dev, u32 nv_index,
+ const u8 *nv_policy, size_t nv_policy_size)
+{
+ int ret;
+ u64 data;
+ u64 data0 = 0;
+
+ ret = tpm_open(dev);
+ if (ret == -EBUSY) {
+ log(UCLASS_ROLLBACK, LOGL_DEBUG,
+ "Existing TPM session found, reusing\n");
+ } else {
+ if (ret) {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "TPM initialization failed (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = tpm2_auto_start(dev);
+ if (ret) {
+ log(UCLASS_ROLLBACK, LOGL_ERR,
+ "TPM startup failed (ret=%d)\n", ret);
+ return ret;
+ }
+ }
+
+ if (ret) {
+ log_err("TPM startup failed\n");
+ return ret;
+ }
+
+ /* test reading NV index from TPM */
+ ret = tpm2_nv_read_value(dev, nv_index, &data, sizeof(u64));
+
+ if (ret) {
+ /*read failed. Assume the NV index hasn't been defined yet */
+ ret = tpm2_nv_define_space(dev, nv_index, sizeof(u64), TPMA_NV_PPREAD |
+ TPMA_NV_PPWRITE | TPMA_NV_PLATFORMCREATE,
+ nv_policy, nv_policy_size);
+
+ /* initialize the rollback counter to 0 */
+ if (ret == TPM2_RC_NV_DEFINED || !ret)
+ ret = tpm2_nv_write_value(dev, nv_index, &data0, sizeof(u64));
+ }
+
+ return ret;
+}
--
2.40.0