1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
4 *
5 * Driver for the vTPM defined by the AMD SVSM spec [1].
6 *
7 * The specification defines a protocol that a SEV-SNP guest OS can use to
8 * discover and talk to a vTPM emulated by the Secure VM Service Module (SVSM)
9 * in the guest context, but at a more privileged level (usually VMPL0).
10 *
11 * [1] "Secure VM Service Module for SEV-SNP Guests"
12 * Publication # 58019 Revision: 1.00
13 */
14
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/platform_device.h>
18 #include <linux/tpm_svsm.h>
19
20 #include <asm/sev.h>
21
22 #include "tpm.h"
23
24 struct tpm_svsm_priv {
25 void *buffer;
26 };
27
tpm_svsm_send(struct tpm_chip * chip,u8 * buf,size_t bufsiz,size_t cmd_len)28 static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
29 size_t cmd_len)
30 {
31 struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev);
32 int ret;
33
34 ret = svsm_vtpm_cmd_request_fill(priv->buffer, 0, buf, cmd_len);
35 if (ret)
36 return ret;
37
38 /*
39 * The SVSM call uses the same buffer for the command and for the
40 * response, so after this call, the buffer will contain the response.
41 *
42 * Note: we have to use an internal buffer because the device in SVSM
43 * expects the svsm_vtpm header + data to be physically contiguous.
44 */
45 ret = snp_svsm_vtpm_send_command(priv->buffer);
46 if (ret)
47 return ret;
48
49 return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz);
50 }
51
52 static struct tpm_class_ops tpm_chip_ops = {
53 .flags = TPM_OPS_AUTO_STARTUP,
54 .send = tpm_svsm_send,
55 };
56
tpm_svsm_probe(struct platform_device * pdev)57 static int __init tpm_svsm_probe(struct platform_device *pdev)
58 {
59 struct device *dev = &pdev->dev;
60 struct tpm_svsm_priv *priv;
61 struct tpm_chip *chip;
62 int err;
63
64 priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
65 if (!priv)
66 return -ENOMEM;
67
68 /*
69 * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER
70 * in tpm_svsm.h).
71 */
72 priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0);
73 if (!priv->buffer)
74 return -ENOMEM;
75
76 chip = tpmm_chip_alloc(dev, &tpm_chip_ops);
77 if (IS_ERR(chip))
78 return PTR_ERR(chip);
79
80 dev_set_drvdata(&chip->dev, priv);
81
82 chip->flags |= TPM_CHIP_FLAG_SYNC;
83 err = tpm2_probe(chip);
84 if (err)
85 return err;
86
87 err = tpm_chip_register(chip);
88 if (err)
89 return err;
90
91 dev_info(dev, "SNP SVSM vTPM %s device\n",
92 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2");
93
94 return 0;
95 }
96
tpm_svsm_remove(struct platform_device * pdev)97 static void __exit tpm_svsm_remove(struct platform_device *pdev)
98 {
99 struct tpm_chip *chip = platform_get_drvdata(pdev);
100
101 tpm_chip_unregister(chip);
102 }
103
104 /*
105 * tpm_svsm_remove() lives in .exit.text. For drivers registered via
106 * module_platform_driver_probe() this is ok because they cannot get unbound
107 * at runtime. So mark the driver struct with __refdata to prevent modpost
108 * triggering a section mismatch warning.
109 */
110 static struct platform_driver tpm_svsm_driver __refdata = {
111 .remove = __exit_p(tpm_svsm_remove),
112 .driver = {
113 .name = "tpm-svsm",
114 },
115 };
116
117 module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
118
119 MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
120 MODULE_LICENSE("GPL");
121 MODULE_ALIAS("platform:tpm-svsm");
122