From: Stefano Garzarella <sgarzare@redhat.com>
Add driver for the vTPM defined by the AMD SVSM spec [1].
The specification defines a protocol that a SEV-SNP guest OS can use to
discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM)
in the guest context, but at a more privileged level (VMPL0).
The new tpm-svsm platform driver uses two functions exposed by x86/sev
to verify that the device is actually emulated by the platform and to
send commands and receive responses.
The device cannot be hot-plugged/unplugged as it is emulated by the
platform, so we can use module_platform_driver_probe(). The probe
function will only check whether in the current runtime configuration,
SVSM is present and provides a vTPM.
This device does not support interrupts and sends responses to commands
synchronously. In order to have .recv() called just after .send() in
tpm_try_transmit(), the .status() callback returns 0, and both
.req_complete_mask and .req_complete_val are set to 0.
[1] "Secure VM Service Module for SEV-SNP Guests"
Publication # 58019 Revision: 1.00
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
---
v6:
- removed the `locality` field (set to 0) and the FIXME comment [Jarkko]
v5:
- removed cancel/status/req_* ops after rebase on master that cotains
commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt")
v4:
- moved "asm" includes after the "linux" includes [Tom]
- allocated buffer separately [Tom/Jarkko/Jason]
v3:
- removed send_recv() ops and followed the ftpm driver implementing .status,
.req_complete_mask, .req_complete_val, etc. [Jarkko]
- removed link to the spec because those URLs are unstable [Borislav]
---
drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++
drivers/char/tpm/Kconfig | 10 +++
drivers/char/tpm/Makefile | 1 +
3 files changed, 139 insertions(+)
create mode 100644 drivers/char/tpm/tpm_svsm.c
diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c
new file mode 100644
index 000000000000..b9242c9eab87
--- /dev/null
+++ b/drivers/char/tpm/tpm_svsm.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ *
+ * Driver for the vTPM defined by the AMD SVSM spec [1].
+ *
+ * The specification defines a protocol that a SEV-SNP guest OS can use to
+ * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM)
+ * in the guest context, but at a more privileged level (usually VMPL0).
+ *
+ * [1] "Secure VM Service Module for SEV-SNP Guests"
+ * Publication # 58019 Revision: 1.00
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/tpm_svsm.h>
+
+#include <asm/sev.h>
+
+#include "tpm.h"
+
+struct tpm_svsm_priv {
+ void *buffer;
+};
+
+static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev);
+ int ret;
+
+ ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len);
+ if (ret)
+ return ret;
+
+ /*
+ * The SVSM call uses the same buffer for the command and for the
+ * response, so after this call, the buffer will contain the response
+ * that can be used by .recv() op.
+ */
+ return snp_svsm_vtpm_send_command(priv->buffer);
+}
+
+static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev);
+
+ /*
+ * The internal buffer contains the response after we send the command
+ * to SVSM.
+ */
+ return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len);
+}
+
+static struct tpm_class_ops tpm_chip_ops = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .recv = tpm_svsm_recv,
+ .send = tpm_svsm_send,
+};
+
+static int __init tpm_svsm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tpm_svsm_priv *priv;
+ struct tpm_chip *chip;
+ int err;
+
+ if (!snp_svsm_vtpm_probe())
+ return -ENODEV;
+
+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /*
+ * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER
+ * in tpm_svsm.h).
+ */
+ priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0);
+ if (!priv->buffer)
+ return -ENOMEM;
+
+ chip = tpmm_chip_alloc(dev, &tpm_chip_ops);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ dev_set_drvdata(&chip->dev, priv);
+
+ err = tpm2_probe(chip);
+ if (err)
+ return err;
+
+ err = tpm_chip_register(chip);
+ if (err)
+ return err;
+
+ dev_info(dev, "SNP SVSM vTPM %s device\n",
+ (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2");
+
+ return 0;
+}
+
+static void __exit tpm_svsm_remove(struct platform_device *pdev)
+{
+ struct tpm_chip *chip = platform_get_drvdata(pdev);
+
+ tpm_chip_unregister(chip);
+}
+
+/*
+ * tpm_svsm_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound
+ * at runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver tpm_svsm_driver __refdata = {
+ .remove = __exit_p(tpm_svsm_remove),
+ .driver = {
+ .name = "tpm-svsm",
+ },
+};
+
+module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
+
+MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tpm-svsm");
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index fe4f3a609934..dddd702b2454 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -234,5 +234,15 @@ config TCG_FTPM_TEE
help
This driver proxies for firmware TPM running in TEE.
+config TCG_SVSM
+ tristate "SNP SVSM vTPM interface"
+ depends on AMD_MEM_ENCRYPT
+ help
+ This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest
+ OS can use to discover and talk to a vTPM emulated by the Secure VM
+ Service Module (SVSM) in the guest context, but at a more privileged
+ level (usually VMPL0). To compile this driver as a module, choose M
+ here; the module will be called tpm_svsm.
+
source "drivers/char/tpm/st33zp24/Kconfig"
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 2b004df8c04b..9de1b3ea34a9 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -45,3 +45,4 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o
obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o
obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
+obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o
--
2.49.0
On Thu, Apr 3, 2025 at 3:10 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > From: Stefano Garzarella <sgarzare@redhat.com> > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > The specification defines a protocol that a SEV-SNP guest OS can use to > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > in the guest context, but at a more privileged level (VMPL0). > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > to verify that the device is actually emulated by the platform and to > send commands and receive responses. > > The device cannot be hot-plugged/unplugged as it is emulated by the > platform, so we can use module_platform_driver_probe(). The probe > function will only check whether in the current runtime configuration, > SVSM is present and provides a vTPM. > > This device does not support interrupts and sends responses to commands > synchronously. In order to have .recv() called just after .send() in > tpm_try_transmit(), the .status() callback returns 0, and both > .req_complete_mask and .req_complete_val are set to 0. > > [1] "Secure VM Service Module for SEV-SNP Guests" > Publication # 58019 Revision: 1.00 > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > --- > v6: > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > v5: > - removed cancel/status/req_* ops after rebase on master that cotains > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > v4: > - moved "asm" includes after the "linux" includes [Tom] > - allocated buffer separately [Tom/Jarkko/Jason] > v3: > - removed send_recv() ops and followed the ftpm driver implementing .status, > .req_complete_mask, .req_complete_val, etc. [Jarkko] > - removed link to the spec because those URLs are unstable [Borislav] > --- > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > drivers/char/tpm/Kconfig | 10 +++ > drivers/char/tpm/Makefile | 1 + > 3 files changed, 139 insertions(+) > create mode 100644 drivers/char/tpm/tpm_svsm.c > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > new file mode 100644 > index 000000000000..b9242c9eab87 > --- /dev/null > +++ b/drivers/char/tpm/tpm_svsm.c > @@ -0,0 +1,128 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > + * > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > + * > + * The specification defines a protocol that a SEV-SNP guest OS can use to > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > + * in the guest context, but at a more privileged level (usually VMPL0). > + * > + * [1] "Secure VM Service Module for SEV-SNP Guests" > + * Publication # 58019 Revision: 1.00 > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/tpm_svsm.h> > + > +#include <asm/sev.h> > + > +#include "tpm.h" > + > +struct tpm_svsm_priv { > + void *buffer; > +}; > + > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > +{ > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > + int ret; > + > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > + if (ret) > + return ret; > + > + /* > + * The SVSM call uses the same buffer for the command and for the > + * response, so after this call, the buffer will contain the response > + * that can be used by .recv() op. > + */ > + return snp_svsm_vtpm_send_command(priv->buffer); > +} > + > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > +{ > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > + > + /* > + * The internal buffer contains the response after we send the command > + * to SVSM. > + */ > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > +} > + > +static struct tpm_class_ops tpm_chip_ops = { > + .flags = TPM_OPS_AUTO_STARTUP, > + .recv = tpm_svsm_recv, > + .send = tpm_svsm_send, > +}; > + > +static int __init tpm_svsm_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct tpm_svsm_priv *priv; > + struct tpm_chip *chip; > + int err; > + > + if (!snp_svsm_vtpm_probe()) > + return -ENODEV; > + > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + /* > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > + * in tpm_svsm.h). > + */ > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > + if (!priv->buffer) > + return -ENOMEM; > + > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > + if (IS_ERR(chip)) > + return PTR_ERR(chip); > + > + dev_set_drvdata(&chip->dev, priv); > + > + err = tpm2_probe(chip); Our testing is showing that tpm2_probe is hitting a null pointer deref in tpm_transmit. > + if (err) > + return err; > + > + err = tpm_chip_register(chip); > + if (err) > + return err; > + > + dev_info(dev, "SNP SVSM vTPM %s device\n", > + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2"); > + > + return 0; > +} > + > +static void __exit tpm_svsm_remove(struct platform_device *pdev) > +{ > + struct tpm_chip *chip = platform_get_drvdata(pdev); > + > + tpm_chip_unregister(chip); > +} > + > +/* > + * tpm_svsm_remove() lives in .exit.text. For drivers registered via > + * module_platform_driver_probe() this is ok because they cannot get unbound > + * at runtime. So mark the driver struct with __refdata to prevent modpost > + * triggering a section mismatch warning. > + */ > +static struct platform_driver tpm_svsm_driver __refdata = { > + .remove = __exit_p(tpm_svsm_remove), > + .driver = { > + .name = "tpm-svsm", > + }, > +}; > + > +module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe); > + > +MODULE_DESCRIPTION("SNP SVSM vTPM Driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:tpm-svsm"); > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > index fe4f3a609934..dddd702b2454 100644 > --- a/drivers/char/tpm/Kconfig > +++ b/drivers/char/tpm/Kconfig > @@ -234,5 +234,15 @@ config TCG_FTPM_TEE > help > This driver proxies for firmware TPM running in TEE. > > +config TCG_SVSM > + tristate "SNP SVSM vTPM interface" > + depends on AMD_MEM_ENCRYPT > + help > + This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest > + OS can use to discover and talk to a vTPM emulated by the Secure VM > + Service Module (SVSM) in the guest context, but at a more privileged > + level (usually VMPL0). To compile this driver as a module, choose M > + here; the module will be called tpm_svsm. > + > source "drivers/char/tpm/st33zp24/Kconfig" > endif # TCG_TPM > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > index 2b004df8c04b..9de1b3ea34a9 100644 > --- a/drivers/char/tpm/Makefile > +++ b/drivers/char/tpm/Makefile > @@ -45,3 +45,4 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o > obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o > obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o > obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o > +obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o > -- > 2.49.0 > -- -Dionna Glaze, PhD, CISSP, CCSP (she/her)
On Fri, 4 Apr 2025 at 19:32, Dionna Amalie Glaze <dionnaglaze@google.com> wrote: > > On Thu, Apr 3, 2025 at 3:10 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > > > From: Stefano Garzarella <sgarzare@redhat.com> > > > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > > > The specification defines a protocol that a SEV-SNP guest OS can use to > > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > in the guest context, but at a more privileged level (VMPL0). > > > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > > to verify that the device is actually emulated by the platform and to > > send commands and receive responses. > > > > The device cannot be hot-plugged/unplugged as it is emulated by the > > platform, so we can use module_platform_driver_probe(). The probe > > function will only check whether in the current runtime configuration, > > SVSM is present and provides a vTPM. > > > > This device does not support interrupts and sends responses to commands > > synchronously. In order to have .recv() called just after .send() in > > tpm_try_transmit(), the .status() callback returns 0, and both > > .req_complete_mask and .req_complete_val are set to 0. > > > > [1] "Secure VM Service Module for SEV-SNP Guests" > > Publication # 58019 Revision: 1.00 > > > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > > --- > > v6: > > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > > v5: > > - removed cancel/status/req_* ops after rebase on master that cotains > > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > > v4: > > - moved "asm" includes after the "linux" includes [Tom] > > - allocated buffer separately [Tom/Jarkko/Jason] > > v3: > > - removed send_recv() ops and followed the ftpm driver implementing .status, > > .req_complete_mask, .req_complete_val, etc. [Jarkko] > > - removed link to the spec because those URLs are unstable [Borislav] > > --- > > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > > drivers/char/tpm/Kconfig | 10 +++ > > drivers/char/tpm/Makefile | 1 + > > 3 files changed, 139 insertions(+) > > create mode 100644 drivers/char/tpm/tpm_svsm.c > > > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > > new file mode 100644 > > index 000000000000..b9242c9eab87 > > --- /dev/null > > +++ b/drivers/char/tpm/tpm_svsm.c > > @@ -0,0 +1,128 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > > + * > > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > > + * > > + * The specification defines a protocol that a SEV-SNP guest OS can use to > > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > + * in the guest context, but at a more privileged level (usually VMPL0). > > + * > > + * [1] "Secure VM Service Module for SEV-SNP Guests" > > + * Publication # 58019 Revision: 1.00 > > + */ > > + > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/platform_device.h> > > +#include <linux/tpm_svsm.h> > > + > > +#include <asm/sev.h> > > + > > +#include "tpm.h" > > + > > +struct tpm_svsm_priv { > > + void *buffer; > > +}; > > + > > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > > +{ > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > + int ret; > > + > > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > > + if (ret) > > + return ret; > > + > > + /* > > + * The SVSM call uses the same buffer for the command and for the > > + * response, so after this call, the buffer will contain the response > > + * that can be used by .recv() op. > > + */ > > + return snp_svsm_vtpm_send_command(priv->buffer); > > +} > > + > > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > > +{ > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > + > > + /* > > + * The internal buffer contains the response after we send the command > > + * to SVSM. > > + */ > > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > > +} > > + > > +static struct tpm_class_ops tpm_chip_ops = { > > + .flags = TPM_OPS_AUTO_STARTUP, > > + .recv = tpm_svsm_recv, > > + .send = tpm_svsm_send, > > +}; > > + > > +static int __init tpm_svsm_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct tpm_svsm_priv *priv; > > + struct tpm_chip *chip; > > + int err; > > + > > + if (!snp_svsm_vtpm_probe()) > > + return -ENODEV; > > + > > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + > > + /* > > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > > + * in tpm_svsm.h). > > + */ > > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > > + if (!priv->buffer) > > + return -ENOMEM; > > + > > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > > + if (IS_ERR(chip)) > > + return PTR_ERR(chip); > > + > > + dev_set_drvdata(&chip->dev, priv); > > + > > + err = tpm2_probe(chip); > > Our testing is showing that tpm2_probe is hitting a null pointer deref > in tpm_transmit. Next time, please share a backtrace. BTW I suspect you're not using Linus' tree, so be sure to backport also commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt"). Without that, you will have a null ptr dereference since .status() is NULL from v5 of this series (as specified in the changelog). Stefano
On Fri, Apr 4, 2025 at 11:37 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > On Fri, 4 Apr 2025 at 19:32, Dionna Amalie Glaze <dionnaglaze@google.com> wrote: > > > > On Thu, Apr 3, 2025 at 3:10 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > > > > > From: Stefano Garzarella <sgarzare@redhat.com> > > > > > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > > > > > The specification defines a protocol that a SEV-SNP guest OS can use to > > > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > in the guest context, but at a more privileged level (VMPL0). > > > > > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > > > to verify that the device is actually emulated by the platform and to > > > send commands and receive responses. > > > > > > The device cannot be hot-plugged/unplugged as it is emulated by the > > > platform, so we can use module_platform_driver_probe(). The probe > > > function will only check whether in the current runtime configuration, > > > SVSM is present and provides a vTPM. > > > > > > This device does not support interrupts and sends responses to commands > > > synchronously. In order to have .recv() called just after .send() in > > > tpm_try_transmit(), the .status() callback returns 0, and both > > > .req_complete_mask and .req_complete_val are set to 0. > > > > > > [1] "Secure VM Service Module for SEV-SNP Guests" > > > Publication # 58019 Revision: 1.00 > > > > > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > > > --- > > > v6: > > > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > > > v5: > > > - removed cancel/status/req_* ops after rebase on master that cotains > > > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > > > v4: > > > - moved "asm" includes after the "linux" includes [Tom] > > > - allocated buffer separately [Tom/Jarkko/Jason] > > > v3: > > > - removed send_recv() ops and followed the ftpm driver implementing .status, > > > .req_complete_mask, .req_complete_val, etc. [Jarkko] > > > - removed link to the spec because those URLs are unstable [Borislav] > > > --- > > > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > > > drivers/char/tpm/Kconfig | 10 +++ > > > drivers/char/tpm/Makefile | 1 + > > > 3 files changed, 139 insertions(+) > > > create mode 100644 drivers/char/tpm/tpm_svsm.c > > > > > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > > > new file mode 100644 > > > index 000000000000..b9242c9eab87 > > > --- /dev/null > > > +++ b/drivers/char/tpm/tpm_svsm.c > > > @@ -0,0 +1,128 @@ > > > +// SPDX-License-Identifier: GPL-2.0-only > > > +/* > > > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > > > + * > > > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > > > + * > > > + * The specification defines a protocol that a SEV-SNP guest OS can use to > > > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > + * in the guest context, but at a more privileged level (usually VMPL0). > > > + * > > > + * [1] "Secure VM Service Module for SEV-SNP Guests" > > > + * Publication # 58019 Revision: 1.00 > > > + */ > > > + > > > +#include <linux/module.h> > > > +#include <linux/kernel.h> > > > +#include <linux/platform_device.h> > > > +#include <linux/tpm_svsm.h> > > > + > > > +#include <asm/sev.h> > > > + > > > +#include "tpm.h" > > > + > > > +struct tpm_svsm_priv { > > > + void *buffer; > > > +}; > > > + > > > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > > > +{ > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > + int ret; > > > + > > > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > > > + if (ret) > > > + return ret; > > > + > > > + /* > > > + * The SVSM call uses the same buffer for the command and for the > > > + * response, so after this call, the buffer will contain the response > > > + * that can be used by .recv() op. > > > + */ > > > + return snp_svsm_vtpm_send_command(priv->buffer); > > > +} > > > + > > > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > > > +{ > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > + > > > + /* > > > + * The internal buffer contains the response after we send the command > > > + * to SVSM. > > > + */ > > > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > > > +} > > > + > > > +static struct tpm_class_ops tpm_chip_ops = { > > > + .flags = TPM_OPS_AUTO_STARTUP, > > > + .recv = tpm_svsm_recv, > > > + .send = tpm_svsm_send, > > > +}; > > > + > > > +static int __init tpm_svsm_probe(struct platform_device *pdev) > > > +{ > > > + struct device *dev = &pdev->dev; > > > + struct tpm_svsm_priv *priv; > > > + struct tpm_chip *chip; > > > + int err; > > > + > > > + if (!snp_svsm_vtpm_probe()) > > > + return -ENODEV; > > > + > > > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > > > + if (!priv) > > > + return -ENOMEM; > > > + > > > + /* > > > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > > > + * in tpm_svsm.h). > > > + */ > > > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > > > + if (!priv->buffer) > > > + return -ENOMEM; > > > + > > > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > > > + if (IS_ERR(chip)) > > > + return PTR_ERR(chip); > > > + > > > + dev_set_drvdata(&chip->dev, priv); > > > + > > > + err = tpm2_probe(chip); > > > > Our testing is showing that tpm2_probe is hitting a null pointer deref > > in tpm_transmit. > > Next time, please share a backtrace. Right, my bad. > > BTW I suspect you're not using Linus' tree, so be sure to backport > also commit 980a573621ea ("tpm: Make > chip->{status,cancel,req_canceled} opt"). > > Without that, you will have a null ptr dereference since .status() is > NULL from v5 of this series (as specified in the changelog). Thanks, I missed that detail. Will report back. > > Stefano > -- -Dionna Glaze, PhD, CISSP, CCSP (she/her)
On Fri, 4 Apr 2025 at 20:59, Dionna Amalie Glaze <dionnaglaze@google.com> wrote: > > On Fri, Apr 4, 2025 at 11:37 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > > > On Fri, 4 Apr 2025 at 19:32, Dionna Amalie Glaze <dionnaglaze@google.com> wrote: > > > > > > On Thu, Apr 3, 2025 at 3:10 AM Stefano Garzarella <sgarzare@redhat.com> wrote: > > > > > > > > From: Stefano Garzarella <sgarzare@redhat.com> > > > > > > > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > > > > > > > The specification defines a protocol that a SEV-SNP guest OS can use to > > > > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > > in the guest context, but at a more privileged level (VMPL0). > > > > > > > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > > > > to verify that the device is actually emulated by the platform and to > > > > send commands and receive responses. > > > > > > > > The device cannot be hot-plugged/unplugged as it is emulated by the > > > > platform, so we can use module_platform_driver_probe(). The probe > > > > function will only check whether in the current runtime configuration, > > > > SVSM is present and provides a vTPM. > > > > > > > > This device does not support interrupts and sends responses to commands > > > > synchronously. In order to have .recv() called just after .send() in > > > > tpm_try_transmit(), the .status() callback returns 0, and both > > > > .req_complete_mask and .req_complete_val are set to 0. > > > > > > > > [1] "Secure VM Service Module for SEV-SNP Guests" > > > > Publication # 58019 Revision: 1.00 > > > > > > > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > > > > --- > > > > v6: > > > > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > > > > v5: > > > > - removed cancel/status/req_* ops after rebase on master that cotains > > > > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > > > > v4: > > > > - moved "asm" includes after the "linux" includes [Tom] > > > > - allocated buffer separately [Tom/Jarkko/Jason] > > > > v3: > > > > - removed send_recv() ops and followed the ftpm driver implementing .status, > > > > .req_complete_mask, .req_complete_val, etc. [Jarkko] > > > > - removed link to the spec because those URLs are unstable [Borislav] > > > > --- > > > > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > > > > drivers/char/tpm/Kconfig | 10 +++ > > > > drivers/char/tpm/Makefile | 1 + > > > > 3 files changed, 139 insertions(+) > > > > create mode 100644 drivers/char/tpm/tpm_svsm.c > > > > > > > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > > > > new file mode 100644 > > > > index 000000000000..b9242c9eab87 > > > > --- /dev/null > > > > +++ b/drivers/char/tpm/tpm_svsm.c > > > > @@ -0,0 +1,128 @@ > > > > +// SPDX-License-Identifier: GPL-2.0-only > > > > +/* > > > > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > > > > + * > > > > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > > > > + * > > > > + * The specification defines a protocol that a SEV-SNP guest OS can use to > > > > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > > + * in the guest context, but at a more privileged level (usually VMPL0). > > > > + * > > > > + * [1] "Secure VM Service Module for SEV-SNP Guests" > > > > + * Publication # 58019 Revision: 1.00 > > > > + */ > > > > + > > > > +#include <linux/module.h> > > > > +#include <linux/kernel.h> > > > > +#include <linux/platform_device.h> > > > > +#include <linux/tpm_svsm.h> > > > > + > > > > +#include <asm/sev.h> > > > > + > > > > +#include "tpm.h" > > > > + > > > > +struct tpm_svsm_priv { > > > > + void *buffer; > > > > +}; > > > > + > > > > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > > > > +{ > > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > > + int ret; > > > > + > > > > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + /* > > > > + * The SVSM call uses the same buffer for the command and for the > > > > + * response, so after this call, the buffer will contain the response > > > > + * that can be used by .recv() op. > > > > + */ > > > > + return snp_svsm_vtpm_send_command(priv->buffer); > > > > +} > > > > + > > > > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > > > > +{ > > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > > + > > > > + /* > > > > + * The internal buffer contains the response after we send the command > > > > + * to SVSM. > > > > + */ > > > > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > > > > +} > > > > + > > > > +static struct tpm_class_ops tpm_chip_ops = { > > > > + .flags = TPM_OPS_AUTO_STARTUP, > > > > + .recv = tpm_svsm_recv, > > > > + .send = tpm_svsm_send, > > > > +}; > > > > + > > > > +static int __init tpm_svsm_probe(struct platform_device *pdev) > > > > +{ > > > > + struct device *dev = &pdev->dev; > > > > + struct tpm_svsm_priv *priv; > > > > + struct tpm_chip *chip; > > > > + int err; > > > > + > > > > + if (!snp_svsm_vtpm_probe()) > > > > + return -ENODEV; > > > > + > > > > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > > > > + if (!priv) > > > > + return -ENOMEM; > > > > + > > > > + /* > > > > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > > > > + * in tpm_svsm.h). > > > > + */ > > > > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > > > > + if (!priv->buffer) > > > > + return -ENOMEM; > > > > + > > > > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > > > > + if (IS_ERR(chip)) > > > > + return PTR_ERR(chip); > > > > + > > > > + dev_set_drvdata(&chip->dev, priv); > > > > + > > > > + err = tpm2_probe(chip); > > > > > > Our testing is showing that tpm2_probe is hitting a null pointer deref > > > in tpm_transmit. > > > > Next time, please share a backtrace. > > Right, my bad. > > > > BTW I suspect you're not using Linus' tree, so be sure to backport > > also commit 980a573621ea ("tpm: Make > > chip->{status,cancel,req_canceled} opt"). > > > > Without that, you will have a null ptr dereference since .status() is > > NULL from v5 of this series (as specified in the changelog). > > Thanks, I missed that detail. Will report back. I'm preparing v7, is your issue solved or do we need to investigate better before sending a v7? Thanks, Stefano
On Thu, Apr 03, 2025 at 12:09:41PM +0200, Stefano Garzarella wrote: > From: Stefano Garzarella <sgarzare@redhat.com> > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > The specification defines a protocol that a SEV-SNP guest OS can use to > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > in the guest context, but at a more privileged level (VMPL0). > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > to verify that the device is actually emulated by the platform and to > send commands and receive responses. > > The device cannot be hot-plugged/unplugged as it is emulated by the > platform, so we can use module_platform_driver_probe(). The probe > function will only check whether in the current runtime configuration, > SVSM is present and provides a vTPM. > > This device does not support interrupts and sends responses to commands > synchronously. In order to have .recv() called just after .send() in > tpm_try_transmit(), the .status() callback returns 0, and both > .req_complete_mask and .req_complete_val are set to 0. > > [1] "Secure VM Service Module for SEV-SNP Guests" > Publication # 58019 Revision: 1.00 > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > --- > v6: > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > v5: > - removed cancel/status/req_* ops after rebase on master that cotains > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > v4: > - moved "asm" includes after the "linux" includes [Tom] > - allocated buffer separately [Tom/Jarkko/Jason] > v3: > - removed send_recv() ops and followed the ftpm driver implementing .status, > .req_complete_mask, .req_complete_val, etc. [Jarkko] > - removed link to the spec because those URLs are unstable [Borislav] > --- > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > drivers/char/tpm/Kconfig | 10 +++ > drivers/char/tpm/Makefile | 1 + > 3 files changed, 139 insertions(+) > create mode 100644 drivers/char/tpm/tpm_svsm.c > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > new file mode 100644 > index 000000000000..b9242c9eab87 > --- /dev/null > +++ b/drivers/char/tpm/tpm_svsm.c > @@ -0,0 +1,128 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > + * > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > + * > + * The specification defines a protocol that a SEV-SNP guest OS can use to > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > + * in the guest context, but at a more privileged level (usually VMPL0). > + * > + * [1] "Secure VM Service Module for SEV-SNP Guests" > + * Publication # 58019 Revision: 1.00 > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/platform_device.h> > +#include <linux/tpm_svsm.h> > + > +#include <asm/sev.h> > + > +#include "tpm.h" > + > +struct tpm_svsm_priv { > + void *buffer; > +}; > + > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > +{ > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > + int ret; > + > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > + if (ret) > + return ret; > + > + /* > + * The SVSM call uses the same buffer for the command and for the > + * response, so after this call, the buffer will contain the response > + * that can be used by .recv() op. > + */ > + return snp_svsm_vtpm_send_command(priv->buffer); > +} > + > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > +{ > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > + > + /* > + * The internal buffer contains the response after we send the command > + * to SVSM. > + */ > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > +} > + > +static struct tpm_class_ops tpm_chip_ops = { > + .flags = TPM_OPS_AUTO_STARTUP, > + .recv = tpm_svsm_recv, > + .send = tpm_svsm_send, > +}; > + > +static int __init tpm_svsm_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct tpm_svsm_priv *priv; > + struct tpm_chip *chip; > + int err; > + > + if (!snp_svsm_vtpm_probe()) > + return -ENODEV; > + > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + /* > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > + * in tpm_svsm.h). > + */ > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > + if (!priv->buffer) > + return -ENOMEM; > + > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > + if (IS_ERR(chip)) > + return PTR_ERR(chip); > + > + dev_set_drvdata(&chip->dev, priv); > + > + err = tpm2_probe(chip); > + if (err) > + return err; > + > + err = tpm_chip_register(chip); > + if (err) > + return err; > + > + dev_info(dev, "SNP SVSM vTPM %s device\n", > + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2"); > + > + return 0; > +} > + > +static void __exit tpm_svsm_remove(struct platform_device *pdev) > +{ > + struct tpm_chip *chip = platform_get_drvdata(pdev); > + > + tpm_chip_unregister(chip); > +} > + > +/* > + * tpm_svsm_remove() lives in .exit.text. For drivers registered via > + * module_platform_driver_probe() this is ok because they cannot get unbound > + * at runtime. So mark the driver struct with __refdata to prevent modpost > + * triggering a section mismatch warning. > + */ > +static struct platform_driver tpm_svsm_driver __refdata = { > + .remove = __exit_p(tpm_svsm_remove), > + .driver = { > + .name = "tpm-svsm", > + }, > +}; > + > +module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe); > + > +MODULE_DESCRIPTION("SNP SVSM vTPM Driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:tpm-svsm"); > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > index fe4f3a609934..dddd702b2454 100644 > --- a/drivers/char/tpm/Kconfig > +++ b/drivers/char/tpm/Kconfig > @@ -234,5 +234,15 @@ config TCG_FTPM_TEE > help > This driver proxies for firmware TPM running in TEE. > > +config TCG_SVSM > + tristate "SNP SVSM vTPM interface" > + depends on AMD_MEM_ENCRYPT > + help > + This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest > + OS can use to discover and talk to a vTPM emulated by the Secure VM > + Service Module (SVSM) in the guest context, but at a more privileged > + level (usually VMPL0). To compile this driver as a module, choose M > + here; the module will be called tpm_svsm. > + > source "drivers/char/tpm/st33zp24/Kconfig" > endif # TCG_TPM > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > index 2b004df8c04b..9de1b3ea34a9 100644 > --- a/drivers/char/tpm/Makefile > +++ b/drivers/char/tpm/Makefile > @@ -45,3 +45,4 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o > obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o > obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o > obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o > +obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o > -- > 2.49.0 > > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> BR, Jarkko
On Thu, 3 Apr 2025 at 21:05, Jarkko Sakkinen <jarkko@kernel.org> wrote: > > On Thu, Apr 03, 2025 at 12:09:41PM +0200, Stefano Garzarella wrote: > > From: Stefano Garzarella <sgarzare@redhat.com> > > > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > > > The specification defines a protocol that a SEV-SNP guest OS can use to > > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > in the guest context, but at a more privileged level (VMPL0). > > > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > > to verify that the device is actually emulated by the platform and to > > send commands and receive responses. > > > > The device cannot be hot-plugged/unplugged as it is emulated by the > > platform, so we can use module_platform_driver_probe(). The probe > > function will only check whether in the current runtime configuration, > > SVSM is present and provides a vTPM. > > > > This device does not support interrupts and sends responses to commands > > synchronously. In order to have .recv() called just after .send() in > > tpm_try_transmit(), the .status() callback returns 0, and both > > .req_complete_mask and .req_complete_val are set to 0. > > > > [1] "Secure VM Service Module for SEV-SNP Guests" > > Publication # 58019 Revision: 1.00 > > > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > > --- > > v6: > > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > > v5: > > - removed cancel/status/req_* ops after rebase on master that cotains > > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > > v4: > > - moved "asm" includes after the "linux" includes [Tom] > > - allocated buffer separately [Tom/Jarkko/Jason] > > v3: > > - removed send_recv() ops and followed the ftpm driver implementing .status, > > .req_complete_mask, .req_complete_val, etc. [Jarkko] > > - removed link to the spec because those URLs are unstable [Borislav] > > --- > > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > > drivers/char/tpm/Kconfig | 10 +++ > > drivers/char/tpm/Makefile | 1 + > > 3 files changed, 139 insertions(+) > > create mode 100644 drivers/char/tpm/tpm_svsm.c > > > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > > new file mode 100644 > > index 000000000000..b9242c9eab87 > > --- /dev/null > > +++ b/drivers/char/tpm/tpm_svsm.c > > @@ -0,0 +1,128 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > > + * > > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > > + * > > + * The specification defines a protocol that a SEV-SNP guest OS can use to > > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > + * in the guest context, but at a more privileged level (usually VMPL0). > > + * > > + * [1] "Secure VM Service Module for SEV-SNP Guests" > > + * Publication # 58019 Revision: 1.00 > > + */ > > + > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/platform_device.h> > > +#include <linux/tpm_svsm.h> > > + > > +#include <asm/sev.h> > > + > > +#include "tpm.h" > > + > > +struct tpm_svsm_priv { > > + void *buffer; > > +}; > > + > > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > > +{ > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > + int ret; > > + > > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > > + if (ret) > > + return ret; > > + > > + /* > > + * The SVSM call uses the same buffer for the command and for the > > + * response, so after this call, the buffer will contain the response > > + * that can be used by .recv() op. > > + */ > > + return snp_svsm_vtpm_send_command(priv->buffer); > > +} > > + > > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > > +{ > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > + > > + /* > > + * The internal buffer contains the response after we send the command > > + * to SVSM. > > + */ > > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > > +} > > + > > +static struct tpm_class_ops tpm_chip_ops = { > > + .flags = TPM_OPS_AUTO_STARTUP, > > + .recv = tpm_svsm_recv, > > + .send = tpm_svsm_send, > > +}; > > + > > +static int __init tpm_svsm_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct tpm_svsm_priv *priv; > > + struct tpm_chip *chip; > > + int err; > > + > > + if (!snp_svsm_vtpm_probe()) > > + return -ENODEV; > > + > > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > > + if (!priv) > > + return -ENOMEM; > > + > > + /* > > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > > + * in tpm_svsm.h). > > + */ > > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > > + if (!priv->buffer) > > + return -ENOMEM; > > + > > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > > + if (IS_ERR(chip)) > > + return PTR_ERR(chip); > > + > > + dev_set_drvdata(&chip->dev, priv); > > + > > + err = tpm2_probe(chip); > > + if (err) > > + return err; > > + > > + err = tpm_chip_register(chip); > > + if (err) > > + return err; > > + > > + dev_info(dev, "SNP SVSM vTPM %s device\n", > > + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2"); > > + > > + return 0; > > +} > > + > > +static void __exit tpm_svsm_remove(struct platform_device *pdev) > > +{ > > + struct tpm_chip *chip = platform_get_drvdata(pdev); > > + > > + tpm_chip_unregister(chip); > > +} > > + > > +/* > > + * tpm_svsm_remove() lives in .exit.text. For drivers registered via > > + * module_platform_driver_probe() this is ok because they cannot get unbound > > + * at runtime. So mark the driver struct with __refdata to prevent modpost > > + * triggering a section mismatch warning. > > + */ > > +static struct platform_driver tpm_svsm_driver __refdata = { > > + .remove = __exit_p(tpm_svsm_remove), > > + .driver = { > > + .name = "tpm-svsm", > > + }, > > +}; > > + > > +module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe); > > + > > +MODULE_DESCRIPTION("SNP SVSM vTPM Driver"); > > +MODULE_LICENSE("GPL"); > > +MODULE_ALIAS("platform:tpm-svsm"); > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > index fe4f3a609934..dddd702b2454 100644 > > --- a/drivers/char/tpm/Kconfig > > +++ b/drivers/char/tpm/Kconfig > > @@ -234,5 +234,15 @@ config TCG_FTPM_TEE > > help > > This driver proxies for firmware TPM running in TEE. > > > > +config TCG_SVSM > > + tristate "SNP SVSM vTPM interface" > > + depends on AMD_MEM_ENCRYPT > > + help > > + This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest > > + OS can use to discover and talk to a vTPM emulated by the Secure VM > > + Service Module (SVSM) in the guest context, but at a more privileged > > + level (usually VMPL0). To compile this driver as a module, choose M > > + here; the module will be called tpm_svsm. > > + > > source "drivers/char/tpm/st33zp24/Kconfig" > > endif # TCG_TPM > > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > > index 2b004df8c04b..9de1b3ea34a9 100644 > > --- a/drivers/char/tpm/Makefile > > +++ b/drivers/char/tpm/Makefile > > @@ -45,3 +45,4 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o > > obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o > > obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o > > obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o > > +obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o > > -- > > 2.49.0 > > > > > > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> In the next version I'm moving the snp_svsm_vtpm_probe() call from here to sev core where the device is registered. See https://lore.kernel.org/linux-integrity/o2u7p3wb64lcc4sziunr274hyubkgmspzdjcvihbpzkw6mkvpo@sjq3vi4y2qfl/ Do I carry your R-b for this patch (and patch 4) or would you rather take a look at v7? Thanks, Stefano
On Wed, Apr 09, 2025 at 11:43:55AM +0200, Stefano Garzarella wrote: > On Thu, 3 Apr 2025 at 21:05, Jarkko Sakkinen <jarkko@kernel.org> wrote: > > > > On Thu, Apr 03, 2025 at 12:09:41PM +0200, Stefano Garzarella wrote: > > > From: Stefano Garzarella <sgarzare@redhat.com> > > > > > > Add driver for the vTPM defined by the AMD SVSM spec [1]. > > > > > > The specification defines a protocol that a SEV-SNP guest OS can use to > > > discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > in the guest context, but at a more privileged level (VMPL0). > > > > > > The new tpm-svsm platform driver uses two functions exposed by x86/sev > > > to verify that the device is actually emulated by the platform and to > > > send commands and receive responses. > > > > > > The device cannot be hot-plugged/unplugged as it is emulated by the > > > platform, so we can use module_platform_driver_probe(). The probe > > > function will only check whether in the current runtime configuration, > > > SVSM is present and provides a vTPM. > > > > > > This device does not support interrupts and sends responses to commands > > > synchronously. In order to have .recv() called just after .send() in > > > tpm_try_transmit(), the .status() callback returns 0, and both > > > .req_complete_mask and .req_complete_val are set to 0. > > > > > > [1] "Secure VM Service Module for SEV-SNP Guests" > > > Publication # 58019 Revision: 1.00 > > > > > > Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> > > > --- > > > v6: > > > - removed the `locality` field (set to 0) and the FIXME comment [Jarkko] > > > v5: > > > - removed cancel/status/req_* ops after rebase on master that cotains > > > commit 980a573621ea ("tpm: Make chip->{status,cancel,req_canceled} opt") > > > v4: > > > - moved "asm" includes after the "linux" includes [Tom] > > > - allocated buffer separately [Tom/Jarkko/Jason] > > > v3: > > > - removed send_recv() ops and followed the ftpm driver implementing .status, > > > .req_complete_mask, .req_complete_val, etc. [Jarkko] > > > - removed link to the spec because those URLs are unstable [Borislav] > > > --- > > > drivers/char/tpm/tpm_svsm.c | 128 ++++++++++++++++++++++++++++++++++++ > > > drivers/char/tpm/Kconfig | 10 +++ > > > drivers/char/tpm/Makefile | 1 + > > > 3 files changed, 139 insertions(+) > > > create mode 100644 drivers/char/tpm/tpm_svsm.c > > > > > > diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c > > > new file mode 100644 > > > index 000000000000..b9242c9eab87 > > > --- /dev/null > > > +++ b/drivers/char/tpm/tpm_svsm.c > > > @@ -0,0 +1,128 @@ > > > +// SPDX-License-Identifier: GPL-2.0-only > > > +/* > > > + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. > > > + * > > > + * Driver for the vTPM defined by the AMD SVSM spec [1]. > > > + * > > > + * The specification defines a protocol that a SEV-SNP guest OS can use to > > > + * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM) > > > + * in the guest context, but at a more privileged level (usually VMPL0). > > > + * > > > + * [1] "Secure VM Service Module for SEV-SNP Guests" > > > + * Publication # 58019 Revision: 1.00 > > > + */ > > > + > > > +#include <linux/module.h> > > > +#include <linux/kernel.h> > > > +#include <linux/platform_device.h> > > > +#include <linux/tpm_svsm.h> > > > + > > > +#include <asm/sev.h> > > > + > > > +#include "tpm.h" > > > + > > > +struct tpm_svsm_priv { > > > + void *buffer; > > > +}; > > > + > > > +static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len) > > > +{ > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > + int ret; > > > + > > > + ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, len); > > > + if (ret) > > > + return ret; > > > + > > > + /* > > > + * The SVSM call uses the same buffer for the command and for the > > > + * response, so after this call, the buffer will contain the response > > > + * that can be used by .recv() op. > > > + */ > > > + return snp_svsm_vtpm_send_command(priv->buffer); > > > +} > > > + > > > +static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len) > > > +{ > > > + struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev); > > > + > > > + /* > > > + * The internal buffer contains the response after we send the command > > > + * to SVSM. > > > + */ > > > + return svsm_vtpm_cmd_response_parse(priv->buffer, buf, len); > > > +} > > > + > > > +static struct tpm_class_ops tpm_chip_ops = { > > > + .flags = TPM_OPS_AUTO_STARTUP, > > > + .recv = tpm_svsm_recv, > > > + .send = tpm_svsm_send, > > > +}; > > > + > > > +static int __init tpm_svsm_probe(struct platform_device *pdev) > > > +{ > > > + struct device *dev = &pdev->dev; > > > + struct tpm_svsm_priv *priv; > > > + struct tpm_chip *chip; > > > + int err; > > > + > > > + if (!snp_svsm_vtpm_probe()) > > > + return -ENODEV; > > > + > > > + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); > > > + if (!priv) > > > + return -ENOMEM; > > > + > > > + /* > > > + * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER > > > + * in tpm_svsm.h). > > > + */ > > > + priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); > > > + if (!priv->buffer) > > > + return -ENOMEM; > > > + > > > + chip = tpmm_chip_alloc(dev, &tpm_chip_ops); > > > + if (IS_ERR(chip)) > > > + return PTR_ERR(chip); > > > + > > > + dev_set_drvdata(&chip->dev, priv); > > > + > > > + err = tpm2_probe(chip); > > > + if (err) > > > + return err; > > > + > > > + err = tpm_chip_register(chip); > > > + if (err) > > > + return err; > > > + > > > + dev_info(dev, "SNP SVSM vTPM %s device\n", > > > + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2"); > > > + > > > + return 0; > > > +} > > > + > > > +static void __exit tpm_svsm_remove(struct platform_device *pdev) > > > +{ > > > + struct tpm_chip *chip = platform_get_drvdata(pdev); > > > + > > > + tpm_chip_unregister(chip); > > > +} > > > + > > > +/* > > > + * tpm_svsm_remove() lives in .exit.text. For drivers registered via > > > + * module_platform_driver_probe() this is ok because they cannot get unbound > > > + * at runtime. So mark the driver struct with __refdata to prevent modpost > > > + * triggering a section mismatch warning. > > > + */ > > > +static struct platform_driver tpm_svsm_driver __refdata = { > > > + .remove = __exit_p(tpm_svsm_remove), > > > + .driver = { > > > + .name = "tpm-svsm", > > > + }, > > > +}; > > > + > > > +module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe); > > > + > > > +MODULE_DESCRIPTION("SNP SVSM vTPM Driver"); > > > +MODULE_LICENSE("GPL"); > > > +MODULE_ALIAS("platform:tpm-svsm"); > > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > > index fe4f3a609934..dddd702b2454 100644 > > > --- a/drivers/char/tpm/Kconfig > > > +++ b/drivers/char/tpm/Kconfig > > > @@ -234,5 +234,15 @@ config TCG_FTPM_TEE > > > help > > > This driver proxies for firmware TPM running in TEE. > > > > > > +config TCG_SVSM > > > + tristate "SNP SVSM vTPM interface" > > > + depends on AMD_MEM_ENCRYPT > > > + help > > > + This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest > > > + OS can use to discover and talk to a vTPM emulated by the Secure VM > > > + Service Module (SVSM) in the guest context, but at a more privileged > > > + level (usually VMPL0). To compile this driver as a module, choose M > > > + here; the module will be called tpm_svsm. > > > + > > > source "drivers/char/tpm/st33zp24/Kconfig" > > > endif # TCG_TPM > > > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > > > index 2b004df8c04b..9de1b3ea34a9 100644 > > > --- a/drivers/char/tpm/Makefile > > > +++ b/drivers/char/tpm/Makefile > > > @@ -45,3 +45,4 @@ obj-$(CONFIG_TCG_CRB) += tpm_crb.o > > > obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o > > > obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o > > > obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o > > > +obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o > > > -- > > > 2.49.0 > > > > > > > > > > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> > > In the next version I'm moving the snp_svsm_vtpm_probe() call from > here to sev core where the device is registered. > See https://lore.kernel.org/linux-integrity/o2u7p3wb64lcc4sziunr274hyubkgmspzdjcvihbpzkw6mkvpo@sjq3vi4y2qfl/ > > Do I carry your R-b for this patch (and patch 4) or would you rather > take a look at v7? I'm fine with it. I'm sure that Boris et al make the complains if/when required :-) This whole process has been going on smoothly overall, and all my concerns have been addressed. > > Thanks, > Stefano > BR, Jarkko
© 2016 - 2025 Red Hat, Inc.