Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
24struct tpm_svsm_priv {
25 void *buffer;
26};
27
28static 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
52static struct tpm_class_ops tpm_chip_ops = {
53 .flags = TPM_OPS_AUTO_STARTUP,
54 .send = tpm_svsm_send,
55};
56
57static 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
97static 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 */
110static struct platform_driver tpm_svsm_driver __refdata = {
111 .remove = __exit_p(tpm_svsm_remove),
112 .driver = {
113 .name = "tpm-svsm",
114 },
115};
116
117module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
118
119MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
120MODULE_LICENSE("GPL");
121MODULE_ALIAS("platform:tpm-svsm");