Commit dd3922cf authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'x86_sev_for_v6.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull AMD SEV update from Borislav Petkov:
 "Add a virtual TPM driver glue which allows a guest kernel to talk to a
  TPM device emulated by a Secure VM Service Module (SVSM) - a helper
  module of sorts which runs at a different privilege level in the
  SEV-SNP VM stack.

  The intent being that a TPM device is emulated by a trusted entity and
  not by the untrusted host which is the default assumption in the
  confidential computing scenarios"

* tag 'x86_sev_for_v6.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/sev: Register tpm-svsm platform device
  tpm: Add SNP SVSM vTPM driver
  svsm: Add header with SVSM_VTPM_CMD helpers
  x86/sev: Add SVSM vTPM probe/send_command functions
parents 350a6042 e396dd85
Loading
Loading
Loading
Loading
+68 −1
Original line number Diff line number Diff line
@@ -1462,11 +1462,74 @@ static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_dat
	return ret;
}

/**
 * snp_svsm_vtpm_probe() - Probe if SVSM provides a vTPM device
 *
 * Check that there is SVSM and that it supports at least TPM_SEND_COMMAND
 * which is the only request used so far.
 *
 * Return: true if the platform provides a vTPM SVSM device, false otherwise.
 */
static bool snp_svsm_vtpm_probe(void)
{
	struct svsm_call call = {};

	/* The vTPM device is available only if a SVSM is present */
	if (!snp_vmpl)
		return false;

	call.caa = svsm_get_caa();
	call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);

	if (svsm_perform_call_protocol(&call))
		return false;

	/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
	return call.rcx_out & BIT_ULL(8);
}

/**
 * snp_svsm_vtpm_send_command() - Execute a vTPM operation on SVSM
 * @buffer: A buffer used to both send the command and receive the response.
 *
 * Execute a SVSM_VTPM_CMD call as defined by
 * "Secure VM Service Module for SEV-SNP Guests" Publication # 58019 Revision: 1.00
 *
 * All command request/response buffers have a common structure as specified by
 * the following table:
 *     Byte      Size       In/Out    Description
 *     Offset    (Bytes)
 *     0x000     4          In        Platform command
 *                          Out       Platform command response size
 *
 * Each command can build upon this common request/response structure to create
 * a structure specific to the command. See include/linux/tpm_svsm.h for more
 * details.
 *
 * Return: 0 on success, -errno on failure
 */
int snp_svsm_vtpm_send_command(u8 *buffer)
{
	struct svsm_call call = {};

	call.caa = svsm_get_caa();
	call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
	call.rcx = __pa(buffer);

	return svsm_perform_call_protocol(&call);
}
EXPORT_SYMBOL_GPL(snp_svsm_vtpm_send_command);

static struct platform_device sev_guest_device = {
	.name		= "sev-guest",
	.id		= -1,
};

static struct platform_device tpm_svsm_device = {
	.name		= "tpm-svsm",
	.id		= -1,
};

static int __init snp_init_platform_device(void)
{
	if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
@@ -1475,7 +1538,11 @@ static int __init snp_init_platform_device(void)
	if (platform_device_register(&sev_guest_device))
		return -ENODEV;

	pr_info("SNP guest platform device initialized.\n");
	if (snp_svsm_vtpm_probe() &&
	    platform_device_register(&tpm_svsm_device))
		return -ENODEV;

	pr_info("SNP guest platform devices initialized.\n");
	return 0;
}
device_initcall(snp_init_platform_device);
+7 −0
Original line number Diff line number Diff line
@@ -415,6 +415,10 @@ struct svsm_call {
#define SVSM_ATTEST_SERVICES		0
#define SVSM_ATTEST_SINGLE_SERVICE	1

#define SVSM_VTPM_CALL(x)		((2ULL << 32) | (x))
#define SVSM_VTPM_QUERY			0
#define SVSM_VTPM_CMD			1

#ifdef CONFIG_AMD_MEM_ENCRYPT

extern u8 snp_vmpl;
@@ -512,6 +516,8 @@ void snp_msg_free(struct snp_msg_desc *mdesc);
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
			   struct snp_guest_request_ioctl *rio);

int snp_svsm_vtpm_send_command(u8 *buffer);

void __init snp_secure_tsc_prepare(void);
void __init snp_secure_tsc_init(void);

@@ -583,6 +589,7 @@ static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; }
static inline void snp_msg_free(struct snp_msg_desc *mdesc) { }
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
					 struct snp_guest_request_ioctl *rio) { return -ENODEV; }
static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
static inline void __init snp_secure_tsc_prepare(void) { }
static inline void __init snp_secure_tsc_init(void) { }

+10 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+125 −0
Original line number Diff line number Diff line
// 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;

	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");
Loading