Skip to content

Commit fb06120

Browse files
committed
Merge: tpm: add and enable SNP SVSM vTPM driver
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/7004 # Merge Request Required Information ## Summary of Changes This is a backport of https://lore.kernel.org/linux-integrity/20250410135118.133240-1-sgarzare@redhat.com/ Add driver for the vTPM defined by the AMD SVSM spec needed for Confidential VMs. And enable it in the configuration. Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> ## Approved Development Ticket(s) All submissions to CentOS Stream must reference a ticket in [Red Hat Jira](https://issues.redhat.com/). JIRA: https://issues.redhat.com/browse/RHEL-87276 Resolves: RHEL-87276 <details><summary>Click for formatting instructions</summary> Please follow the CentOS Stream [contribution documentation](https://docs.centos.org/en-US/stream-contrib/quickstart/) for how to file this ticket and have it approved. List tickets each on their own line of this description using the format "Resolves: RHEL-76229", "Related: RHEL-76229" or "Reverts: RHEL-76229", as appropriate. </details> Approved-by: Vitaly Kuznetsov <vkuznets@redhat.com> Approved-by: Luigi Leonardi <leonardi@redhat.com> Approved-by: Ani Sinha <anisinha@redhat.com> Approved-by: Štěpán Horáček <shoracek@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Augusto Caringi <acaringi@redhat.com>
2 parents ad536b0 + 287607b commit fb06120

File tree

9 files changed

+387
-24
lines changed

9 files changed

+387
-24
lines changed

arch/x86/coco/sev/core.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2636,11 +2636,74 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
26362636
}
26372637
EXPORT_SYMBOL_GPL(snp_issue_guest_request);
26382638

2639+
/**
2640+
* snp_svsm_vtpm_probe() - Probe if SVSM provides a vTPM device
2641+
*
2642+
* Check that there is SVSM and that it supports at least TPM_SEND_COMMAND
2643+
* which is the only request used so far.
2644+
*
2645+
* Return: true if the platform provides a vTPM SVSM device, false otherwise.
2646+
*/
2647+
static bool snp_svsm_vtpm_probe(void)
2648+
{
2649+
struct svsm_call call = {};
2650+
2651+
/* The vTPM device is available only if a SVSM is present */
2652+
if (!snp_vmpl)
2653+
return false;
2654+
2655+
call.caa = svsm_get_caa();
2656+
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);
2657+
2658+
if (svsm_perform_call_protocol(&call))
2659+
return false;
2660+
2661+
/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
2662+
return call.rcx_out & BIT_ULL(8);
2663+
}
2664+
2665+
/**
2666+
* snp_svsm_vtpm_send_command() - Execute a vTPM operation on SVSM
2667+
* @buffer: A buffer used to both send the command and receive the response.
2668+
*
2669+
* Execute a SVSM_VTPM_CMD call as defined by
2670+
* "Secure VM Service Module for SEV-SNP Guests" Publication # 58019 Revision: 1.00
2671+
*
2672+
* All command request/response buffers have a common structure as specified by
2673+
* the following table:
2674+
* Byte Size     In/Out    Description
2675+
* Offset    (Bytes)
2676+
* 0x000     4          In        Platform command
2677+
 *                         Out       Platform command response size
2678+
*
2679+
* Each command can build upon this common request/response structure to create
2680+
* a structure specific to the command. See include/linux/tpm_svsm.h for more
2681+
* details.
2682+
*
2683+
* Return: 0 on success, -errno on failure
2684+
*/
2685+
int snp_svsm_vtpm_send_command(u8 *buffer)
2686+
{
2687+
struct svsm_call call = {};
2688+
2689+
call.caa = svsm_get_caa();
2690+
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
2691+
call.rcx = __pa(buffer);
2692+
2693+
return svsm_perform_call_protocol(&call);
2694+
}
2695+
EXPORT_SYMBOL_GPL(snp_svsm_vtpm_send_command);
2696+
26392697
static struct platform_device sev_guest_device = {
26402698
.name = "sev-guest",
26412699
.id = -1,
26422700
};
26432701

2702+
static struct platform_device tpm_svsm_device = {
2703+
.name = "tpm-svsm",
2704+
.id = -1,
2705+
};
2706+
26442707
static int __init snp_init_platform_device(void)
26452708
{
26462709
struct sev_guest_platform_data data;
@@ -2660,7 +2723,11 @@ static int __init snp_init_platform_device(void)
26602723
if (platform_device_register(&sev_guest_device))
26612724
return -ENODEV;
26622725

2663-
pr_info("SNP guest platform device initialized.\n");
2726+
if (snp_svsm_vtpm_probe() &&
2727+
platform_device_register(&tpm_svsm_device))
2728+
return -ENODEV;
2729+
2730+
pr_info("SNP guest platform devices initialized.\n");
26642731
return 0;
26652732
}
26662733
device_initcall(snp_init_platform_device);

arch/x86/include/asm/sev.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ struct svsm_call {
327327
#define SVSM_ATTEST_SERVICES 0
328328
#define SVSM_ATTEST_SINGLE_SERVICE 1
329329

330+
#define SVSM_VTPM_CALL(x) ((2ULL << 32) | (x))
331+
#define SVSM_VTPM_QUERY 0
332+
#define SVSM_VTPM_CMD 1
333+
330334
#ifdef CONFIG_AMD_MEM_ENCRYPT
331335

332336
extern u8 snp_vmpl;
@@ -418,6 +422,7 @@ int prepare_pte_enc(struct pte_enc_desc *d);
418422
void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot);
419423
void snp_kexec_finish(void);
420424
void snp_kexec_begin(void);
425+
int snp_svsm_vtpm_send_command(u8 *buffer);
421426

