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 - 2026 Red Hat, Inc.