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 len)
29{
30 struct tpm_svsm_priv *priv = dev_get_drvdata(dev: &chip->dev);
31 int ret;
32
33 ret = svsm_vtpm_cmd_request_fill(req: priv->buffer, locality: 0, buf, len);
34 if (ret)
35 return ret;
36
37 /*
38 * The SVSM call uses the same buffer for the command and for the
39 * response, so after this call, the buffer will contain the response
40 * that can be used by .recv() op.
41 */
42 return snp_svsm_vtpm_send_command(buffer: priv->buffer);
43}
44
45static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len)
46{
47 struct tpm_svsm_priv *priv = dev_get_drvdata(dev: &chip->dev);
48
49 /*
50 * The internal buffer contains the response after we send the command
51 * to SVSM.
52 */
53 return svsm_vtpm_cmd_response_parse(resp: priv->buffer, buf, len);
54}
55
56static struct tpm_class_ops tpm_chip_ops = {
57 .flags = TPM_OPS_AUTO_STARTUP,
58 .recv = tpm_svsm_recv,
59 .send = tpm_svsm_send,
60};
61
62static int __init tpm_svsm_probe(struct platform_device *pdev)
63{
64 struct device *dev = &pdev->dev;
65 struct tpm_svsm_priv *priv;
66 struct tpm_chip *chip;
67 int err;
68
69 priv = devm_kmalloc(dev, size: sizeof(*priv), GFP_KERNEL);
70 if (!priv)
71 return -ENOMEM;
72
73 /*
74 * The maximum buffer supported is one page (see SVSM_VTPM_MAX_BUFFER
75 * in tpm_svsm.h).
76 */
77 priv->buffer = (void *)devm_get_free_pages(dev, GFP_KERNEL, order: 0);
78 if (!priv->buffer)
79 return -ENOMEM;
80
81 chip = tpmm_chip_alloc(pdev: dev, ops: &tpm_chip_ops);
82 if (IS_ERR(ptr: chip))
83 return PTR_ERR(ptr: chip);
84
85 dev_set_drvdata(dev: &chip->dev, data: priv);
86
87 err = tpm2_probe(chip);
88 if (err)
89 return err;
90
91 err = tpm_chip_register(chip);
92 if (err)
93 return err;
94
95 dev_info(dev, "SNP SVSM vTPM %s device\n",
96 (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2");
97
98 return 0;
99}
100
101static void __exit tpm_svsm_remove(struct platform_device *pdev)
102{
103 struct tpm_chip *chip = platform_get_drvdata(pdev);
104
105 tpm_chip_unregister(chip);
106}
107
108/*
109 * tpm_svsm_remove() lives in .exit.text. For drivers registered via
110 * module_platform_driver_probe() this is ok because they cannot get unbound
111 * at runtime. So mark the driver struct with __refdata to prevent modpost
112 * triggering a section mismatch warning.
113 */
114static struct platform_driver tpm_svsm_driver __refdata = {
115 .remove = __exit_p(tpm_svsm_remove),
116 .driver = {
117 .name = "tpm-svsm",
118 },
119};
120
121module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
122
123MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
124MODULE_LICENSE("GPL");
125MODULE_ALIAS("platform:tpm-svsm");
126

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/drivers/char/tpm/tpm_svsm.c