422427
#else /* !CONFIG_AMD_MEM_ENCRYPT */
423428

@@ -458,6 +463,7 @@ static inline int prepare_pte_enc(struct pte_enc_desc *d) { return 0; }
458463
static inline void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot) { }
459464
static inline void snp_kexec_finish(void) { }
460465
static inline void snp_kexec_begin(void) { }
466+
static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
461467

462468
#endif /* CONFIG_AMD_MEM_ENCRYPT */
463469

drivers/char/tpm/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,15 @@ config TCG_FTPM_TEE
198198
help
199199
This driver proxies for firmware TPM running in TEE.
200200

201+
config TCG_SVSM
202+
tristate "SNP SVSM vTPM interface"
203+
depends on AMD_MEM_ENCRYPT
204+
help
205+
This is a driver for the AMD SVSM vTPM protocol that a SEV-SNP guest
206+
OS can use to discover and talk to a vTPM emulated by the Secure VM
207+
Service Module (SVSM) in the guest context, but at a more privileged
208+
level (usually VMPL0). To compile this driver as a module, choose M
209+
here; the module will be called tpm_svsm.
210+
201211
source "drivers/char/tpm/st33zp24/Kconfig"
202212
endif # TCG_TPM

drivers/char/tpm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
4242
obj-$(CONFIG_TCG_CRB) += tpm_crb.o
4343
obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
4444
obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
45+
obj-$(CONFIG_TCG_SVSM) += tpm_svsm.o

drivers/char/tpm/tpm-interface.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,30 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
5858
}
5959
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
6060

61+
static void tpm_chip_cancel(struct tpm_chip *chip)
62+
{
63+
if (!chip->ops->cancel)
64+
return;
65+
66+
chip->ops->cancel(chip);
67+
}
68+
69+
static u8 tpm_chip_status(struct tpm_chip *chip)
70+
{
71+
if (!chip->ops->status)
72+
return 0;
73+
74+
return chip->ops->status(chip);
75+
}
76+
77+
static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status)
78+
{
79+
if (!chip->ops->req_canceled)
80+
return false;
81+
82+
return chip->ops->req_canceled(chip, status);
83+
}
84+
6185
static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
6286
{
6387
struct tpm_header *header = buf;
@@ -104,12 +128,12 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
104128

105129
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
106130
do {
107-
u8 status = chip->ops->status(chip);
131+
u8 status = tpm_chip_status(chip);
108132
if ((status & chip->ops->req_complete_mask) ==
109133
chip->ops->req_complete_val)
110134
goto out_recv;
111135

112-
if (chip->ops->req_canceled(chip, status)) {
136+
if (tpm_chip_req_canceled(chip, status)) {
113137
dev_err(&chip->dev, "Operation Canceled\n");
114138
return -ECANCELED;
115139
}
@@ -118,7 +142,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
118142
rmb();
119143
} while (time_before(jiffies, stop));
120144

121-
chip->ops->cancel(chip);
145+
tpm_chip_cancel(chip);
122146
dev_err(&chip->dev, "Operation Timed out\n");
123147
return -ETIME;
124148

drivers/char/tpm/tpm_ftpm_tee.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -165,30 +165,10 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
165165
return 0;
166166
}
167167

168-
static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
169-
{
170-
/* not supported */
171-
}
172-
173-
static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
174-
{
175-
return 0;
176-
}
177-
178-
static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
179-
{
180-
return 0;
181-
}
182-
183168
static const struct tpm_class_ops ftpm_tee_tpm_ops = {
184169
.flags = TPM_OPS_AUTO_STARTUP,
185170
.recv = ftpm_tee_tpm_op_recv,
186171
.send = ftpm_tee_tpm_op_send,
187-
.cancel = ftpm_tee_tpm_op_cancel,
188-
.status = ftpm_tee_tpm_op_status,
189-
.req_complete_mask = 0,
190-
.req_complete_val = 0,
191-
.req_canceled = ftpm_tee_tpm_req_canceled,
192172
};
193173

194174
/*

drivers/char/tpm/tpm_svsm.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
28+
static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t len)
29+
{
30+
struct tpm_svsm_priv *priv = dev_get_drvdata(&chip->dev);
31+
int ret;
32+
33+
ret = svsm_vtpm_cmd_request_fill(priv->buffer, 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(priv->buffer);
43+
}
44+
45+
static int tpm_svsm_recv(struct tpm_chip *chip, u8 *buf, size_t len)
46+
{
47+
struct tpm_svsm_priv *priv = dev_get_drvdata(&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(priv->buffer, buf, len);
54+
}
55+
56+
static 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+
62+
static 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, 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, 0);
78+
if (!priv->buffer)
79+
return -ENOMEM;
80+
81+
chip = tpmm_chip_alloc(dev, &tpm_chip_ops);
82+
if (IS_ERR(chip))
83+
return PTR_ERR(chip);
84+
85+
dev_set_drvdata(&chip->dev, 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+
101+
static 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+
*/
114+
static struct platform_driver tpm_svsm_driver __refdata = {
115+
.remove_new = __exit_p(tpm_svsm_remove),
116+
.driver = {
117+
.name = "tpm-svsm",
118+
},
119+
};
120+
121+
module_platform_driver_probe(tpm_svsm_driver, tpm_svsm_probe);
122+
123+
MODULE_DESCRIPTION("SNP SVSM vTPM Driver");
124+
MODULE_LICENSE("GPL");
125+
MODULE_ALIAS("platform:tpm-svsm");

0 commit comments

Comments
 (0)