2023-10-30 06:37:28

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 00/14] Add Secure TSC support for SNP guests

Secure TSC allows guests to securely use RDTSC/RDTSCP instructions as the
parameters being used cannot be changed by hypervisor once the guest is
launched. More details in the AMD64 APM Vol 2, Section "Secure TSC".

During the boot-up of the secondary cpus, SecureTSC enabled guests need to
query TSC info from AMD Security Processor. This communication channel is
encrypted between the AMD Security Processor and the guest, the hypervisor
is just the conduit to deliver the guest messages to the AMD Security
Processor. Each message is protected with an AEAD (AES-256 GCM). See "SEV
Secure Nested Paging Firmware ABI Specification" document (currently at
https://www.amd.com/system/files/TechDocs/56860.pdf) section "TSC Info"

Use a minimal GCM library to encrypt/decrypt SNP Guest messages to
communicate with the AMD Security Processor which is available at early
boot.

SEV-guest driver has the implementation for guest and AMD Security
Processor communication. As the TSC_INFO needs to be initialized during
early boot before smp cpus are started, move most of the sev-guest driver
code to kernel/sev.c and provide well defined APIs to the sev-guest driver
to use the interface to avoid code-duplication.

Patches:
01-07: Preparation and movement of sev-guest driver code
08-14: SecureTSC enablement patches.

Testing SecureTSC
-----------------

SecureTSC hypervisor patches based on top of SEV-SNP UPM series:
https://github.com/nikunjad/linux/tree/snp-host-latest-securetsc_v5

QEMU changes:
https://github.com/nikunjad/qemu/tree/snp_securetsc_v5

QEMU commandline SEV-SNP-UPM with SecureTSC:

qemu-system-x86_64 -cpu EPYC-Milan-v2,+secure-tsc -smp 4 \
-object memory-backend-memfd-private,id=ram1,size=1G,share=true \
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,secure-tsc=on \
-machine q35,confidential-guest-support=sev0,memory-backend=ram1,kvm-type=snp \
...

Changelog:
----------
v5:
* Rebased on v6.6 kernel
* Dropped link tag in first patch
* Dropped get_ctx_authsize() as it was redundant

v4:
* Drop handle_guest_request() and handle_guest_request_ext()
* Drop NULL check for key
* Corrected commit subject
* Added Reviewed-by from Tom

https://lore.kernel.org/lkml/[email protected]/

v3:
* Updated commit messages
* Made snp_setup_psp_messaging() generic that is accessed by both the
kernel and the driver
* Moved most of the context information to sev.c, sev-guest driver
does not need to know the secrets page layout anymore
* Add CC_ATTR_GUEST_SECURE_TSC early in the series therefore it can be
used in later patches.
* Removed data_gpa and data_npages from struct snp_req_data, as certs_data
and its size is passed to handle_guest_request_ext()
* Make vmpck_id as unsigned int
* Dropped unnecessary usage of memzero_explicit()
* Cache secrets_pa instead of remapping the cc_blob always
* Rebase on top of v6.4 kernel
https://lore.kernel.org/lkml/[email protected]/

v2:
* Rebased on top of v6.3-rc3 that has Boris's sev-guest cleanup series
https://lore.kernel.org/r/[email protected]/

v1: https://lore.kernel.org/r/[email protected]/

Nikunj A Dadhania (14):
virt: sev-guest: Use AES GCM crypto library
virt: sev-guest: Move mutex to SNP guest device structure
virt: sev-guest: Replace dev_dbg with pr_debug
virt: sev-guest: Add SNP guest request structure
virt: sev-guest: Add vmpck_id to snp_guest_dev struct
x86/sev: Cache the secrets page address
x86/sev: Move and reorganize sev guest request api
x86/mm: Add generic guest initialization hook
x86/sev: Add Secure TSC support for SNP guests
x86/sev: Change TSC MSR behavior for Secure TSC enabled guests
x86/sev: Prevent RDTSC/RDTSCP interception for Secure TSC enabled
guests
x86/kvmclock: Skip kvmclock when Secure TSC is available
x86/tsc: Mark Secure TSC as reliable clocksource
x86/sev: Enable Secure TSC for SNP guests

arch/x86/Kconfig | 1 +
arch/x86/boot/compressed/sev.c | 3 +-
arch/x86/coco/core.c | 3 +
arch/x86/include/asm/sev-guest.h | 175 +++++++
arch/x86/include/asm/sev.h | 20 +-
arch/x86/include/asm/svm.h | 6 +-
arch/x86/include/asm/x86_init.h | 2 +
arch/x86/kernel/kvmclock.c | 2 +-
arch/x86/kernel/sev-shared.c | 7 +
arch/x86/kernel/sev.c | 631 +++++++++++++++++++++--
arch/x86/kernel/tsc.c | 2 +-
arch/x86/kernel/x86_init.c | 2 +
arch/x86/mm/mem_encrypt.c | 13 +-
arch/x86/mm/mem_encrypt_amd.c | 6 +
drivers/virt/coco/sev-guest/Kconfig | 3 -
drivers/virt/coco/sev-guest/sev-guest.c | 646 +++---------------------
drivers/virt/coco/sev-guest/sev-guest.h | 63 ---
include/linux/cc_platform.h | 8 +
18 files changed, 877 insertions(+), 716 deletions(-)
create mode 100644 arch/x86/include/asm/sev-guest.h
delete mode 100644 drivers/virt/coco/sev-guest/sev-guest.h


base-commit: ffc253263a1375a65fa6c9f62a893e9767fbebfa
--
2.34.1


2023-10-30 06:37:39

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 01/14] virt: sev-guest: Use AES GCM crypto library

The sev-guest driver encryption code uses Crypto API for SNP guest
messaging to interact with AMD Security processor. For enabling SecureTSC,
SEV-SNP guests need to send a TSC_INFO request guest message before the
smpboot phase starts. Details from the TSC_INFO response will be used to
program the VMSA before the secondary CPUs are brought up. The Crypto API
is not available this early in the boot phase.

In preparation of moving the encryption code out of sev-guest driver to
support SecureTSC and make reviewing the diff easier, start using AES GCM
library implementation instead of Crypto API.

CC: Ard Biesheuvel <[email protected]>
Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
drivers/virt/coco/sev-guest/Kconfig | 4 +-
drivers/virt/coco/sev-guest/sev-guest.c | 163 ++++++------------------
drivers/virt/coco/sev-guest/sev-guest.h | 3 +
3 files changed, 44 insertions(+), 126 deletions(-)

diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig
index da2d7ca531f0..bcc760bfb468 100644
--- a/drivers/virt/coco/sev-guest/Kconfig
+++ b/drivers/virt/coco/sev-guest/Kconfig
@@ -2,9 +2,7 @@ config SEV_GUEST
tristate "AMD SEV Guest driver"
default m
depends on AMD_MEM_ENCRYPT
- select CRYPTO
- select CRYPTO_AEAD2
- select CRYPTO_GCM
+ select CRYPTO_LIB_AESGCM
help
SEV-SNP firmware provides the guest a mechanism to communicate with
the PSP without risk from a malicious hypervisor who wishes to read,
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 97dbe715e96a..68044c436866 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -16,8 +16,7 @@
#include <linux/miscdevice.h>
#include <linux/set_memory.h>
#include <linux/fs.h>
-#include <crypto/aead.h>
-#include <linux/scatterlist.h>
+#include <crypto/gcm.h>
#include <linux/psp-sev.h>
#include <uapi/linux/sev-guest.h>
#include <uapi/linux/psp-sev.h>
@@ -28,24 +27,16 @@
#include "sev-guest.h"

#define DEVICE_NAME "sev-guest"
-#define AAD_LEN 48
-#define MSG_HDR_VER 1

#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
#define SNP_REQ_RETRY_DELAY (2*HZ)

-struct snp_guest_crypto {
- struct crypto_aead *tfm;
- u8 *iv, *authtag;
- int iv_len, a_len;
-};
-
struct snp_guest_dev {
struct device *dev;
struct miscdevice misc;

void *certs_data;
- struct snp_guest_crypto *crypto;
+ struct aesgcm_ctx *ctx;
/* request and response are in unencrypted memory */
struct snp_guest_msg *request, *response;

@@ -152,132 +143,59 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
return container_of(dev, struct snp_guest_dev, misc);
}

-static struct snp_guest_crypto *init_crypto(struct snp_guest_dev *snp_dev, u8 *key, size_t keylen)
+static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
{
- struct snp_guest_crypto *crypto;
+ struct aesgcm_ctx *ctx;

- crypto = kzalloc(sizeof(*crypto), GFP_KERNEL_ACCOUNT);
- if (!crypto)
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
+ if (!ctx)
return NULL;

- crypto->tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
- if (IS_ERR(crypto->tfm))
- goto e_free;
-
- if (crypto_aead_setkey(crypto->tfm, key, keylen))
- goto e_free_crypto;
-
- crypto->iv_len = crypto_aead_ivsize(crypto->tfm);
- crypto->iv = kmalloc(crypto->iv_len, GFP_KERNEL_ACCOUNT);
- if (!crypto->iv)
- goto e_free_crypto;
-
- if (crypto_aead_authsize(crypto->tfm) > MAX_AUTHTAG_LEN) {
- if (crypto_aead_setauthsize(crypto->tfm, MAX_AUTHTAG_LEN)) {
- dev_err(snp_dev->dev, "failed to set authsize to %d\n", MAX_AUTHTAG_LEN);
- goto e_free_iv;
- }
+ if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
+ pr_err("SNP: crypto init failed\n");
+ kfree(ctx);
+ return NULL;
}

- crypto->a_len = crypto_aead_authsize(crypto->tfm);
- crypto->authtag = kmalloc(crypto->a_len, GFP_KERNEL_ACCOUNT);
- if (!crypto->authtag)
- goto e_free_iv;
-
- return crypto;
-
-e_free_iv:
- kfree(crypto->iv);
-e_free_crypto:
- crypto_free_aead(crypto->tfm);
-e_free:
- kfree(crypto);
-
- return NULL;
-}
-
-static void deinit_crypto(struct snp_guest_crypto *crypto)
-{
- crypto_free_aead(crypto->tfm);
- kfree(crypto->iv);
- kfree(crypto->authtag);
- kfree(crypto);
-}
-
-static int enc_dec_message(struct snp_guest_crypto *crypto, struct snp_guest_msg *msg,
- u8 *src_buf, u8 *dst_buf, size_t len, bool enc)
-{
- struct snp_guest_msg_hdr *hdr = &msg->hdr;
- struct scatterlist src[3], dst[3];
- DECLARE_CRYPTO_WAIT(wait);
- struct aead_request *req;
- int ret;
-
- req = aead_request_alloc(crypto->tfm, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- /*
- * AEAD memory operations:
- * +------ AAD -------+------- DATA -----+---- AUTHTAG----+
- * | msg header | plaintext | hdr->authtag |
- * | bytes 30h - 5Fh | or | |
- * | | cipher | |
- * +------------------+------------------+----------------+
- */
- sg_init_table(src, 3);
- sg_set_buf(&src[0], &hdr->algo, AAD_LEN);
- sg_set_buf(&src[1], src_buf, hdr->msg_sz);
- sg_set_buf(&src[2], hdr->authtag, crypto->a_len);
-
- sg_init_table(dst, 3);
- sg_set_buf(&dst[0], &hdr->algo, AAD_LEN);
- sg_set_buf(&dst[1], dst_buf, hdr->msg_sz);
- sg_set_buf(&dst[2], hdr->authtag, crypto->a_len);
-
- aead_request_set_ad(req, AAD_LEN);
- aead_request_set_tfm(req, crypto->tfm);
- aead_request_set_callback(req, 0, crypto_req_done, &wait);
-
- aead_request_set_crypt(req, src, dst, len, crypto->iv);
- ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait);
-
- aead_request_free(req);
- return ret;
+ return ctx;
}

-static int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
+static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
void *plaintext, size_t len)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_guest_msg_hdr *hdr = &msg->hdr;
+ u8 iv[GCM_AES_IV_SIZE] = {};

- memset(crypto->iv, 0, crypto->iv_len);
- memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
+ if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
+ return -EBADMSG;

- return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true);
+ memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
+ aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
+ iv, hdr->authtag);
+ return 0;
}

-static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
+static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
void *plaintext, size_t len)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_guest_msg_hdr *hdr = &msg->hdr;
+ u8 iv[GCM_AES_IV_SIZE] = {};

- /* Build IV with response buffer sequence number */
- memset(crypto->iv, 0, crypto->iv_len);
- memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
-
- return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false);
+ memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
+ if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
+ AAD_LEN, iv, hdr->authtag))
+ return 0;
+ else
+ return -EBADMSG;
}

static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_guest_msg *resp = &snp_dev->secret_response;
struct snp_guest_msg *req = &snp_dev->secret_request;
struct snp_guest_msg_hdr *req_hdr = &req->hdr;
struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
+ struct aesgcm_ctx *ctx = snp_dev->ctx;

dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n",
resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz);
@@ -298,11 +216,11 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
* If the message size is greater than our buffer length then return
* an error.
*/
- if (unlikely((resp_hdr->msg_sz + crypto->a_len) > sz))
+ if (unlikely((resp_hdr->msg_sz + ctx->authsize) > sz))
return -EBADMSG;

/* Decrypt the payload */
- return dec_payload(snp_dev, resp, payload, resp_hdr->msg_sz + crypto->a_len);
+ return dec_payload(ctx, resp, payload, resp_hdr->msg_sz);
}

static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
@@ -329,7 +247,7 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
dev_dbg(snp_dev->dev, "request [seqno %lld type %d version %d sz %d]\n",
hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);

- return __enc_payload(snp_dev, req, payload, sz);
+ return __enc_payload(snp_dev->ctx, req, payload, sz);
}

static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
@@ -472,7 +390,6 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,

static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_report_resp *resp;
struct snp_report_req req;
int rc, resp_len;
@@ -490,7 +407,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
* response payload. Make sure that it has enough space to cover the
* authtag.
*/
- resp_len = sizeof(resp->data) + crypto->a_len;
+ resp_len = sizeof(resp->data) + snp_dev->ctx->authsize;
resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
if (!resp)
return -ENOMEM;
@@ -511,7 +428,6 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io

static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_derived_key_resp resp = {0};
struct snp_derived_key_req req;
int rc, resp_len;
@@ -528,7 +444,7 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
* response payload. Make sure that it has enough space to cover the
* authtag.
*/
- resp_len = sizeof(resp.data) + crypto->a_len;
+ resp_len = sizeof(resp.data) + snp_dev->ctx->authsize;
if (sizeof(buf) < resp_len)
return -ENOMEM;

@@ -552,7 +468,6 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque

static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
- struct snp_guest_crypto *crypto = snp_dev->crypto;
struct snp_ext_report_req req;
struct snp_report_resp *resp;
int ret, npages = 0, resp_len;
@@ -590,7 +505,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
* response payload. Make sure that it has enough space to cover the
* authtag.
*/
- resp_len = sizeof(resp->data) + crypto->a_len;
+ resp_len = sizeof(resp->data) + snp_dev->ctx->authsize;
resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
if (!resp)
return -ENOMEM;
@@ -802,8 +717,8 @@ static int __init sev_guest_probe(struct platform_device *pdev)
goto e_free_response;

ret = -EIO;
- snp_dev->crypto = init_crypto(snp_dev, snp_dev->vmpck, VMPCK_KEY_LEN);
- if (!snp_dev->crypto)
+ snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
+ if (!snp_dev->ctx)
goto e_free_cert_data;

misc = &snp_dev->misc;
@@ -818,11 +733,13 @@ static int __init sev_guest_probe(struct platform_device *pdev)

ret = misc_register(misc);
if (ret)
- goto e_free_cert_data;
+ goto e_free_ctx;

dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id);
return 0;

+e_free_ctx:
+ kfree(snp_dev->ctx);
e_free_cert_data:
free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
e_free_response:
@@ -841,7 +758,7 @@ static int __exit sev_guest_remove(struct platform_device *pdev)
free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
- deinit_crypto(snp_dev->crypto);
+ kfree(snp_dev->ctx);
misc_deregister(&snp_dev->misc);

return 0;
diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/drivers/virt/coco/sev-guest/sev-guest.h
index 21bda26fdb95..ceb798a404d6 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.h
+++ b/drivers/virt/coco/sev-guest/sev-guest.h
@@ -13,6 +13,9 @@
#include <linux/types.h>

#define MAX_AUTHTAG_LEN 32
+#define AUTHTAG_LEN 16
+#define AAD_LEN 48
+#define MSG_HDR_VER 1

/* See SNP spec SNP_GUEST_REQUEST section for the structure */
enum msg_type {
--
2.34.1

2023-10-30 06:38:06

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 02/14] virt: sev-guest: Move mutex to SNP guest device structure

In preparation for providing a new API to the sev-guest driver for sending
an SNP guest message, move the SNP command mutex to the snp_guest_dev
structure.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
drivers/virt/coco/sev-guest/sev-guest.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 68044c436866..85bda0c72a27 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -35,6 +35,9 @@ struct snp_guest_dev {
struct device *dev;
struct miscdevice misc;

+ /* Mutex to serialize the shared buffer access and command handling. */
+ struct mutex cmd_mutex;
+
void *certs_data;
struct aesgcm_ctx *ctx;
/* request and response are in unencrypted memory */
@@ -98,7 +101,7 @@ static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
{
u64 count;

- lockdep_assert_held(&snp_cmd_mutex);
+ lockdep_assert_held(&snp_dev->cmd_mutex);

/* Read the current message sequence counter from secrets pages */
count = *snp_dev->os_area_msg_seqno;
@@ -394,7 +397,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
struct snp_report_req req;
int rc, resp_len;

- lockdep_assert_held(&snp_cmd_mutex);
+ lockdep_assert_held(&snp_dev->cmd_mutex);

if (!arg->req_data || !arg->resp_data)
return -EINVAL;
@@ -434,7 +437,7 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
/* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
u8 buf[64 + 16];

- lockdep_assert_held(&snp_cmd_mutex);
+ lockdep_assert_held(&snp_dev->cmd_mutex);

if (!arg->req_data || !arg->resp_data)
return -EINVAL;
@@ -472,7 +475,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
struct snp_report_resp *resp;
int ret, npages = 0, resp_len;

- lockdep_assert_held(&snp_cmd_mutex);
+ lockdep_assert_held(&snp_dev->cmd_mutex);

if (!arg->req_data || !arg->resp_data)
return -EINVAL;
@@ -557,12 +560,12 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
if (!input.msg_version)
return -EINVAL;

- mutex_lock(&snp_cmd_mutex);
+ mutex_lock(&snp_dev->cmd_mutex);

/* Check if the VMPCK is not empty */
if (is_vmpck_empty(snp_dev)) {
dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
- mutex_unlock(&snp_cmd_mutex);
+ mutex_unlock(&snp_dev->cmd_mutex);
return -ENOTTY;
}

@@ -580,7 +583,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
break;
}

- mutex_unlock(&snp_cmd_mutex);
+ mutex_unlock(&snp_dev->cmd_mutex);

if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
return -EFAULT;
@@ -699,6 +702,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
goto e_unmap;
}

+ mutex_init(&snp_dev->cmd_mutex);
platform_set_drvdata(pdev, snp_dev);
snp_dev->dev = dev;
snp_dev->layout = layout;
--
2.34.1

2023-10-30 06:38:47

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 03/14] virt: sev-guest: Replace dev_dbg with pr_debug

In preparation of moving code to arch/x86/kernel/sev.c,
replace dev_dbg with pr_debug.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
drivers/virt/coco/sev-guest/sev-guest.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 85bda0c72a27..49bafd2e9f42 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -200,8 +200,9 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
struct aesgcm_ctx *ctx = snp_dev->ctx;

- dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n",
- resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz);
+ pr_debug("response [seqno %lld type %d version %d sz %d]\n",
+ resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version,
+ resp_hdr->msg_sz);

/* Copy response from shared memory to encrypted memory. */
memcpy(resp, snp_dev->response, sizeof(*resp));
@@ -247,8 +248,8 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
if (!hdr->msg_seqno)
return -ENOSR;

- dev_dbg(snp_dev->dev, "request [seqno %lld type %d version %d sz %d]\n",
- hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
+ pr_debug("request [seqno %lld type %d version %d sz %d]\n",
+ hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);

return __enc_payload(snp_dev->ctx, req, payload, sz);
}
--
2.34.1

2023-10-30 06:38:48

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 04/14] virt: sev-guest: Add SNP guest request structure

Add a snp_guest_req structure to simplify the function arguments. The
structure will be used to call the SNP Guest message request API
instead of passing a long list of parameters.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
.../x86/include/asm}/sev-guest.h | 11 ++
arch/x86/include/asm/sev.h | 8 --
arch/x86/kernel/sev.c | 15 ++-
drivers/virt/coco/sev-guest/sev-guest.c | 103 +++++++++++-------
4 files changed, 84 insertions(+), 53 deletions(-)
rename {drivers/virt/coco/sev-guest => arch/x86/include/asm}/sev-guest.h (80%)

diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/arch/x86/include/asm/sev-guest.h
similarity index 80%
rename from drivers/virt/coco/sev-guest/sev-guest.h
rename to arch/x86/include/asm/sev-guest.h
index ceb798a404d6..22ef97b55069 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.h
+++ b/arch/x86/include/asm/sev-guest.h
@@ -63,4 +63,15 @@ struct snp_guest_msg {
u8 payload[4000];
} __packed;

+struct snp_guest_req {
+ void *req_buf, *resp_buf, *data;
+ size_t req_sz, resp_sz, *data_npages;
+ u64 exit_code;
+ unsigned int vmpck_id;
+ u8 msg_version;
+ u8 msg_type;
+};
+
+int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
+ struct snp_guest_request_ioctl *rio);
#endif /* __VIRT_SEVGUEST_H__ */
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 5b4a1ce3d368..78465a8c7dc6 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -97,8 +97,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
struct snp_req_data {
unsigned long req_gpa;
unsigned long resp_gpa;
- unsigned long data_gpa;
- unsigned int data_npages;
};

struct sev_guest_platform_data {
@@ -209,7 +207,6 @@ void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
void snp_set_wakeup_secondary_cpu(void);
bool snp_init(struct boot_params *bp);
void __init __noreturn snp_abort(void);
-int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
u64 snp_get_unsupported_features(u64 status);
u64 sev_get_status(void);
@@ -233,11 +230,6 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npa
static inline void snp_set_wakeup_secondary_cpu(void) { }
static inline bool snp_init(struct boot_params *bp) { return false; }
static inline void snp_abort(void) { }
-static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
-{
- return -ENOTTY;
-}
-
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
static inline u64 sev_get_status(void) { return 0; }
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 6395bfd87b68..f8caf0a73052 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -28,6 +28,7 @@
#include <asm/cpu_entry_area.h>
#include <asm/stacktrace.h>
#include <asm/sev.h>
+#include <asm/sev-guest.h>
#include <asm/insn-eval.h>
#include <asm/fpu/xcr.h>
#include <asm/processor.h>
@@ -2167,15 +2168,21 @@ static int __init init_sev_config(char *str)
}
__setup("sev=", init_sev_config);

-int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
+int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
+ struct snp_guest_request_ioctl *rio)
{
struct ghcb_state state;
struct es_em_ctxt ctxt;
unsigned long flags;
struct ghcb *ghcb;
+ u64 exit_code;
int ret;

rio->exitinfo2 = SEV_RET_NO_FW_CALL;
+ if (!req)
+ return -EINVAL;
+
+ exit_code = req->exit_code;

/*
* __sev_get_ghcb() needs to run with IRQs disabled because it is using
@@ -2192,8 +2199,8 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
vc_ghcb_invalidate(ghcb);

if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
- ghcb_set_rax(ghcb, input->data_gpa);
- ghcb_set_rbx(ghcb, input->data_npages);
+ ghcb_set_rax(ghcb, __pa(req->data));
+ ghcb_set_rbx(ghcb, *req->data_npages);
}

ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
@@ -2212,7 +2219,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
/* Number of expected pages are returned in RBX */
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
- input->data_npages = ghcb_get_rbx(ghcb);
+ *req->data_npages = ghcb_get_rbx(ghcb);
ret = -ENOSPC;
break;
}
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 49bafd2e9f42..5801dd52ffdf 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -23,8 +23,7 @@

#include <asm/svm.h>
#include <asm/sev.h>
-
-#include "sev-guest.h"
+#include <asm/sev-guest.h>

#define DEVICE_NAME "sev-guest"

@@ -192,7 +191,7 @@ static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
return -EBADMSG;
}

-static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
+static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req)
{
struct snp_guest_msg *resp = &snp_dev->secret_response;
struct snp_guest_msg *req = &snp_dev->secret_request;
@@ -220,29 +219,28 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
* If the message size is greater than our buffer length then return
* an error.
*/
- if (unlikely((resp_hdr->msg_sz + ctx->authsize) > sz))
+ if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
return -EBADMSG;

/* Decrypt the payload */
- return dec_payload(ctx, resp, payload, resp_hdr->msg_sz);
+ return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
}

-static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
- void *payload, size_t sz)
+static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
{
- struct snp_guest_msg *req = &snp_dev->secret_request;
- struct snp_guest_msg_hdr *hdr = &req->hdr;
+ struct snp_guest_msg *msg = &snp_dev->secret_request;
+ struct snp_guest_msg_hdr *hdr = &msg->hdr;

- memset(req, 0, sizeof(*req));
+ memset(msg, 0, sizeof(*msg));

hdr->algo = SNP_AEAD_AES_256_GCM;
hdr->hdr_version = MSG_HDR_VER;
hdr->hdr_sz = sizeof(*hdr);
- hdr->msg_type = type;
- hdr->msg_version = version;
+ hdr->msg_type = req->msg_type;
+ hdr->msg_version = req->msg_version;
hdr->msg_seqno = seqno;
- hdr->msg_vmpck = vmpck_id;
- hdr->msg_sz = sz;
+ hdr->msg_vmpck = req->vmpck_id;
+ hdr->msg_sz = req->req_sz;

/* Verify the sequence number is non-zero */
if (!hdr->msg_seqno)
@@ -251,10 +249,10 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
pr_debug("request [seqno %lld type %d version %d sz %d]\n",
hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);

- return __enc_payload(snp_dev->ctx, req, payload, sz);
+ return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
}

-static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
+static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
struct snp_guest_request_ioctl *rio)
{
unsigned long req_start = jiffies;
@@ -269,7 +267,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
* sequence number must be incremented or the VMPCK must be deleted to
* prevent reuse of the IV.
*/
- rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
+ rc = snp_issue_guest_request(req, &snp_dev->input, rio);
switch (rc) {
case -ENOSPC:
/*
@@ -279,8 +277,8 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
* order to increment the sequence number and thus avoid
* IV reuse.
*/
- override_npages = snp_dev->input.data_npages;
- exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+ override_npages = *req->data_npages;
+ req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;

/*
* Override the error to inform callers the given extended
@@ -335,15 +333,13 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
}

if (override_npages)
- snp_dev->input.data_npages = override_npages;
+ *req->data_npages = override_npages;

return rc;
}

-static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
- struct snp_guest_request_ioctl *rio, u8 type,
- void *req_buf, size_t req_sz, void *resp_buf,
- u32 resp_sz)
+static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio)
{
u64 seqno;
int rc;
@@ -357,7 +353,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));

/* Encrypt the userspace provided payload in snp_dev->secret_request. */
- rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
+ rc = enc_payload(snp_dev, seqno, req);
if (rc)
return rc;

@@ -368,7 +364,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
memcpy(snp_dev->request, &snp_dev->secret_request,
sizeof(snp_dev->secret_request));

- rc = __handle_guest_request(snp_dev, exit_code, rio);
+ rc = __handle_guest_request(snp_dev, req, rio);
if (rc) {
if (rc == -EIO &&
rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
@@ -377,12 +373,11 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
dev_alert(snp_dev->dev,
"Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
rc, rio->exitinfo2);
-
snp_disable_vmpck(snp_dev);
return rc;
}

- rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz);
+ rc = verify_and_dec_payload(snp_dev, req);
if (rc) {
dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc);
snp_disable_vmpck(snp_dev);
@@ -394,6 +389,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,

static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
+ struct snp_guest_req guest_req = {0};
struct snp_report_resp *resp;
struct snp_report_req req;
int rc, resp_len;
@@ -416,9 +412,16 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
if (!resp)
return -ENOMEM;

- rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
- SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
- resp_len);
+ guest_req.msg_version = arg->msg_version;
+ guest_req.msg_type = SNP_MSG_REPORT_REQ;
+ guest_req.vmpck_id = vmpck_id;
+ guest_req.req_buf = &req;
+ guest_req.req_sz = sizeof(req);
+ guest_req.resp_buf = resp->data;
+ guest_req.resp_sz = resp_len;
+ guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+
+ rc = snp_send_guest_request(snp_dev, &guest_req, arg);
if (rc)
goto e_free;

@@ -433,6 +436,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
struct snp_derived_key_resp resp = {0};
+ struct snp_guest_req guest_req = {0};
struct snp_derived_key_req req;
int rc, resp_len;
/* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
@@ -455,8 +459,16 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
return -EFAULT;

- rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
- SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len);
+ guest_req.msg_version = arg->msg_version;
+ guest_req.msg_type = SNP_MSG_KEY_REQ;
+ guest_req.vmpck_id = vmpck_id;
+ guest_req.req_buf = &req;
+ guest_req.req_sz = sizeof(req);
+ guest_req.resp_buf = buf;
+ guest_req.resp_sz = resp_len;
+ guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+
+ rc = snp_send_guest_request(snp_dev, &guest_req, arg);
if (rc)
return rc;

@@ -472,9 +484,11 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque

static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
+ struct snp_guest_req guest_req = {0};
struct snp_ext_report_req req;
struct snp_report_resp *resp;
- int ret, npages = 0, resp_len;
+ int ret, resp_len;
+ size_t npages = 0;

lockdep_assert_held(&snp_dev->cmd_mutex);

@@ -514,14 +528,22 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
if (!resp)
return -ENOMEM;

- snp_dev->input.data_npages = npages;
- ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg,
- SNP_MSG_REPORT_REQ, &req.data,
- sizeof(req.data), resp->data, resp_len);
+ guest_req.msg_version = arg->msg_version;
+ guest_req.msg_type = SNP_MSG_REPORT_REQ;
+ guest_req.vmpck_id = vmpck_id;
+ guest_req.req_buf = &req.data;
+ guest_req.req_sz = sizeof(req.data);
+ guest_req.resp_buf = resp->data;
+ guest_req.resp_sz = resp_len;
+ guest_req.exit_code = SVM_VMGEXIT_EXT_GUEST_REQUEST;
+ guest_req.data = snp_dev->certs_data;
+ guest_req.data_npages = &npages;
+
+ ret = snp_send_guest_request(snp_dev, &guest_req, arg);

/* If certs length is invalid then copy the returned length */
if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
- req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
+ req.certs_len = npages << PAGE_SHIFT;

if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req)))
ret = -EFAULT;
@@ -530,7 +552,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
if (ret)
goto e_free;

- if (npages &&
+ if (npages && req.certs_len &&
copy_to_user((void __user *)req.certs_address, snp_dev->certs_data,
req.certs_len)) {
ret = -EFAULT;
@@ -734,7 +756,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
/* initial the input address for guest request */
snp_dev->input.req_gpa = __pa(snp_dev->request);
snp_dev->input.resp_gpa = __pa(snp_dev->response);
- snp_dev->input.data_gpa = __pa(snp_dev->certs_data);

ret = misc_register(misc);
if (ret)
--
2.34.1

2023-10-30 06:39:17

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 07/14] x86/sev: Move and reorganize sev guest request api

For enabling Secure TSC, SEV-SNP guests need to communicate with the
AMD Security Processor early during boot. Many of the required
functions are implemented in the sev-guest driver and therefore not
available at early boot. Move the required functions and provide an
API to the driver to assign key and send guest request.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/sev-guest.h | 84 +++-
arch/x86/include/asm/sev.h | 10 -
arch/x86/kernel/sev.c | 466 ++++++++++++++++++++++-
drivers/virt/coco/sev-guest/Kconfig | 1 -
drivers/virt/coco/sev-guest/sev-guest.c | 486 +-----------------------
6 files changed, 555 insertions(+), 493 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 66bfabae8814..245a18f6910a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1509,6 +1509,7 @@ config AMD_MEM_ENCRYPT
select ARCH_HAS_CC_PLATFORM
select X86_MEM_ENCRYPT
select UNACCEPTED_MEMORY
+ select CRYPTO_LIB_AESGCM
help
Say yes to enable support for the encryption of system memory.
This requires an AMD processor that supports Secure Memory
diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
index 22ef97b55069..e6f94208173d 100644
--- a/arch/x86/include/asm/sev-guest.h
+++ b/arch/x86/include/asm/sev-guest.h
@@ -11,6 +11,11 @@
#define __VIRT_SEVGUEST_H__

#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <asm/sev.h>
+
+#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
+#define SNP_REQ_RETRY_DELAY (2*HZ)

#define MAX_AUTHTAG_LEN 32
#define AUTHTAG_LEN 16
@@ -58,11 +63,45 @@ struct snp_guest_msg_hdr {
u8 rsvd3[35];
} __packed;

+/* SNP Guest message request */
+struct snp_req_data {
+ unsigned long req_gpa;
+ unsigned long resp_gpa;
+};
+
struct snp_guest_msg {
struct snp_guest_msg_hdr hdr;
u8 payload[4000];
} __packed;

+struct sev_guest_platform_data {
+ /* request and response are in unencrypted memory */
+ struct snp_guest_msg *request, *response;
+
+ struct snp_secrets_page_layout *layout;
+ struct snp_req_data input;
+};
+
+struct snp_guest_dev {
+ struct device *dev;
+ struct miscdevice misc;
+
+ /* Mutex to serialize the shared buffer access and command handling. */
+ struct mutex cmd_mutex;
+
+ void *certs_data;
+ struct aesgcm_ctx *ctx;
+
+ /*
+ * Avoid information leakage by double-buffering shared messages
+ * in fields that are in regular encrypted memory
+ */
+ struct snp_guest_msg secret_request, secret_response;
+
+ struct sev_guest_platform_data *pdata;
+ unsigned int vmpck_id;
+};
+
struct snp_guest_req {
void *req_buf, *resp_buf, *data;
size_t req_sz, resp_sz, *data_npages;
@@ -72,6 +111,47 @@ struct snp_guest_req {
u8 msg_type;
};

-int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
- struct snp_guest_request_ioctl *rio);
+int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
+int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio);
+bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id);
+bool snp_is_vmpck_empty(unsigned int vmpck_id);
+
+static void free_shared_pages(void *buf, size_t sz)
+{
+ unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
+ int ret;
+
+ if (!buf)
+ return;
+
+ ret = set_memory_encrypted((unsigned long)buf, npages);
+ if (ret) {
+ WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
+ return;
+ }
+
+ __free_pages(virt_to_page(buf), get_order(sz));
+}
+
+static void *alloc_shared_pages(size_t sz)
+{
+ unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
+ struct page *page;
+ int ret;
+
+ page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
+ if (!page)
+ return NULL;
+
+ ret = set_memory_decrypted((unsigned long)page_address(page), npages);
+ if (ret) {
+ pr_err("%s: failed to mark page shared, ret=%d\n", __func__, ret);
+ __free_pages(page, get_order(sz));
+ return NULL;
+ }
+
+ return page_address(page);
+}
+
#endif /* __VIRT_SEVGUEST_H__ */
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 78465a8c7dc6..783150458864 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -93,16 +93,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);

#define RMPADJUST_VMSA_PAGE_BIT BIT(16)

-/* SNP Guest message request */
-struct snp_req_data {
- unsigned long req_gpa;
- unsigned long resp_gpa;
-};
-
-struct sev_guest_platform_data {
- u64 secrets_gpa;
-};
-
/*
* The secrets page contains 96-bytes of reserved field that can be used by
* the guest OS. The guest OS uses the area to save the message sequence
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index fd3b822fa9e7..fb3b1feb1b84 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/psp-sev.h>
#include <uapi/linux/sev-guest.h>
+#include <crypto/gcm.h>

#include <asm/cpu_entry_area.h>
#include <asm/stacktrace.h>
@@ -941,6 +942,457 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
free_page((unsigned long)vmsa);
}

+static struct sev_guest_platform_data *platform_data;
+
+static inline u8 *snp_get_vmpck(unsigned int vmpck_id)
+{
+ if (!platform_data)
+ return NULL;
+
+ return platform_data->layout->vmpck0 + vmpck_id * VMPCK_KEY_LEN;
+}
+
+static inline u32 *snp_get_os_area_msg_seqno(unsigned int vmpck_id)
+{
+ if (!platform_data)
+ return NULL;
+
+ return &platform_data->layout->os_area.msg_seqno_0 + vmpck_id;
+}
+
+bool snp_is_vmpck_empty(unsigned int vmpck_id)
+{
+ char zero_key[VMPCK_KEY_LEN] = {0};
+ u8 *key = snp_get_vmpck(vmpck_id);
+
+ if (key)
+ return !memcmp(key, zero_key, VMPCK_KEY_LEN);
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snp_is_vmpck_empty);
+
+/*
+ * If an error is received from the host or AMD Secure Processor (ASP) there
+ * are two options. Either retry the exact same encrypted request or discontinue
+ * using the VMPCK.
+ *
+ * This is because in the current encryption scheme GHCB v2 uses AES-GCM to
+ * encrypt the requests. The IV for this scheme is the sequence number. GCM
+ * cannot tolerate IV reuse.
+ *
+ * The ASP FW v1.51 only increments the sequence numbers on a successful
+ * guest<->ASP back and forth and only accepts messages at its exact sequence
+ * number.
+ *
+ * So if the sequence number were to be reused the encryption scheme is
+ * vulnerable. If the sequence number were incremented for a fresh IV the ASP
+ * will reject the request.
+ */
+static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
+{
+ u8 *key = snp_get_vmpck(snp_dev->vmpck_id);
+
+ pr_alert("Disabling vmpck_id %d to prevent IV reuse.\n", snp_dev->vmpck_id);
+ memzero_explicit(key, VMPCK_KEY_LEN);
+}
+
+static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
+{
+ u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
+ u64 count;
+
+ if (!os_area_msg_seqno) {
+ pr_err("SNP unable to get message sequence counter\n");
+ return 0;
+ }
+
+ lockdep_assert_held(&snp_dev->cmd_mutex);
+
+ /* Read the current message sequence counter from secrets pages */
+ count = *os_area_msg_seqno;
+
+ return count + 1;
+}
+
+/* Return a non-zero on success */
+static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
+{
+ u64 count = __snp_get_msg_seqno(snp_dev);
+
+ /*
+ * The message sequence counter for the SNP guest request is a 64-bit
+ * value but the version 2 of GHCB specification defines a 32-bit storage
+ * for it. If the counter exceeds the 32-bit value then return zero.
+ * The caller should check the return value, but if the caller happens to
+ * not check the value and use it, then the firmware treats zero as an
+ * invalid number and will fail the message request.
+ */
+ if (count >= UINT_MAX) {
+ pr_err("SNP request message sequence counter overflow\n");
+ return 0;
+ }
+
+ return count;
+}
+
+static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
+{
+ u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
+
+ if (!os_area_msg_seqno) {
+ pr_err("SNP unable to get message sequence counter\n");
+ return;
+ }
+
+ /*
+ * The counter is also incremented by the PSP, so increment it by 2
+ * and save in secrets page.
+ */
+ *os_area_msg_seqno += 2;
+}
+
+static struct aesgcm_ctx *snp_init_crypto(unsigned int vmpck_id)
+{
+ struct aesgcm_ctx *ctx;
+ u8 *key;
+
+ if (snp_is_vmpck_empty(vmpck_id)) {
+ pr_err("SNP: vmpck id %d is null\n", vmpck_id);
+ return NULL;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
+ if (!ctx)
+ return NULL;
+
+ key = snp_get_vmpck(vmpck_id);
+ if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
+ pr_err("SNP: crypto init failed\n");
+ kfree(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev)
+{
+ struct sev_guest_platform_data *pdata;
+ int ret;
+
+ if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
+ pr_err("SNP not supported\n");
+ return 0;
+ }
+
+ if (platform_data) {
+ pr_debug("SNP platform data already initialized.\n");
+ goto create_ctx;
+ }
+
+ if (!secrets_pa) {
+ pr_err("SNP no secrets page\n");
+ return -ENODEV;
+ }
+
+ pdata = kzalloc(sizeof(struct sev_guest_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("SNP alloc failed\n");
+ return -ENOMEM;
+ }
+
+ pdata->layout = (__force void *)ioremap_encrypted(secrets_pa, PAGE_SIZE);
+ if (!pdata->layout) {
+ pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
+ goto e_free_pdata;
+ }
+
+ ret = -ENOMEM;
+ /* Allocate the shared page used for the request and response message. */
+ pdata->request = alloc_shared_pages(sizeof(struct snp_guest_msg));
+ if (!pdata->request)
+ goto e_unmap;
+
+ pdata->response = alloc_shared_pages(sizeof(struct snp_guest_msg));
+ if (!pdata->response)
+ goto e_free_request;
+
+ /* initial the input address for guest request */
+ pdata->input.req_gpa = __pa(pdata->request);
+ pdata->input.resp_gpa = __pa(pdata->response);
+ platform_data = pdata;
+
+create_ctx:
+ ret = -EIO;
+ snp_dev->ctx = snp_init_crypto(snp_dev->vmpck_id);
+ if (!snp_dev->ctx) {
+ pr_err("SNP init crypto failed\n");
+ platform_data = NULL;
+ goto e_free_response;
+ }
+
+ snp_dev->pdata = platform_data;
+ return 0;
+
+e_free_response:
+ free_shared_pages(pdata->response, sizeof(struct snp_guest_msg));
+e_free_request:
+ free_shared_pages(pdata->request, sizeof(struct snp_guest_msg));
+e_unmap:
+ iounmap(pdata->layout);
+e_free_pdata:
+ kfree(pdata);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snp_setup_psp_messaging);
+
+static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
+ void *plaintext, size_t len)
+{
+ struct snp_guest_msg_hdr *hdr = &msg->hdr;
+ u8 iv[GCM_AES_IV_SIZE] = {};
+
+ if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
+ return -EBADMSG;
+
+ memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
+ aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
+ iv, hdr->authtag);
+ return 0;
+}
+
+static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
+ void *plaintext, size_t len)
+{
+ struct snp_guest_msg_hdr *hdr = &msg->hdr;
+ u8 iv[GCM_AES_IV_SIZE] = {};
+
+ memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
+ if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
+ AAD_LEN, iv, hdr->authtag))
+ return 0;
+ else
+ return -EBADMSG;
+}
+
+static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req,
+ struct sev_guest_platform_data *pdata)
+{
+ struct snp_guest_msg *resp = &snp_dev->secret_response;
+ struct snp_guest_msg *req = &snp_dev->secret_request;
+ struct snp_guest_msg_hdr *req_hdr = &req->hdr;
+ struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
+ struct aesgcm_ctx *ctx = snp_dev->ctx;
+
+ pr_debug("response [seqno %lld type %d version %d sz %d]\n",
+ resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version,
+ resp_hdr->msg_sz);
+
+ /* Copy response from shared memory to encrypted memory. */
+ memcpy(resp, pdata->response, sizeof(*resp));
+
+ /* Verify that the sequence counter is incremented by 1 */
+ if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
+ return -EBADMSG;
+
+ /* Verify response message type and version number. */
+ if (resp_hdr->msg_type != (req_hdr->msg_type + 1) ||
+ resp_hdr->msg_version != req_hdr->msg_version)
+ return -EBADMSG;
+
+ /*
+ * If the message size is greater than our buffer length then return
+ * an error.
+ */
+ if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
+ return -EBADMSG;
+
+ return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
+}
+
+static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
+{
+ struct snp_guest_msg *msg = &snp_dev->secret_request;
+ struct snp_guest_msg_hdr *hdr = &msg->hdr;
+
+ memset(msg, 0, sizeof(*msg));
+
+ hdr->algo = SNP_AEAD_AES_256_GCM;
+ hdr->hdr_version = MSG_HDR_VER;
+ hdr->hdr_sz = sizeof(*hdr);
+ hdr->msg_type = req->msg_type;
+ hdr->msg_version = req->msg_version;
+ hdr->msg_seqno = seqno;
+ hdr->msg_vmpck = req->vmpck_id;
+ hdr->msg_sz = req->req_sz;
+
+ /* Verify the sequence number is non-zero */
+ if (!hdr->msg_seqno)
+ return -ENOSR;
+
+ pr_debug("request [seqno %lld type %d version %d sz %d]\n",
+ hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
+
+ return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
+}
+
+static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
+ struct snp_guest_request_ioctl *rio);
+
+static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio,
+ struct sev_guest_platform_data *pdata)
+{
+ unsigned long req_start = jiffies;
+ unsigned int override_npages = 0;
+ u64 override_err = 0;
+ int rc;
+
+retry_request:
+ /*
+ * Call firmware to process the request. In this function the encrypted
+ * message enters shared memory with the host. So after this call the
+ * sequence number must be incremented or the VMPCK must be deleted to
+ * prevent reuse of the IV.
+ */
+ rc = snp_issue_guest_request(req, &pdata->input, rio);
+ switch (rc) {
+ case -ENOSPC:
+ /*
+ * If the extended guest request fails due to having too
+ * small of a certificate data buffer, retry the same
+ * guest request without the extended data request in
+ * order to increment the sequence number and thus avoid
+ * IV reuse.
+ */
+ override_npages = *req->data_npages;
+ req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+
+ /*
+ * Override the error to inform callers the given extended
+ * request buffer size was too small and give the caller the
+ * required buffer size.
+ */
+ override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN);
+
+ /*
+ * If this call to the firmware succeeds, the sequence number can
+ * be incremented allowing for continued use of the VMPCK. If
+ * there is an error reflected in the return value, this value
+ * is checked further down and the result will be the deletion
+ * of the VMPCK and the error code being propagated back to the
+ * user as an ioctl() return code.
+ */
+ goto retry_request;
+
+ /*
+ * The host may return SNP_GUEST_REQ_ERR_BUSY if the request has been
+ * throttled. Retry in the driver to avoid returning and reusing the
+ * message sequence number on a different message.
+ */
+ case -EAGAIN:
+ if (jiffies - req_start > SNP_REQ_MAX_RETRY_DURATION) {
+ rc = -ETIMEDOUT;
+ break;
+ }
+ schedule_timeout_killable(SNP_REQ_RETRY_DELAY);
+ goto retry_request;
+ }
+
+ /*
+ * Increment the message sequence number. There is no harm in doing
+ * this now because decryption uses the value stored in the response
+ * structure and any failure will wipe the VMPCK, preventing further
+ * use anyway.
+ */
+ snp_inc_msg_seqno(snp_dev);
+
+ if (override_err) {
+ rio->exitinfo2 = override_err;
+
+ /*
+ * If an extended guest request was issued and the supplied certificate
+ * buffer was not large enough, a standard guest request was issued to
+ * prevent IV reuse. If the standard request was successful, return -EIO
+ * back to the caller as would have originally been returned.
+ */
+ if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
+ rc = -EIO;
+ }
+
+ if (override_npages)
+ *req->data_npages = override_npages;
+
+ return rc;
+}
+
+int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio)
+{
+ struct sev_guest_platform_data *pdata;
+ u64 seqno;
+ int rc;
+
+ if (!snp_dev || !snp_dev->pdata || !req || !rio)
+ return -ENODEV;
+
+ pdata = snp_dev->pdata;
+
+ /* Get message sequence and verify that its a non-zero */
+ seqno = snp_get_msg_seqno(snp_dev);
+ if (!seqno)
+ return -EIO;
+
+ /* Clear shared memory's response for the host to populate. */
+ memset(pdata->response, 0, sizeof(struct snp_guest_msg));
+
+ /* Encrypt the userspace provided payload in pdata->secret_request. */
+ rc = enc_payload(snp_dev, seqno, req);
+ if (rc)
+ return rc;
+
+ /*
+ * Write the fully encrypted request to the shared unencrypted
+ * request page.
+ */
+ memcpy(pdata->request, &snp_dev->secret_request, sizeof(snp_dev->secret_request));
+
+ rc = __handle_guest_request(snp_dev, req, rio, pdata);
+ if (rc) {
+ if (rc == -EIO &&
+ rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
+ return rc;
+
+ pr_alert("Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
+ rc, rio->exitinfo2);
+ snp_disable_vmpck(snp_dev);
+ return rc;
+ }
+
+ rc = verify_and_dec_payload(snp_dev, req, pdata);
+ if (rc) {
+ pr_alert("Detected unexpected decode failure from ASP. rc: %d\n", rc);
+ snp_disable_vmpck(snp_dev);
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snp_send_guest_request);
+
+bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
+{
+ if (WARN_ON(vmpck_id > 3))
+ return false;
+
+ dev->vmpck_id = vmpck_id;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snp_assign_vmpck);
+
static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
{
struct sev_es_save_area *cur_vmsa, *vmsa;
@@ -2150,8 +2602,8 @@ static int __init init_sev_config(char *str)
}
__setup("sev=", init_sev_config);

-int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
- struct snp_guest_request_ioctl *rio)
+static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
+ struct snp_guest_request_ioctl *rio)
{
struct ghcb_state state;
struct es_em_ctxt ctxt;
@@ -2218,7 +2670,6 @@ int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *inpu

return ret;
}
-EXPORT_SYMBOL_GPL(snp_issue_guest_request);

static struct platform_device sev_guest_device = {
.name = "sev-guest",
@@ -2227,18 +2678,9 @@ static struct platform_device sev_guest_device = {

static int __init snp_init_platform_device(void)
{
- struct sev_guest_platform_data data;
-
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
return -ENODEV;

- if (!secrets_pa)
- return -ENODEV;
-
- data.secrets_gpa = secrets_pa;
- if (platform_device_add_data(&sev_guest_device, &data, sizeof(data)))
- return -ENODEV;
-
if (platform_device_register(&sev_guest_device))
return -ENODEV;

diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig
index bcc760bfb468..c130456ad401 100644
--- a/drivers/virt/coco/sev-guest/Kconfig
+++ b/drivers/virt/coco/sev-guest/Kconfig
@@ -2,7 +2,6 @@ config SEV_GUEST
tristate "AMD SEV Guest driver"
default m
depends on AMD_MEM_ENCRYPT
- select CRYPTO_LIB_AESGCM
help
SEV-SNP firmware provides the guest a mechanism to communicate with
the PSP without risk from a malicious hypervisor who wishes to read,
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 4dd094c73e2f..062ff12b030e 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -27,32 +27,6 @@

#define DEVICE_NAME "sev-guest"

-#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
-#define SNP_REQ_RETRY_DELAY (2*HZ)
-
-struct snp_guest_dev {
- struct device *dev;
- struct miscdevice misc;
-
- /* Mutex to serialize the shared buffer access and command handling. */
- struct mutex cmd_mutex;
-
- void *certs_data;
- struct aesgcm_ctx *ctx;
- /* request and response are in unencrypted memory */
- struct snp_guest_msg *request, *response;
-
- /*
- * Avoid information leakage by double-buffering shared messages
- * in fields that are in regular encrypted memory.
- */
- struct snp_guest_msg secret_request, secret_response;
-
- struct snp_secrets_page_layout *layout;
- struct snp_req_data input;
- unsigned int vmpck_id;
-};
-
static u32 vmpck_id;
module_param(vmpck_id, uint, 0444);
MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.");
@@ -60,95 +34,6 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
/* Mutex to serialize the shared buffer access and command handling. */
static DEFINE_MUTEX(snp_cmd_mutex);

-static inline u8 *snp_get_vmpck(struct snp_guest_dev *snp_dev)
-{
- return snp_dev->layout->vmpck0 + snp_dev->vmpck_id * VMPCK_KEY_LEN;
-}
-
-static inline u32 *snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
-{
- return &snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;
-}
-
-static bool snp_is_vmpck_empty(struct snp_guest_dev *snp_dev)
-{
- char zero_key[VMPCK_KEY_LEN] = {0};
- u8 *key = snp_get_vmpck(snp_dev);
-
- return !memcmp(key, zero_key, VMPCK_KEY_LEN);
-}
-
-/*
- * If an error is received from the host or AMD Secure Processor (ASP) there
- * are two options. Either retry the exact same encrypted request or discontinue
- * using the VMPCK.
- *
- * This is because in the current encryption scheme GHCB v2 uses AES-GCM to
- * encrypt the requests. The IV for this scheme is the sequence number. GCM
- * cannot tolerate IV reuse.
- *
- * The ASP FW v1.51 only increments the sequence numbers on a successful
- * guest<->ASP back and forth and only accepts messages at its exact sequence
- * number.
- *
- * So if the sequence number were to be reused the encryption scheme is
- * vulnerable. If the sequence number were incremented for a fresh IV the ASP
- * will reject the request.
- */
-static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
-{
- u8 *key = snp_get_vmpck(snp_dev);
-
- dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n",
- snp_dev->vmpck_id);
- memzero_explicit(key, VMPCK_KEY_LEN);
-}
-
-static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
-{
- u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
- u64 count;
-
- lockdep_assert_held(&snp_dev->cmd_mutex);
-
- /* Read the current message sequence counter from secrets pages */
- count = *os_area_msg_seqno;
-
- return count + 1;
-}
-
-/* Return a non-zero on success */
-static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
-{
- u64 count = __snp_get_msg_seqno(snp_dev);
-
- /*
- * The message sequence counter for the SNP guest request is a 64-bit
- * value but the version 2 of GHCB specification defines a 32-bit storage
- * for it. If the counter exceeds the 32-bit value then return zero.
- * The caller should check the return value, but if the caller happens to
- * not check the value and use it, then the firmware treats zero as an
- * invalid number and will fail the message request.
- */
- if (count >= UINT_MAX) {
- dev_err(snp_dev->dev, "request message sequence counter overflow\n");
- return 0;
- }
-
- return count;
-}
-
-static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
-{
- u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
-
- /*
- * The counter is also incremented by the PSP, so increment it by 2
- * and save in secrets page.
- */
- *os_area_msg_seqno += 2;
-}
-
static inline struct snp_guest_dev *to_snp_dev(struct file *file)
{
struct miscdevice *dev = file->private_data;
@@ -156,255 +41,6 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
return container_of(dev, struct snp_guest_dev, misc);
}

-static struct aesgcm_ctx *snp_init_crypto(struct snp_guest_dev *snp_dev)
-{
- struct aesgcm_ctx *ctx;
- u8 *key;
-
- if (snp_is_vmpck_empty(snp_dev)) {
- pr_err("SNP: vmpck id %d is null\n", snp_dev->vmpck_id);
- return NULL;
- }
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
- if (!ctx)
- return NULL;
-
- key = snp_get_vmpck(snp_dev);
- if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
- pr_err("SNP: crypto init failed\n");
- kfree(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
- void *plaintext, size_t len)
-{
- struct snp_guest_msg_hdr *hdr = &msg->hdr;
- u8 iv[GCM_AES_IV_SIZE] = {};
-
- if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
- return -EBADMSG;
-
- memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
- aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
- iv, hdr->authtag);
- return 0;
-}
-
-static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
- void *plaintext, size_t len)
-{
- struct snp_guest_msg_hdr *hdr = &msg->hdr;
- u8 iv[GCM_AES_IV_SIZE] = {};
-
- memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
- if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
- AAD_LEN, iv, hdr->authtag))
- return 0;
- else
- return -EBADMSG;
-}
-
-static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req)
-{
- struct snp_guest_msg *resp = &snp_dev->secret_response;
- struct snp_guest_msg *req = &snp_dev->secret_request;
- struct snp_guest_msg_hdr *req_hdr = &req->hdr;
- struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
- struct aesgcm_ctx *ctx = snp_dev->ctx;
-
- pr_debug("response [seqno %lld type %d version %d sz %d]\n",
- resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version,
- resp_hdr->msg_sz);
-
- /* Copy response from shared memory to encrypted memory. */
- memcpy(resp, snp_dev->response, sizeof(*resp));
-
- /* Verify that the sequence counter is incremented by 1 */
- if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
- return -EBADMSG;
-
- /* Verify response message type and version number. */
- if (resp_hdr->msg_type != (req_hdr->msg_type + 1) ||
- resp_hdr->msg_version != req_hdr->msg_version)
- return -EBADMSG;
-
- /*
- * If the message size is greater than our buffer length then return
- * an error.
- */
- if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
- return -EBADMSG;
-
- /* Decrypt the payload */
- return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
-}
-
-static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
-{
- struct snp_guest_msg *msg = &snp_dev->secret_request;
- struct snp_guest_msg_hdr *hdr = &msg->hdr;
-
- memset(msg, 0, sizeof(*msg));
-
- hdr->algo = SNP_AEAD_AES_256_GCM;
- hdr->hdr_version = MSG_HDR_VER;
- hdr->hdr_sz = sizeof(*hdr);
- hdr->msg_type = req->msg_type;
- hdr->msg_version = req->msg_version;
- hdr->msg_seqno = seqno;
- hdr->msg_vmpck = req->vmpck_id;
- hdr->msg_sz = req->req_sz;
-
- /* Verify the sequence number is non-zero */
- if (!hdr->msg_seqno)
- return -ENOSR;
-
- pr_debug("request [seqno %lld type %d version %d sz %d]\n",
- hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
-
- return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
-}
-
-static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
- struct snp_guest_request_ioctl *rio)
-{
- unsigned long req_start = jiffies;
- unsigned int override_npages = 0;
- u64 override_err = 0;
- int rc;
-
-retry_request:
- /*
- * Call firmware to process the request. In this function the encrypted
- * message enters shared memory with the host. So after this call the
- * sequence number must be incremented or the VMPCK must be deleted to
- * prevent reuse of the IV.
- */
- rc = snp_issue_guest_request(req, &snp_dev->input, rio);
- switch (rc) {
- case -ENOSPC:
- /*
- * If the extended guest request fails due to having too
- * small of a certificate data buffer, retry the same
- * guest request without the extended data request in
- * order to increment the sequence number and thus avoid
- * IV reuse.
- */
- override_npages = *req->data_npages;
- req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
-
- /*
- * Override the error to inform callers the given extended
- * request buffer size was too small and give the caller the
- * required buffer size.
- */
- override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN);
-
- /*
- * If this call to the firmware succeeds, the sequence number can
- * be incremented allowing for continued use of the VMPCK. If
- * there is an error reflected in the return value, this value
- * is checked further down and the result will be the deletion
- * of the VMPCK and the error code being propagated back to the
- * user as an ioctl() return code.
- */
- goto retry_request;
-
- /*
- * The host may return SNP_GUEST_VMM_ERR_BUSY if the request has been
- * throttled. Retry in the driver to avoid returning and reusing the
- * message sequence number on a different message.
- */
- case -EAGAIN:
- if (jiffies - req_start > SNP_REQ_MAX_RETRY_DURATION) {
- rc = -ETIMEDOUT;
- break;
- }
- schedule_timeout_killable(SNP_REQ_RETRY_DELAY);
- goto retry_request;
- }
-
- /*
- * Increment the message sequence number. There is no harm in doing
- * this now because decryption uses the value stored in the response
- * structure and any failure will wipe the VMPCK, preventing further
- * use anyway.
- */
- snp_inc_msg_seqno(snp_dev);
-
- if (override_err) {
- rio->exitinfo2 = override_err;
-
- /*
- * If an extended guest request was issued and the supplied certificate
- * buffer was not large enough, a standard guest request was issued to
- * prevent IV reuse. If the standard request was successful, return -EIO
- * back to the caller as would have originally been returned.
- */
- if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
- rc = -EIO;
- }
-
- if (override_npages)
- *req->data_npages = override_npages;
-
- return rc;
-}
-
-static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
- struct snp_guest_request_ioctl *rio)
-{
- u64 seqno;
- int rc;
-
- /* Get message sequence and verify that its a non-zero */
- seqno = snp_get_msg_seqno(snp_dev);
- if (!seqno)
- return -EIO;
-
- /* Clear shared memory's response for the host to populate. */
- memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
-
- /* Encrypt the userspace provided payload in snp_dev->secret_request. */
- rc = enc_payload(snp_dev, seqno, req);
- if (rc)
- return rc;
-
- /*
- * Write the fully encrypted request to the shared unencrypted
- * request page.
- */
- memcpy(snp_dev->request, &snp_dev->secret_request,
- sizeof(snp_dev->secret_request));
-
- rc = __handle_guest_request(snp_dev, req, rio);
- if (rc) {
- if (rc == -EIO &&
- rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
- return rc;
-
- dev_alert(snp_dev->dev,
- "Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
- rc, rio->exitinfo2);
- snp_disable_vmpck(snp_dev);
- return rc;
- }
-
- rc = verify_and_dec_payload(snp_dev, req);
- if (rc) {
- dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc);
- snp_disable_vmpck(snp_dev);
- return rc;
- }
-
- return 0;
-}
-
static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
struct snp_guest_req guest_req = {0};
@@ -604,7 +240,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
mutex_lock(&snp_dev->cmd_mutex);

/* Check if the VMPCK is not empty */
- if (snp_is_vmpck_empty(snp_dev)) {
+ if (snp_is_vmpck_empty(snp_dev->vmpck_id)) {
dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
mutex_unlock(&snp_dev->cmd_mutex);
return -ENOTTY;
@@ -632,147 +268,63 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
return ret;
}

-static void free_shared_pages(void *buf, size_t sz)
-{
- unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
- int ret;
-
- if (!buf)
- return;
-
- ret = set_memory_encrypted((unsigned long)buf, npages);
- if (ret) {
- WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
- return;
- }
-
- __free_pages(virt_to_page(buf), get_order(sz));
-}
-
-static void *alloc_shared_pages(struct device *dev, size_t sz)
-{
- unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
- struct page *page;
- int ret;
-
- page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
- if (!page)
- return NULL;
-
- ret = set_memory_decrypted((unsigned long)page_address(page), npages);
- if (ret) {
- dev_err(dev, "failed to mark page shared, ret=%d\n", ret);
- __free_pages(page, get_order(sz));
- return NULL;
- }
-
- return page_address(page);
-}
-
static const struct file_operations snp_guest_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = snp_guest_ioctl,
};

-bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
-{
- if (WARN_ON(vmpck_id > 3))
- return false;
-
- dev->vmpck_id = vmpck_id;
-
- return true;
-}
-
static int __init sev_guest_probe(struct platform_device *pdev)
{
- struct snp_secrets_page_layout *layout;
- struct sev_guest_platform_data *data;
struct device *dev = &pdev->dev;
struct snp_guest_dev *snp_dev;
struct miscdevice *misc;
- void __iomem *mapping;
int ret;

if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
return -ENODEV;

- if (!dev->platform_data)
- return -ENODEV;
-
- data = (struct sev_guest_platform_data *)dev->platform_data;
- mapping = ioremap_encrypted(data->secrets_gpa, PAGE_SIZE);
- if (!mapping)
- return -ENODEV;
-
- layout = (__force void *)mapping;
-
- ret = -ENOMEM;
snp_dev = devm_kzalloc(&pdev->dev, sizeof(struct snp_guest_dev), GFP_KERNEL);
if (!snp_dev)
- goto e_unmap;
+ return -ENOMEM;

- ret = -EINVAL;
- snp_dev->layout = layout;
if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
- goto e_unmap;
+ ret = -EINVAL;
+ goto e_free_snpdev;
}

- /* Verify that VMPCK is not zero. */
- if (snp_is_vmpck_empty(snp_dev)) {
- dev_err(dev, "vmpck id %d is null\n", vmpck_id);
- goto e_unmap;
+ if (snp_setup_psp_messaging(snp_dev)) {
+ dev_err(dev, "Unable to setup PSP messaging vmpck id %d\n", snp_dev->vmpck_id);
+ ret = -ENODEV;
+ goto e_free_snpdev;
}

mutex_init(&snp_dev->cmd_mutex);
platform_set_drvdata(pdev, snp_dev);
snp_dev->dev = dev;

- /* Allocate the shared page used for the request and response message. */
- snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
- if (!snp_dev->request)
- goto e_unmap;
-
- snp_dev->response = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
- if (!snp_dev->response)
- goto e_free_request;
-
- snp_dev->certs_data = alloc_shared_pages(dev, SEV_FW_BLOB_MAX_SIZE);
+ snp_dev->certs_data = alloc_shared_pages(SEV_FW_BLOB_MAX_SIZE);
if (!snp_dev->certs_data)
- goto e_free_response;
-
- ret = -EIO;
- snp_dev->ctx = snp_init_crypto(snp_dev);
- if (!snp_dev->ctx)
- goto e_free_cert_data;
+ goto e_free_ctx;

misc = &snp_dev->misc;
misc->minor = MISC_DYNAMIC_MINOR;
misc->name = DEVICE_NAME;
misc->fops = &snp_guest_fops;

- /* initial the input address for guest request */
- snp_dev->input.req_gpa = __pa(snp_dev->request);
- snp_dev->input.resp_gpa = __pa(snp_dev->response);
-
ret = misc_register(misc);
if (ret)
- goto e_free_ctx;
+ goto e_free_cert_data;

- dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id);
+ dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", snp_dev->vmpck_id);
return 0;

-e_free_ctx:
- kfree(snp_dev->ctx);
e_free_cert_data:
free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
-e_free_response:
- free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
-e_free_request:
- free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
-e_unmap:
- iounmap(mapping);
+ e_free_ctx:
+ kfree(snp_dev->ctx);
+e_free_snpdev:
+ kfree(snp_dev);
return ret;
}

@@ -780,11 +332,9 @@ static int __exit sev_guest_remove(struct platform_device *pdev)
{
struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev);

- free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
- free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
- free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
- kfree(snp_dev->ctx);
misc_deregister(&snp_dev->misc);
+ kfree(snp_dev->ctx);
+ kfree(snp_dev);

return 0;
}
--
2.34.1

2023-10-30 06:39:21

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 05/14] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

Drop vmpck and os_area_msg_seqno pointers so that secret page layout
does not need to be exposed to the sev-guest driver after the rework.
Instead, add helper APIs to access vmpck and os_area_msg_seqno when
needed.

Also, change function is_vmpck_empty() to snp_is_vmpck_empty() in
preparation for moving to sev.c.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
drivers/virt/coco/sev-guest/sev-guest.c | 85 ++++++++++++-------------
1 file changed, 42 insertions(+), 43 deletions(-)

diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 5801dd52ffdf..4dd094c73e2f 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -50,8 +50,7 @@ struct snp_guest_dev {

struct snp_secrets_page_layout *layout;
struct snp_req_data input;
- u32 *os_area_msg_seqno;
- u8 *vmpck;
+ unsigned int vmpck_id;
};

static u32 vmpck_id;
@@ -61,14 +60,22 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
/* Mutex to serialize the shared buffer access and command handling. */
static DEFINE_MUTEX(snp_cmd_mutex);

-static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
+static inline u8 *snp_get_vmpck(struct snp_guest_dev *snp_dev)
{
- char zero_key[VMPCK_KEY_LEN] = {0};
+ return snp_dev->layout->vmpck0 + snp_dev->vmpck_id * VMPCK_KEY_LEN;
+}

- if (snp_dev->vmpck)
- return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
+static inline u32 *snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
+{
+ return &snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;
+}

- return true;
+static bool snp_is_vmpck_empty(struct snp_guest_dev *snp_dev)
+{
+ char zero_key[VMPCK_KEY_LEN] = {0};
+ u8 *key = snp_get_vmpck(snp_dev);
+
+ return !memcmp(key, zero_key, VMPCK_KEY_LEN);
}

/*
@@ -90,20 +97,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
*/
static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
{
+ u8 *key = snp_get_vmpck(snp_dev);
+
dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n",
- vmpck_id);
- memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
- snp_dev->vmpck = NULL;
+ snp_dev->vmpck_id);
+ memzero_explicit(key, VMPCK_KEY_LEN);
}

static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
{
+ u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
u64 count;

lockdep_assert_held(&snp_dev->cmd_mutex);

/* Read the current message sequence counter from secrets pages */
- count = *snp_dev->os_area_msg_seqno;
+ count = *os_area_msg_seqno;

return count + 1;
}
@@ -131,11 +140,13 @@ static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)

static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
{
+ u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
+
/*
* The counter is also incremented by the PSP, so increment it by 2
* and save in secrets page.
*/
- *snp_dev->os_area_msg_seqno += 2;
+ *os_area_msg_seqno += 2;
}

static inline struct snp_guest_dev *to_snp_dev(struct file *file)
@@ -145,15 +156,22 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
return container_of(dev, struct snp_guest_dev, misc);
}

-static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
+static struct aesgcm_ctx *snp_init_crypto(struct snp_guest_dev *snp_dev)
{
struct aesgcm_ctx *ctx;
+ u8 *key;
+
+ if (snp_is_vmpck_empty(snp_dev)) {
+ pr_err("SNP: vmpck id %d is null\n", snp_dev->vmpck_id);
+ return NULL;
+ }

ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
if (!ctx)
return NULL;

- if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
+ key = snp_get_vmpck(snp_dev);
+ if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
pr_err("SNP: crypto init failed\n");
kfree(ctx);
return NULL;
@@ -586,7 +604,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
mutex_lock(&snp_dev->cmd_mutex);

/* Check if the VMPCK is not empty */
- if (is_vmpck_empty(snp_dev)) {
+ if (snp_is_vmpck_empty(snp_dev)) {
dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
mutex_unlock(&snp_dev->cmd_mutex);
return -ENOTTY;
@@ -656,32 +674,14 @@ static const struct file_operations snp_guest_fops = {
.unlocked_ioctl = snp_guest_ioctl,
};

-static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
+bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
{
- u8 *key = NULL;
+ if (WARN_ON(vmpck_id > 3))
+ return false;

- switch (id) {
- case 0:
- *seqno = &layout->os_area.msg_seqno_0;
- key = layout->vmpck0;
- break;
- case 1:
- *seqno = &layout->os_area.msg_seqno_1;
- key = layout->vmpck1;
- break;
- case 2:
- *seqno = &layout->os_area.msg_seqno_2;
- key = layout->vmpck2;
- break;
- case 3:
- *seqno = &layout->os_area.msg_seqno_3;
- key = layout->vmpck3;
- break;
- default:
- break;
- }
+ dev->vmpck_id = vmpck_id;

- return key;
+ return true;
}

static int __init sev_guest_probe(struct platform_device *pdev)
@@ -713,14 +713,14 @@ static int __init sev_guest_probe(struct platform_device *pdev)
goto e_unmap;

ret = -EINVAL;
- snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
- if (!snp_dev->vmpck) {
+ snp_dev->layout = layout;
+ if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
goto e_unmap;
}

/* Verify that VMPCK is not zero. */
- if (is_vmpck_empty(snp_dev)) {
+ if (snp_is_vmpck_empty(snp_dev)) {
dev_err(dev, "vmpck id %d is null\n", vmpck_id);
goto e_unmap;
}
@@ -728,7 +728,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
mutex_init(&snp_dev->cmd_mutex);
platform_set_drvdata(pdev, snp_dev);
snp_dev->dev = dev;
- snp_dev->layout = layout;

/* Allocate the shared page used for the request and response message. */
snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
@@ -744,7 +743,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
goto e_free_response;

ret = -EIO;
- snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
+ snp_dev->ctx = snp_init_crypto(snp_dev);
if (!snp_dev->ctx)
goto e_free_cert_data;

--
2.34.1

2023-10-30 06:39:26

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

Add support for Secure TSC in SNP enabled guests. Secure TSC allows
guest to securely use RDTSC/RDTSCP instructions as the parameters
being used cannot be changed by hypervisor once the guest is launched.

During the boot-up of the secondary cpus, SecureTSC enabled guests
need to query TSC info from AMD Security Processor. This communication
channel is encrypted between the AMD Security Processor and the guest,
the hypervisor is just the conduit to deliver the guest messages to
the AMD Security Processor. Each message is protected with an
AEAD (AES-256 GCM). Use minimal AES GCM library to encrypt/decrypt SNP
Guest messages to communicate with the PSP.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/coco/core.c | 3 ++
arch/x86/include/asm/sev-guest.h | 18 +++++++
arch/x86/include/asm/sev.h | 2 +
arch/x86/include/asm/svm.h | 6 ++-
arch/x86/kernel/sev.c | 82 ++++++++++++++++++++++++++++++++
arch/x86/mm/mem_encrypt_amd.c | 6 +++
include/linux/cc_platform.h | 8 ++++
7 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
index eeec9986570e..5d5d4d03c543 100644
--- a/arch/x86/coco/core.c
+++ b/arch/x86/coco/core.c
@@ -89,6 +89,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
case CC_ATTR_GUEST_SEV_SNP:
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;

+ case CC_ATTR_GUEST_SECURE_TSC:
+ return sev_status & MSR_AMD64_SNP_SECURE_TSC;
+
default:
return false;
}
diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
index e6f94208173d..58739173eba9 100644
--- a/arch/x86/include/asm/sev-guest.h
+++ b/arch/x86/include/asm/sev-guest.h
@@ -39,6 +39,8 @@ enum msg_type {
SNP_MSG_ABSORB_RSP,
SNP_MSG_VMRK_REQ,
SNP_MSG_VMRK_RSP,
+ SNP_MSG_TSC_INFO_REQ = 17,
+ SNP_MSG_TSC_INFO_RSP,

SNP_MSG_TYPE_MAX
};
@@ -111,6 +113,22 @@ struct snp_guest_req {
u8 msg_type;
};

+struct snp_tsc_info_req {
+#define SNP_TSC_INFO_REQ_SZ 128
+ /* Must be zero filled */
+ u8 rsvd[SNP_TSC_INFO_REQ_SZ];
+} __packed;
+
+struct snp_tsc_info_resp {
+ /* Status of TSC_INFO message */
+ u32 status;
+ u32 rsvd1;
+ u64 tsc_scale;
+ u64 tsc_offset;
+ u32 tsc_factor;
+ u8 rsvd2[100];
+} __packed;
+
int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
struct snp_guest_request_ioctl *rio);
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 783150458864..038a5a15d937 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -200,6 +200,7 @@ void __init __noreturn snp_abort(void);
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
u64 snp_get_unsupported_features(u64 status);
u64 sev_get_status(void);
+void __init snp_secure_tsc_prepare(void);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
@@ -223,6 +224,7 @@ static inline void snp_abort(void) { }
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
static inline u64 sev_get_status(void) { return 0; }
+static inline void __init snp_secure_tsc_prepare(void) { }
#endif

#endif
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 3ac0ffc4f3e2..ee35c0488f56 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -414,7 +414,9 @@ struct sev_es_save_area {
u8 reserved_0x298[80];
u32 pkru;
u32 tsc_aux;
- u8 reserved_0x2f0[24];
+ u64 tsc_scale;
+ u64 tsc_offset;
+ u8 reserved_0x300[8];
u64 rcx;
u64 rdx;
u64 rbx;
@@ -546,7 +548,7 @@ static inline void __unused_size_checks(void)
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298);
- BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0);
+ BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index fb3b1feb1b84..9468809d02c7 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -76,6 +76,10 @@ static u64 sev_hv_features __ro_after_init;
/* Secrets page physical address from the CC blob */
static u64 secrets_pa __ro_after_init;

+/* Secure TSC values read using TSC_INFO SNP Guest request */
+static u64 guest_tsc_scale __ro_after_init;
+static u64 guest_tsc_offset __ro_after_init;
+
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
struct ghcb ghcb_page;
@@ -1393,6 +1397,78 @@ bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
}
EXPORT_SYMBOL_GPL(snp_assign_vmpck);

+static struct snp_guest_dev tsc_snp_dev __initdata;
+
+static int __init snp_get_tsc_info(void)
+{
+ static u8 buf[SNP_TSC_INFO_REQ_SZ + AUTHTAG_LEN];
+ struct snp_guest_request_ioctl rio;
+ struct snp_tsc_info_resp tsc_resp;
+ struct snp_tsc_info_req tsc_req;
+ struct snp_guest_req req;
+ int rc, resp_len;
+
+ /*
+ * The intermediate response buffer is used while decrypting the
+ * response payload. Make sure that it has enough space to cover the
+ * authtag.
+ */
+ resp_len = sizeof(tsc_resp) + AUTHTAG_LEN;
+ if (sizeof(buf) < resp_len)
+ return -EINVAL;
+
+ memset(&tsc_req, 0, sizeof(tsc_req));
+ memset(&req, 0, sizeof(req));
+ memset(&rio, 0, sizeof(rio));
+ memset(buf, 0, sizeof(buf));
+
+ if (!snp_assign_vmpck(&tsc_snp_dev, 0))
+ return -EINVAL;
+
+ /* Initialize the PSP channel to send snp messages */
+ if (snp_setup_psp_messaging(&tsc_snp_dev))
+ sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+
+ req.msg_version = MSG_HDR_VER;
+ req.msg_type = SNP_MSG_TSC_INFO_REQ;
+ req.vmpck_id = tsc_snp_dev.vmpck_id;
+ req.req_buf = &tsc_req;
+ req.req_sz = sizeof(tsc_req);
+ req.resp_buf = buf;
+ req.resp_sz = resp_len;
+ req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+ rc = snp_send_guest_request(&tsc_snp_dev, &req, &rio);
+ if (rc)
+ goto err_req;
+
+ memcpy(&tsc_resp, buf, sizeof(tsc_resp));
+ pr_debug("%s: Valid response status %x scale %llx offset %llx factor %x\n",
+ __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset,
+ tsc_resp.tsc_factor);
+
+ guest_tsc_scale = tsc_resp.tsc_scale;
+ guest_tsc_offset = tsc_resp.tsc_offset;
+
+err_req:
+ /* The response buffer contains the sensitive data, explicitly clear it. */
+ memzero_explicit(buf, sizeof(buf));
+ memzero_explicit(&tsc_resp, sizeof(tsc_resp));
+ memzero_explicit(&req, sizeof(req));
+
+ return rc;
+}
+
+void __init snp_secure_tsc_prepare(void)
+{
+ if (!cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
+ return;
+
+ if (snp_get_tsc_info())
+ sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+
+ pr_debug("SecureTSC enabled\n");
+}
+
static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
{
struct sev_es_save_area *cur_vmsa, *vmsa;
@@ -1493,6 +1569,12 @@ static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
vmsa->vmpl = 0;
vmsa->sev_features = sev_status >> 2;

+ /* Setting Secure TSC parameters */
+ if (cc_platform_has(CC_ATTR_GUEST_SECURE_TSC)) {
+ vmsa->tsc_scale = guest_tsc_scale;
+ vmsa->tsc_offset = guest_tsc_offset;
+ }
+
/* Switch the page over to a VMSA page now that it is initialized */
ret = snp_set_vmsa(vmsa, true);
if (ret) {
diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index 6faea41e99b6..9935fc506e99 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -215,6 +215,11 @@ void __init sme_map_bootdata(char *real_mode_data)
__sme_early_map_unmap_mem(__va(cmdline_paddr), COMMAND_LINE_SIZE, true);
}

+void __init amd_enc_init(void)
+{
+ snp_secure_tsc_prepare();
+}
+
void __init sev_setup_arch(void)
{
phys_addr_t total_mem = memblock_phys_mem_size();
@@ -502,6 +507,7 @@ void __init sme_early_init(void)
x86_platform.guest.enc_status_change_finish = amd_enc_status_change_finish;
x86_platform.guest.enc_tlb_flush_required = amd_enc_tlb_flush_required;
x86_platform.guest.enc_cache_flush_required = amd_enc_cache_flush_required;
+ x86_platform.guest.enc_init = amd_enc_init;

/*
* AMD-SEV-ES intercepts the RDMSR to read the X2APIC ID in the
diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
index cb0d6cd1c12f..e081ca4d5da2 100644
--- a/include/linux/cc_platform.h
+++ b/include/linux/cc_platform.h
@@ -90,6 +90,14 @@ enum cc_attr {
* Examples include TDX Guest.
*/
CC_ATTR_HOTPLUG_DISABLED,
+
+ /**
+ * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
+ *
+ * The platform/OS is running as a guest/virtual machine and actively
+ * using AMD SEV-SNP Secure TSC feature.
+ */
+ CC_ATTR_GUEST_SECURE_TSC,
};

#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
--
2.34.1

2023-10-30 06:39:29

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 10/14] x86/sev: Change TSC MSR behavior for Secure TSC enabled guests

Secure TSC enabled guests should not write MSR_IA32_TSC(10H) register
as the subsequent TSC value reads are undefined. MSR_IA32_TSC related
accesses should not exit to the hypervisor for such guests.

Accesses to MSR_IA32_TSC needs special handling in the #VC handler for
the guests with Secure TSC enabled. Writes to MSR_IA32_TSC should be
ignored, and reads of MSR_IA32_TSC should return the result of the
RDTSC instruction.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
arch/x86/kernel/sev.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 9468809d02c7..47e2be38a6bc 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -1711,6 +1711,30 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
/* Is it a WRMSR? */
exit_info_1 = (ctxt->insn.opcode.bytes[1] == 0x30) ? 1 : 0;

+ /*
+ * TSC related accesses should not exit to the hypervisor when a
+ * guest is executing with SecureTSC enabled, so special handling
+ * is required for accesses of MSR_IA32_TSC:
+ *
+ * Writes: Writing to MSR_IA32_TSC can cause subsequent reads
+ * of the TSC to return undefined values, so ignore all
+ * writes.
+ * Reads: Reads of MSR_IA32_TSC should return the current TSC
+ * value, use the value returned by RDTSC.
+ */
+ if (regs->cx == MSR_IA32_TSC && cc_platform_has(CC_ATTR_GUEST_SECURE_TSC)) {
+ u64 tsc;
+
+ if (exit_info_1)
+ return ES_OK;
+
+ tsc = rdtsc();
+ regs->ax = UINT_MAX & tsc;
+ regs->dx = UINT_MAX & (tsc >> 32);
+
+ return ES_OK;
+ }
+
ghcb_set_rcx(ghcb, regs->cx);
if (exit_info_1) {
ghcb_set_rax(ghcb, regs->ax);
--
2.34.1

2023-10-30 06:39:34

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 06/14] x86/sev: Cache the secrets page address

Save the secrets page address during snp_init() from the CC blob. Use
secrets_pa instead of calling get_secrets_page() that remaps the CC
blob for getting the secrets page every time.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
arch/x86/kernel/sev.c | 52 +++++++++++++------------------------------
1 file changed, 16 insertions(+), 36 deletions(-)

diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index f8caf0a73052..fd3b822fa9e7 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -72,6 +72,9 @@ static struct ghcb *boot_ghcb __section(".data");
/* Bitmap of SEV features supported by the hypervisor */
static u64 sev_hv_features __ro_after_init;

+/* Secrets page physical address from the CC blob */
+static u64 secrets_pa __ro_after_init;
+
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
struct ghcb ghcb_page;
@@ -598,45 +601,16 @@ void noinstr __sev_es_nmi_complete(void)
__sev_put_ghcb(&state);
}

-static u64 __init get_secrets_page(void)
-{
- u64 pa_data = boot_params.cc_blob_address;
- struct cc_blob_sev_info info;
- void *map;
-
- /*
- * The CC blob contains the address of the secrets page, check if the
- * blob is present.
- */
- if (!pa_data)
- return 0;
-
- map = early_memremap(pa_data, sizeof(info));
- if (!map) {
- pr_err("Unable to locate SNP secrets page: failed to map the Confidential Computing blob.\n");
- return 0;
- }
- memcpy(&info, map, sizeof(info));
- early_memunmap(map, sizeof(info));
-
- /* smoke-test the secrets page passed */
- if (!info.secrets_phys || info.secrets_len != PAGE_SIZE)
- return 0;
-
- return info.secrets_phys;
-}
-
static u64 __init get_snp_jump_table_addr(void)
{
struct snp_secrets_page_layout *layout;
void __iomem *mem;
- u64 pa, addr;
+ u64 addr;

- pa = get_secrets_page();
- if (!pa)
+ if (!secrets_pa)
return 0;

- mem = ioremap_encrypted(pa, PAGE_SIZE);
+ mem = ioremap_encrypted(secrets_pa, PAGE_SIZE);
if (!mem) {
pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
return 0;
@@ -2083,6 +2057,12 @@ static __init struct cc_blob_sev_info *find_cc_blob(struct boot_params *bp)
return cc_info;
}

+static void __init set_secrets_pa(const struct cc_blob_sev_info *cc_info)
+{
+ if (cc_info && cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE)
+ secrets_pa = cc_info->secrets_phys;
+}
+
bool __init snp_init(struct boot_params *bp)
{
struct cc_blob_sev_info *cc_info;
@@ -2094,6 +2074,8 @@ bool __init snp_init(struct boot_params *bp)
if (!cc_info)
return false;

+ set_secrets_pa(cc_info);
+
setup_cpuid_table(cc_info);

/*
@@ -2246,16 +2228,14 @@ static struct platform_device sev_guest_device = {
static int __init snp_init_platform_device(void)
{
struct sev_guest_platform_data data;
- u64 gpa;

if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
return -ENODEV;

- gpa = get_secrets_page();
- if (!gpa)
+ if (!secrets_pa)
return -ENODEV;

- data.secrets_gpa = gpa;
+ data.secrets_gpa = secrets_pa;
if (platform_device_add_data(&sev_guest_device, &data, sizeof(data)))
return -ENODEV;

--
2.34.1

2023-10-30 06:39:57

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 11/14] x86/sev: Prevent RDTSC/RDTSCP interception for Secure TSC enabled guests

The hypervisor should not be intercepting RDTSC/RDTSCP when Secure TSC
is enabled. A #VC exception will be generated if the RDTSC/RDTSCP
instructions are being intercepted. If this should occur and Secure
TSC is enabled, terminate guest execution.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/kernel/sev-shared.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
index ccb0915e84e1..833b0ae38f0b 100644
--- a/arch/x86/kernel/sev-shared.c
+++ b/arch/x86/kernel/sev-shared.c
@@ -991,6 +991,13 @@ static enum es_result vc_handle_rdtsc(struct ghcb *ghcb,
bool rdtscp = (exit_code == SVM_EXIT_RDTSCP);
enum es_result ret;

+ /*
+ * RDTSC and RDTSCP should not be intercepted when Secure TSC is
+ * enabled. Terminate the SNP guest when the interception is enabled.
+ */
+ if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
+ return ES_VMM_ERROR;
+
ret = sev_es_ghcb_hv_call(ghcb, ctxt, exit_code, 0, 0);
if (ret != ES_OK)
return ret;
--
2.34.1

2023-10-30 06:40:04

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 12/14] x86/kvmclock: Skip kvmclock when Secure TSC is available

For AMD SNP guests having Secure TSC enabled, skip using the kvmclock.
The guest kernel will fallback and use Secure TSC based clocksource.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/kernel/kvmclock.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index fb8f52149be9..779e7311fa6f 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -288,7 +288,7 @@ void __init kvmclock_init(void)
{
u8 flags;

- if (!kvm_para_available() || !kvmclock)
+ if (!kvm_para_available() || !kvmclock || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
return;

if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE2)) {
--
2.34.1

2023-10-30 06:40:14

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 08/14] x86/mm: Add generic guest initialization hook

Add generic enc_init guest hook for performing any type of
initialization that is vendor specific.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/include/asm/x86_init.h | 2 ++
arch/x86/kernel/x86_init.c | 2 ++
arch/x86/mm/mem_encrypt.c | 3 +++
3 files changed, 7 insertions(+)

diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index 5240d88db52a..6a08dcd1f3c4 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -148,12 +148,14 @@ struct x86_init_acpi {
* @enc_status_change_finish Notify HV after the encryption status of a range is changed
* @enc_tlb_flush_required Returns true if a TLB flush is needed before changing page encryption status
* @enc_cache_flush_required Returns true if a cache flush is needed before changing page encryption status
+ * @enc_init Prepare and initialize encryption features
*/
struct x86_guest {
bool (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc);
bool (*enc_status_change_finish)(unsigned long vaddr, int npages, bool enc);
bool (*enc_tlb_flush_required)(bool enc);
bool (*enc_cache_flush_required)(void);
+ void (*enc_init)(void);
};

/**
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index a37ebd3b4773..a07985a96ca5 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -136,6 +136,7 @@ static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool
static bool enc_tlb_flush_required_noop(bool enc) { return false; }
static bool enc_cache_flush_required_noop(void) { return false; }
static bool is_private_mmio_noop(u64 addr) {return false; }
+static void enc_init_noop(void) { }

struct x86_platform_ops x86_platform __ro_after_init = {
.calibrate_cpu = native_calibrate_cpu_early,
@@ -158,6 +159,7 @@ struct x86_platform_ops x86_platform __ro_after_init = {
.enc_status_change_finish = enc_status_change_finish_noop,
.enc_tlb_flush_required = enc_tlb_flush_required_noop,
.enc_cache_flush_required = enc_cache_flush_required_noop,
+ .enc_init = enc_init_noop,
},
};

diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
index 9f27e14e185f..01abecc9a774 100644
--- a/arch/x86/mm/mem_encrypt.c
+++ b/arch/x86/mm/mem_encrypt.c
@@ -84,5 +84,8 @@ void __init mem_encrypt_init(void)
/* Call into SWIOTLB to update the SWIOTLB DMA buffers */
swiotlb_update_mem_attributes();

+ if (x86_platform.guest.enc_init)
+ x86_platform.guest.enc_init();
+
print_mem_encrypt_feature_info();
}
--
2.34.1

2023-10-30 06:40:19

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 14/14] x86/sev: Enable Secure TSC for SNP guests

Now that all the required plumbing is done for enabling SNP
Secure TSC feature, add Secure TSC to snp features present list.

The CC_ATTR_GUEST_SECURE_TSC can be used by the guest to query whether
the SNP guest has Secure TSC feature active.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/boot/compressed/sev.c | 3 ++-
arch/x86/mm/mem_encrypt.c | 10 ++++++++--
2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index 80d76aea1f7b..b1a4bab8ecf1 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -375,7 +375,8 @@ static void enforce_vmpl0(void)
* by the guest kernel. As and when a new feature is implemented in the
* guest kernel, a corresponding bit should be added to the mask.
*/
-#define SNP_FEATURES_PRESENT MSR_AMD64_SNP_DEBUG_SWAP
+#define SNP_FEATURES_PRESENT (MSR_AMD64_SNP_DEBUG_SWAP | \
+ MSR_AMD64_SNP_SECURE_TSC)

u64 snp_get_unsupported_features(u64 status)
{
diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
index 01abecc9a774..26608b9f2ca7 100644
--- a/arch/x86/mm/mem_encrypt.c
+++ b/arch/x86/mm/mem_encrypt.c
@@ -69,8 +69,14 @@ static void print_mem_encrypt_feature_info(void)
pr_cont(" SEV-ES");

/* Secure Nested Paging */
- if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
- pr_cont(" SEV-SNP");
+ if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
+ pr_cont(" SEV-SNP\n");
+ pr_cont("SNP Features active: ");
+
+ /* SNP Secure TSC */
+ if (cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
+ pr_cont(" SECURE-TSC");
+ }

pr_cont("\n");
}
--
2.34.1

2023-10-30 06:40:19

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

AMD SNP guests may have Secure TSC feature enabled. Secure TSC as
clocksource is wrongly marked as unstable, mark Secure TSC as
reliable.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/kernel/tsc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 15f97c0abc9d..b0a8546d3703 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
tsc_clocksource_reliable = 1;
}
#endif
- if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
+ if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
tsc_clocksource_reliable = 1;

/*
--
2.34.1

2023-10-30 16:16:49

by Dionna Amalie Glaze

[permalink] [raw]
Subject: Re: [PATCH v5 05/14] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On Sun, Oct 29, 2023 at 11:38 PM Nikunj A Dadhania <[email protected]> wrote:
>
> Drop vmpck and os_area_msg_seqno pointers so that secret page layout
> does not need to be exposed to the sev-guest driver after the rework.
> Instead, add helper APIs to access vmpck and os_area_msg_seqno when
> needed.
>
> Also, change function is_vmpck_empty() to snp_is_vmpck_empty() in
> preparation for moving to sev.c.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> drivers/virt/coco/sev-guest/sev-guest.c | 85 ++++++++++++-------------
> 1 file changed, 42 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
> index 5801dd52ffdf..4dd094c73e2f 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -50,8 +50,7 @@ struct snp_guest_dev {
>
> struct snp_secrets_page_layout *layout;
> struct snp_req_data input;
> - u32 *os_area_msg_seqno;
> - u8 *vmpck;
> + unsigned int vmpck_id;
> };
>
> static u32 vmpck_id;
> @@ -61,14 +60,22 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
> /* Mutex to serialize the shared buffer access and command handling. */
> static DEFINE_MUTEX(snp_cmd_mutex);
>
> -static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
> +static inline u8 *snp_get_vmpck(struct snp_guest_dev *snp_dev)
> {
> - char zero_key[VMPCK_KEY_LEN] = {0};
> + return snp_dev->layout->vmpck0 + snp_dev->vmpck_id * VMPCK_KEY_LEN;
> +}
>
> - if (snp_dev->vmpck)
> - return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
> +static inline u32 *snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
> +{
> + return &snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;
> +}
>
> - return true;
> +static bool snp_is_vmpck_empty(struct snp_guest_dev *snp_dev)
> +{
> + char zero_key[VMPCK_KEY_LEN] = {0};
> + u8 *key = snp_get_vmpck(snp_dev);
> +
> + return !memcmp(key, zero_key, VMPCK_KEY_LEN);
> }
>
> /*
> @@ -90,20 +97,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
> */
> static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
> {
> + u8 *key = snp_get_vmpck(snp_dev);
> +
> dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n",
> - vmpck_id);
> - memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
> - snp_dev->vmpck = NULL;
> + snp_dev->vmpck_id);
> + memzero_explicit(key, VMPCK_KEY_LEN);
> }

We disable the VMPCK because we believe the guest to be under attack,
but this only clears a single key. Shouldn't we clear all VMPCK keys
in the secrets page for good measure? If at VMPCK > 0, most likely the
0..VMPCK-1 keys have been zeroed by whatever was prior in the boot
stack, but still better to be safe.

>
> static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
> {
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
> u64 count;
>
> lockdep_assert_held(&snp_dev->cmd_mutex);
>
> /* Read the current message sequence counter from secrets pages */
> - count = *snp_dev->os_area_msg_seqno;
> + count = *os_area_msg_seqno;
>
> return count + 1;
> }
> @@ -131,11 +140,13 @@ static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>
> static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
> {
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
> +
> /*
> * The counter is also incremented by the PSP, so increment it by 2
> * and save in secrets page.
> */
> - *snp_dev->os_area_msg_seqno += 2;
> + *os_area_msg_seqno += 2;
> }
>
> static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> @@ -145,15 +156,22 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> return container_of(dev, struct snp_guest_dev, misc);
> }
>
> -static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
> +static struct aesgcm_ctx *snp_init_crypto(struct snp_guest_dev *snp_dev)
> {
> struct aesgcm_ctx *ctx;
> + u8 *key;
> +
> + if (snp_is_vmpck_empty(snp_dev)) {
> + pr_err("SNP: vmpck id %d is null\n", snp_dev->vmpck_id);
> + return NULL;
> + }
>
> ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
> if (!ctx)
> return NULL;
>
> - if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
> + key = snp_get_vmpck(snp_dev);
> + if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
> pr_err("SNP: crypto init failed\n");
> kfree(ctx);
> return NULL;
> @@ -586,7 +604,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
> mutex_lock(&snp_dev->cmd_mutex);
>
> /* Check if the VMPCK is not empty */
> - if (is_vmpck_empty(snp_dev)) {
> + if (snp_is_vmpck_empty(snp_dev)) {
> dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
> mutex_unlock(&snp_dev->cmd_mutex);
> return -ENOTTY;
> @@ -656,32 +674,14 @@ static const struct file_operations snp_guest_fops = {
> .unlocked_ioctl = snp_guest_ioctl,
> };
>
> -static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
> +bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
> {
> - u8 *key = NULL;
> + if (WARN_ON(vmpck_id > 3))
> + return false;

The vmpck_id is an int for some reason, so < 0 is also a problem. Can
we not use unsigned int?

>
> - switch (id) {
> - case 0:
> - *seqno = &layout->os_area.msg_seqno_0;
> - key = layout->vmpck0;
> - break;
> - case 1:
> - *seqno = &layout->os_area.msg_seqno_1;
> - key = layout->vmpck1;
> - break;
> - case 2:
> - *seqno = &layout->os_area.msg_seqno_2;
> - key = layout->vmpck2;
> - break;
> - case 3:
> - *seqno = &layout->os_area.msg_seqno_3;
> - key = layout->vmpck3;
> - break;
> - default:
> - break;
> - }
> + dev->vmpck_id = vmpck_id;
>
> - return key;
> + return true;
> }
>
> static int __init sev_guest_probe(struct platform_device *pdev)
> @@ -713,14 +713,14 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> goto e_unmap;
>
> ret = -EINVAL;
> - snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
> - if (!snp_dev->vmpck) {
> + snp_dev->layout = layout;
> + if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
> dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
> goto e_unmap;
> }
>
> /* Verify that VMPCK is not zero. */
> - if (is_vmpck_empty(snp_dev)) {
> + if (snp_is_vmpck_empty(snp_dev)) {
> dev_err(dev, "vmpck id %d is null\n", vmpck_id);
> goto e_unmap;
> }
> @@ -728,7 +728,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> mutex_init(&snp_dev->cmd_mutex);
> platform_set_drvdata(pdev, snp_dev);
> snp_dev->dev = dev;
> - snp_dev->layout = layout;
>
> /* Allocate the shared page used for the request and response message. */
> snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
> @@ -744,7 +743,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> goto e_free_response;
>
> ret = -EIO;
> - snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
> + snp_dev->ctx = snp_init_crypto(snp_dev);
> if (!snp_dev->ctx)
> goto e_free_cert_data;
>
> --
> 2.34.1
>


--
-Dionna Glaze, PhD (she/her)

2023-10-30 16:46:42

by Dionna Amalie Glaze

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On Sun, Oct 29, 2023 at 11:38 PM Nikunj A Dadhania <[email protected]> wrote:
>
> Add support for Secure TSC in SNP enabled guests. Secure TSC allows
> guest to securely use RDTSC/RDTSCP instructions as the parameters
> being used cannot be changed by hypervisor once the guest is launched.
>
> During the boot-up of the secondary cpus, SecureTSC enabled guests
> need to query TSC info from AMD Security Processor. This communication
> channel is encrypted between the AMD Security Processor and the guest,
> the hypervisor is just the conduit to deliver the guest messages to
> the AMD Security Processor. Each message is protected with an
> AEAD (AES-256 GCM). Use minimal AES GCM library to encrypt/decrypt SNP
> Guest messages to communicate with the PSP.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/coco/core.c | 3 ++
> arch/x86/include/asm/sev-guest.h | 18 +++++++
> arch/x86/include/asm/sev.h | 2 +
> arch/x86/include/asm/svm.h | 6 ++-
> arch/x86/kernel/sev.c | 82 ++++++++++++++++++++++++++++++++
> arch/x86/mm/mem_encrypt_amd.c | 6 +++
> include/linux/cc_platform.h | 8 ++++
> 7 files changed, 123 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
> index eeec9986570e..5d5d4d03c543 100644
> --- a/arch/x86/coco/core.c
> +++ b/arch/x86/coco/core.c
> @@ -89,6 +89,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
> case CC_ATTR_GUEST_SEV_SNP:
> return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
>
> + case CC_ATTR_GUEST_SECURE_TSC:
> + return sev_status & MSR_AMD64_SNP_SECURE_TSC;
> +
> default:
> return false;
> }
> diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
> index e6f94208173d..58739173eba9 100644
> --- a/arch/x86/include/asm/sev-guest.h
> +++ b/arch/x86/include/asm/sev-guest.h
> @@ -39,6 +39,8 @@ enum msg_type {
> SNP_MSG_ABSORB_RSP,
> SNP_MSG_VMRK_REQ,
> SNP_MSG_VMRK_RSP,
> + SNP_MSG_TSC_INFO_REQ = 17,
> + SNP_MSG_TSC_INFO_RSP,
>
> SNP_MSG_TYPE_MAX
> };
> @@ -111,6 +113,22 @@ struct snp_guest_req {
> u8 msg_type;
> };
>
> +struct snp_tsc_info_req {
> +#define SNP_TSC_INFO_REQ_SZ 128
> + /* Must be zero filled */
> + u8 rsvd[SNP_TSC_INFO_REQ_SZ];
> +} __packed;
> +
> +struct snp_tsc_info_resp {
> + /* Status of TSC_INFO message */
> + u32 status;
> + u32 rsvd1;
> + u64 tsc_scale;
> + u64 tsc_offset;
> + u32 tsc_factor;
> + u8 rsvd2[100];
> +} __packed;
> +
> int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
> int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
> struct snp_guest_request_ioctl *rio);
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 783150458864..038a5a15d937 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -200,6 +200,7 @@ void __init __noreturn snp_abort(void);
> void snp_accept_memory(phys_addr_t start, phys_addr_t end);
> u64 snp_get_unsupported_features(u64 status);
> u64 sev_get_status(void);
> +void __init snp_secure_tsc_prepare(void);
> #else
> static inline void sev_es_ist_enter(struct pt_regs *regs) { }
> static inline void sev_es_ist_exit(void) { }
> @@ -223,6 +224,7 @@ static inline void snp_abort(void) { }
> static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
> static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
> static inline u64 sev_get_status(void) { return 0; }
> +static inline void __init snp_secure_tsc_prepare(void) { }
> #endif
>
> #endif
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index 3ac0ffc4f3e2..ee35c0488f56 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -414,7 +414,9 @@ struct sev_es_save_area {
> u8 reserved_0x298[80];
> u32 pkru;
> u32 tsc_aux;
> - u8 reserved_0x2f0[24];
> + u64 tsc_scale;
> + u64 tsc_offset;
> + u8 reserved_0x300[8];
> u64 rcx;
> u64 rdx;
> u64 rbx;
> @@ -546,7 +548,7 @@ static inline void __unused_size_checks(void)
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298);
> - BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0);
> + BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
> index fb3b1feb1b84..9468809d02c7 100644
> --- a/arch/x86/kernel/sev.c
> +++ b/arch/x86/kernel/sev.c
> @@ -76,6 +76,10 @@ static u64 sev_hv_features __ro_after_init;
> /* Secrets page physical address from the CC blob */
> static u64 secrets_pa __ro_after_init;
>
> +/* Secure TSC values read using TSC_INFO SNP Guest request */
> +static u64 guest_tsc_scale __ro_after_init;
> +static u64 guest_tsc_offset __ro_after_init;
> +
> /* #VC handler runtime per-CPU data */
> struct sev_es_runtime_data {
> struct ghcb ghcb_page;
> @@ -1393,6 +1397,78 @@ bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
> }
> EXPORT_SYMBOL_GPL(snp_assign_vmpck);
>
> +static struct snp_guest_dev tsc_snp_dev __initdata;
> +
> +static int __init snp_get_tsc_info(void)
> +{
> + static u8 buf[SNP_TSC_INFO_REQ_SZ + AUTHTAG_LEN];
> + struct snp_guest_request_ioctl rio;
> + struct snp_tsc_info_resp tsc_resp;
> + struct snp_tsc_info_req tsc_req;
> + struct snp_guest_req req;
> + int rc, resp_len;
> +
> + /*
> + * The intermediate response buffer is used while decrypting the
> + * response payload. Make sure that it has enough space to cover the
> + * authtag.
> + */
> + resp_len = sizeof(tsc_resp) + AUTHTAG_LEN;
> + if (sizeof(buf) < resp_len)
> + return -EINVAL;
> +
> + memset(&tsc_req, 0, sizeof(tsc_req));
> + memset(&req, 0, sizeof(req));
> + memset(&rio, 0, sizeof(rio));
> + memset(buf, 0, sizeof(buf));
> +
> + if (!snp_assign_vmpck(&tsc_snp_dev, 0))
> + return -EINVAL;
> +

I don't see a requirement for VMPL0 in the API docs. I just see "When
a guest creates its own VMSA, it must query the PSP for information
with the TSC_INFO message to determine the correct values to write
into GUEST_TSC_SCALE and GUEST_TSC_OFFSET". In that case, I don't see
a particular use for this request in Linux. I would expect it either
in the UEFI or in SVSM. Is this code path explicitly for direct boot
to Linux? If so, did I miss that documentation in this patch series?

> + /* Initialize the PSP channel to send snp messages */
> + if (snp_setup_psp_messaging(&tsc_snp_dev))
> + sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +
> + req.msg_version = MSG_HDR_VER;
> + req.msg_type = SNP_MSG_TSC_INFO_REQ;
> + req.vmpck_id = tsc_snp_dev.vmpck_id;
> + req.req_buf = &tsc_req;
> + req.req_sz = sizeof(tsc_req);
> + req.resp_buf = buf;
> + req.resp_sz = resp_len;
> + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
> + rc = snp_send_guest_request(&tsc_snp_dev, &req, &rio);
> + if (rc)
> + goto err_req;
> +
> + memcpy(&tsc_resp, buf, sizeof(tsc_resp));
> + pr_debug("%s: Valid response status %x scale %llx offset %llx factor %x\n",
> + __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset,
> + tsc_resp.tsc_factor);
> +
> + guest_tsc_scale = tsc_resp.tsc_scale;
> + guest_tsc_offset = tsc_resp.tsc_offset;
> +
> +err_req:
> + /* The response buffer contains the sensitive data, explicitly clear it. */
> + memzero_explicit(buf, sizeof(buf));
> + memzero_explicit(&tsc_resp, sizeof(tsc_resp));
> + memzero_explicit(&req, sizeof(req));
> +
> + return rc;
> +}
> +
> +void __init snp_secure_tsc_prepare(void)
> +{
> + if (!cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
> + return;
> +
> + if (snp_get_tsc_info())
> + sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +
> + pr_debug("SecureTSC enabled\n");
> +}
> +
> static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
> {
> struct sev_es_save_area *cur_vmsa, *vmsa;
> @@ -1493,6 +1569,12 @@ static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
> vmsa->vmpl = 0;
> vmsa->sev_features = sev_status >> 2;
>
> + /* Setting Secure TSC parameters */
> + if (cc_platform_has(CC_ATTR_GUEST_SECURE_TSC)) {
> + vmsa->tsc_scale = guest_tsc_scale;
> + vmsa->tsc_offset = guest_tsc_offset;
> + }
> +
> /* Switch the page over to a VMSA page now that it is initialized */
> ret = snp_set_vmsa(vmsa, true);
> if (ret) {
> diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
> index 6faea41e99b6..9935fc506e99 100644
> --- a/arch/x86/mm/mem_encrypt_amd.c
> +++ b/arch/x86/mm/mem_encrypt_amd.c
> @@ -215,6 +215,11 @@ void __init sme_map_bootdata(char *real_mode_data)
> __sme_early_map_unmap_mem(__va(cmdline_paddr), COMMAND_LINE_SIZE, true);
> }
>
> +void __init amd_enc_init(void)
> +{
> + snp_secure_tsc_prepare();
> +}
> +
> void __init sev_setup_arch(void)
> {
> phys_addr_t total_mem = memblock_phys_mem_size();
> @@ -502,6 +507,7 @@ void __init sme_early_init(void)
> x86_platform.guest.enc_status_change_finish = amd_enc_status_change_finish;
> x86_platform.guest.enc_tlb_flush_required = amd_enc_tlb_flush_required;
> x86_platform.guest.enc_cache_flush_required = amd_enc_cache_flush_required;
> + x86_platform.guest.enc_init = amd_enc_init;
>
> /*
> * AMD-SEV-ES intercepts the RDMSR to read the X2APIC ID in the
> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
> index cb0d6cd1c12f..e081ca4d5da2 100644
> --- a/include/linux/cc_platform.h
> +++ b/include/linux/cc_platform.h
> @@ -90,6 +90,14 @@ enum cc_attr {
> * Examples include TDX Guest.
> */
> CC_ATTR_HOTPLUG_DISABLED,
> +
> + /**
> + * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
> + *
> + * The platform/OS is running as a guest/virtual machine and actively
> + * using AMD SEV-SNP Secure TSC feature.
> + */
> + CC_ATTR_GUEST_SECURE_TSC,
> };
>
> #ifdef CONFIG_ARCH_HAS_CC_PLATFORM
> --
> 2.34.1
>


--
-Dionna Glaze, PhD (she/her)

2023-10-30 17:12:42

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 05/14] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On 10/30/23 11:16, Dionna Amalie Glaze wrote:
> On Sun, Oct 29, 2023 at 11:38 PM Nikunj A Dadhania <[email protected]> wrote:
>>
>> Drop vmpck and os_area_msg_seqno pointers so that secret page layout
>> does not need to be exposed to the sev-guest driver after the rework.
>> Instead, add helper APIs to access vmpck and os_area_msg_seqno when
>> needed.
>>
>> Also, change function is_vmpck_empty() to snp_is_vmpck_empty() in
>> preparation for moving to sev.c.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---
>> drivers/virt/coco/sev-guest/sev-guest.c | 85 ++++++++++++-------------
>> 1 file changed, 42 insertions(+), 43 deletions(-)
>>
>> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
>> index 5801dd52ffdf..4dd094c73e2f 100644
>> --- a/drivers/virt/coco/sev-guest/sev-guest.c
>> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
>> @@ -50,8 +50,7 @@ struct snp_guest_dev {
>>
>> struct snp_secrets_page_layout *layout;
>> struct snp_req_data input;
>> - u32 *os_area_msg_seqno;
>> - u8 *vmpck;
>> + unsigned int vmpck_id;
>> };
>>
>> static u32 vmpck_id;
>> @@ -61,14 +60,22 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
>> /* Mutex to serialize the shared buffer access and command handling. */
>> static DEFINE_MUTEX(snp_cmd_mutex);
>>
>> -static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
>> +static inline u8 *snp_get_vmpck(struct snp_guest_dev *snp_dev)
>> {
>> - char zero_key[VMPCK_KEY_LEN] = {0};
>> + return snp_dev->layout->vmpck0 + snp_dev->vmpck_id * VMPCK_KEY_LEN;
>> +}
>>
>> - if (snp_dev->vmpck)
>> - return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
>> +static inline u32 *snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
>> +{
>> + return &snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;
>> +}
>>
>> - return true;
>> +static bool snp_is_vmpck_empty(struct snp_guest_dev *snp_dev)
>> +{
>> + char zero_key[VMPCK_KEY_LEN] = {0};
>> + u8 *key = snp_get_vmpck(snp_dev);
>> +
>> + return !memcmp(key, zero_key, VMPCK_KEY_LEN);
>> }
>>
>> /*
>> @@ -90,20 +97,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
>> */
>> static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
>> {
>> + u8 *key = snp_get_vmpck(snp_dev);
>> +
>> dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n",
>> - vmpck_id);
>> - memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
>> - snp_dev->vmpck = NULL;
>> + snp_dev->vmpck_id);
>> + memzero_explicit(key, VMPCK_KEY_LEN);
>> }
>
> We disable the VMPCK because we believe the guest to be under attack,
> but this only clears a single key. Shouldn't we clear all VMPCK keys
> in the secrets page for good measure? If at VMPCK > 0, most likely the
> 0..VMPCK-1 keys have been zeroed by whatever was prior in the boot
> stack, but still better to be safe.

Doing that would be a separate patch series and isn't appropriate here.

Thanks,
Tom

>
>>
>> static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>> {
>> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
>> u64 count;
>>
>> lockdep_assert_held(&snp_dev->cmd_mutex);
>>
>> /* Read the current message sequence counter from secrets pages */
>> - count = *snp_dev->os_area_msg_seqno;
>> + count = *os_area_msg_seqno;
>>
>> return count + 1;
>> }
>> @@ -131,11 +140,13 @@ static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>>
>> static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
>> {
>> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
>> +
>> /*
>> * The counter is also incremented by the PSP, so increment it by 2
>> * and save in secrets page.
>> */
>> - *snp_dev->os_area_msg_seqno += 2;
>> + *os_area_msg_seqno += 2;
>> }
>>
>> static inline struct snp_guest_dev *to_snp_dev(struct file *file)
>> @@ -145,15 +156,22 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
>> return container_of(dev, struct snp_guest_dev, misc);
>> }
>>
>> -static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
>> +static struct aesgcm_ctx *snp_init_crypto(struct snp_guest_dev *snp_dev)
>> {
>> struct aesgcm_ctx *ctx;
>> + u8 *key;
>> +
>> + if (snp_is_vmpck_empty(snp_dev)) {
>> + pr_err("SNP: vmpck id %d is null\n", snp_dev->vmpck_id);
>> + return NULL;
>> + }
>>
>> ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
>> if (!ctx)
>> return NULL;
>>
>> - if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
>> + key = snp_get_vmpck(snp_dev);
>> + if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
>> pr_err("SNP: crypto init failed\n");
>> kfree(ctx);
>> return NULL;
>> @@ -586,7 +604,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
>> mutex_lock(&snp_dev->cmd_mutex);
>>
>> /* Check if the VMPCK is not empty */
>> - if (is_vmpck_empty(snp_dev)) {
>> + if (snp_is_vmpck_empty(snp_dev)) {
>> dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
>> mutex_unlock(&snp_dev->cmd_mutex);
>> return -ENOTTY;
>> @@ -656,32 +674,14 @@ static const struct file_operations snp_guest_fops = {
>> .unlocked_ioctl = snp_guest_ioctl,
>> };
>>
>> -static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
>> +bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
>> {
>> - u8 *key = NULL;
>> + if (WARN_ON(vmpck_id > 3))
>> + return false;
>
> The vmpck_id is an int for some reason, so < 0 is also a problem. Can
> we not use unsigned int?
>
>>
>> - switch (id) {
>> - case 0:
>> - *seqno = &layout->os_area.msg_seqno_0;
>> - key = layout->vmpck0;
>> - break;
>> - case 1:
>> - *seqno = &layout->os_area.msg_seqno_1;
>> - key = layout->vmpck1;
>> - break;
>> - case 2:
>> - *seqno = &layout->os_area.msg_seqno_2;
>> - key = layout->vmpck2;
>> - break;
>> - case 3:
>> - *seqno = &layout->os_area.msg_seqno_3;
>> - key = layout->vmpck3;
>> - break;
>> - default:
>> - break;
>> - }
>> + dev->vmpck_id = vmpck_id;
>>
>> - return key;
>> + return true;
>> }
>>
>> static int __init sev_guest_probe(struct platform_device *pdev)
>> @@ -713,14 +713,14 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>> goto e_unmap;
>>
>> ret = -EINVAL;
>> - snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
>> - if (!snp_dev->vmpck) {
>> + snp_dev->layout = layout;
>> + if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
>> dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
>> goto e_unmap;
>> }
>>
>> /* Verify that VMPCK is not zero. */
>> - if (is_vmpck_empty(snp_dev)) {
>> + if (snp_is_vmpck_empty(snp_dev)) {
>> dev_err(dev, "vmpck id %d is null\n", vmpck_id);
>> goto e_unmap;
>> }
>> @@ -728,7 +728,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>> mutex_init(&snp_dev->cmd_mutex);
>> platform_set_drvdata(pdev, snp_dev);
>> snp_dev->dev = dev;
>> - snp_dev->layout = layout;
>>
>> /* Allocate the shared page used for the request and response message. */
>> snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
>> @@ -744,7 +743,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>> goto e_free_response;
>>
>> ret = -EIO;
>> - snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
>> + snp_dev->ctx = snp_init_crypto(snp_dev);
>> if (!snp_dev->ctx)
>> goto e_free_cert_data;
>>
>> --
>> 2.34.1
>>
>
>

2023-10-30 17:19:03

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On 10/29/23 23:36, Nikunj A Dadhania wrote:
...
> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
> index 15f97c0abc9d..b0a8546d3703 100644
> --- a/arch/x86/kernel/tsc.c
> +++ b/arch/x86/kernel/tsc.c
> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
> tsc_clocksource_reliable = 1;
> }
> #endif
> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
> tsc_clocksource_reliable = 1;

Why can't you just set X86_FEATURE_TSC_RELIABLE?

2023-10-30 17:23:31

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v5 08/14] x86/mm: Add generic guest initialization hook

On 10/29/23 23:36, Nikunj A Dadhania wrote:
> diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
> index a37ebd3b4773..a07985a96ca5 100644
> --- a/arch/x86/kernel/x86_init.c
> +++ b/arch/x86/kernel/x86_init.c
> @@ -136,6 +136,7 @@ static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool
> static bool enc_tlb_flush_required_noop(bool enc) { return false; }
> static bool enc_cache_flush_required_noop(void) { return false; }
> static bool is_private_mmio_noop(u64 addr) {return false; }
> +static void enc_init_noop(void) { }
>
> struct x86_platform_ops x86_platform __ro_after_init = {
> .calibrate_cpu = native_calibrate_cpu_early,
> @@ -158,6 +159,7 @@ struct x86_platform_ops x86_platform __ro_after_init = {
> .enc_status_change_finish = enc_status_change_finish_noop,
> .enc_tlb_flush_required = enc_tlb_flush_required_noop,
> .enc_cache_flush_required = enc_cache_flush_required_noop,
> + .enc_init = enc_init_noop,
> },
> };
>
> diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
> index 9f27e14e185f..01abecc9a774 100644
> --- a/arch/x86/mm/mem_encrypt.c
> +++ b/arch/x86/mm/mem_encrypt.c
> @@ -84,5 +84,8 @@ void __init mem_encrypt_init(void)
> /* Call into SWIOTLB to update the SWIOTLB DMA buffers */
> swiotlb_update_mem_attributes();
>
> + if (x86_platform.guest.enc_init)
> + x86_platform.guest.enc_init();
> +
> print_mem_encrypt_feature_info();
> }

How does '.enc_init' ever get set to NULL? Isn't the point of having
and using a 'noop' function so that you don't have to do NULL checks?

2023-10-30 17:52:11

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 01/14] virt: sev-guest: Use AES GCM crypto library

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> The sev-guest driver encryption code uses Crypto API for SNP guest
> messaging to interact with AMD Security processor. For enabling SecureTSC,
> SEV-SNP guests need to send a TSC_INFO request guest message before the
> smpboot phase starts. Details from the TSC_INFO response will be used to
> program the VMSA before the secondary CPUs are brought up. The Crypto API
> is not available this early in the boot phase.
>
> In preparation of moving the encryption code out of sev-guest driver to
> support SecureTSC and make reviewing the diff easier, start using AES GCM
> library implementation instead of Crypto API.
>
> CC: Ard Biesheuvel <[email protected]>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> Reviewed-by: Tom Lendacky <[email protected]>

I just a few nit comments that might be nice to cover if you have to do a
v6...

> ---
> drivers/virt/coco/sev-guest/Kconfig | 4 +-
> drivers/virt/coco/sev-guest/sev-guest.c | 163 ++++++------------------
> drivers/virt/coco/sev-guest/sev-guest.h | 3 +
> 3 files changed, 44 insertions(+), 126 deletions(-)
>
> diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig
> index da2d7ca531f0..bcc760bfb468 100644
> --- a/drivers/virt/coco/sev-guest/Kconfig
> +++ b/drivers/virt/coco/sev-guest/Kconfig
> @@ -2,9 +2,7 @@ config SEV_GUEST
> tristate "AMD SEV Guest driver"
> default m
> depends on AMD_MEM_ENCRYPT
> - select CRYPTO
> - select CRYPTO_AEAD2
> - select CRYPTO_GCM
> + select CRYPTO_LIB_AESGCM
> help
> SEV-SNP firmware provides the guest a mechanism to communicate with
> the PSP without risk from a malicious hypervisor who wishes to read,
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
> index 97dbe715e96a..68044c436866 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -16,8 +16,7 @@
> #include <linux/miscdevice.h>
> #include <linux/set_memory.h>
> #include <linux/fs.h>
> -#include <crypto/aead.h>
> -#include <linux/scatterlist.h>
> +#include <crypto/gcm.h>
> #include <linux/psp-sev.h>
> #include <uapi/linux/sev-guest.h>
> #include <uapi/linux/psp-sev.h>
> @@ -28,24 +27,16 @@
> #include "sev-guest.h"
>
> #define DEVICE_NAME "sev-guest"
> -#define AAD_LEN 48
> -#define MSG_HDR_VER 1
>
> #define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
> #define SNP_REQ_RETRY_DELAY (2*HZ)
>
> -struct snp_guest_crypto {
> - struct crypto_aead *tfm;
> - u8 *iv, *authtag;
> - int iv_len, a_len;
> -};
> -
> struct snp_guest_dev {
> struct device *dev;
> struct miscdevice misc;
>
> void *certs_data;
> - struct snp_guest_crypto *crypto;
> + struct aesgcm_ctx *ctx;
> /* request and response are in unencrypted memory */
> struct snp_guest_msg *request, *response;
>
> @@ -152,132 +143,59 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> return container_of(dev, struct snp_guest_dev, misc);
> }
>
> -static struct snp_guest_crypto *init_crypto(struct snp_guest_dev *snp_dev, u8 *key, size_t keylen)
> +static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
> {
> - struct snp_guest_crypto *crypto;
> + struct aesgcm_ctx *ctx;
>
> - crypto = kzalloc(sizeof(*crypto), GFP_KERNEL_ACCOUNT);
> - if (!crypto)
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
> + if (!ctx)
> return NULL;
>
> - crypto->tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
> - if (IS_ERR(crypto->tfm))
> - goto e_free;
> -
> - if (crypto_aead_setkey(crypto->tfm, key, keylen))
> - goto e_free_crypto;
> -
> - crypto->iv_len = crypto_aead_ivsize(crypto->tfm);
> - crypto->iv = kmalloc(crypto->iv_len, GFP_KERNEL_ACCOUNT);
> - if (!crypto->iv)
> - goto e_free_crypto;
> -
> - if (crypto_aead_authsize(crypto->tfm) > MAX_AUTHTAG_LEN) {
> - if (crypto_aead_setauthsize(crypto->tfm, MAX_AUTHTAG_LEN)) {
> - dev_err(snp_dev->dev, "failed to set authsize to %d\n", MAX_AUTHTAG_LEN);
> - goto e_free_iv;
> - }
> + if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
> + pr_err("SNP: crypto init failed\n");
> + kfree(ctx);
> + return NULL;
> }
>
> - crypto->a_len = crypto_aead_authsize(crypto->tfm);
> - crypto->authtag = kmalloc(crypto->a_len, GFP_KERNEL_ACCOUNT);
> - if (!crypto->authtag)
> - goto e_free_iv;
> -
> - return crypto;
> -
> -e_free_iv:
> - kfree(crypto->iv);
> -e_free_crypto:
> - crypto_free_aead(crypto->tfm);
> -e_free:
> - kfree(crypto);
> -
> - return NULL;
> -}
> -
> -static void deinit_crypto(struct snp_guest_crypto *crypto)
> -{
> - crypto_free_aead(crypto->tfm);
> - kfree(crypto->iv);
> - kfree(crypto->authtag);
> - kfree(crypto);
> -}
> -
> -static int enc_dec_message(struct snp_guest_crypto *crypto, struct snp_guest_msg *msg,
> - u8 *src_buf, u8 *dst_buf, size_t len, bool enc)
> -{
> - struct snp_guest_msg_hdr *hdr = &msg->hdr;
> - struct scatterlist src[3], dst[3];
> - DECLARE_CRYPTO_WAIT(wait);
> - struct aead_request *req;
> - int ret;
> -
> - req = aead_request_alloc(crypto->tfm, GFP_KERNEL);
> - if (!req)
> - return -ENOMEM;
> -
> - /*
> - * AEAD memory operations:
> - * +------ AAD -------+------- DATA -----+---- AUTHTAG----+
> - * | msg header | plaintext | hdr->authtag |
> - * | bytes 30h - 5Fh | or | |
> - * | | cipher | |
> - * +------------------+------------------+----------------+
> - */
> - sg_init_table(src, 3);
> - sg_set_buf(&src[0], &hdr->algo, AAD_LEN);
> - sg_set_buf(&src[1], src_buf, hdr->msg_sz);
> - sg_set_buf(&src[2], hdr->authtag, crypto->a_len);
> -
> - sg_init_table(dst, 3);
> - sg_set_buf(&dst[0], &hdr->algo, AAD_LEN);
> - sg_set_buf(&dst[1], dst_buf, hdr->msg_sz);
> - sg_set_buf(&dst[2], hdr->authtag, crypto->a_len);
> -
> - aead_request_set_ad(req, AAD_LEN);
> - aead_request_set_tfm(req, crypto->tfm);
> - aead_request_set_callback(req, 0, crypto_req_done, &wait);
> -
> - aead_request_set_crypt(req, src, dst, len, crypto->iv);
> - ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait);
> -
> - aead_request_free(req);
> - return ret;
> + return ctx;
> }
>
> -static int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
> +static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
> void *plaintext, size_t len)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_guest_msg_hdr *hdr = &msg->hdr;
> + u8 iv[GCM_AES_IV_SIZE] = {};
>
> - memset(crypto->iv, 0, crypto->iv_len);
> - memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> + if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
> + return -EBADMSG;
>
> - return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true);
> + memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> + aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
> + iv, hdr->authtag);
> + return 0;
> }

__enc_payload() is pretty small now and can probably just be part of the
only function that calls it, enc_payload().

>
> -static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
> +static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
> void *plaintext, size_t len)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_guest_msg_hdr *hdr = &msg->hdr;
> + u8 iv[GCM_AES_IV_SIZE] = {};
>
> - /* Build IV with response buffer sequence number */
> - memset(crypto->iv, 0, crypto->iv_len);
> - memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> -
> - return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false);
> + memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> + if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
> + AAD_LEN, iv, hdr->authtag))
> + return 0;
> + else
> + return -EBADMSG;

This would look cleaner / read easier to me to have as:

if (!aesgcm_decrypt(...))
return -EBADMSG;

return 0;

But just my opinion.

And ditto here on the size now, can probably just be part of
verify_and_dec_payload() now.

Thanks,
Tom

> }
>
> static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_guest_msg *resp = &snp_dev->secret_response;
> struct snp_guest_msg *req = &snp_dev->secret_request;
> struct snp_guest_msg_hdr *req_hdr = &req->hdr;
> struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
> + struct aesgcm_ctx *ctx = snp_dev->ctx;
>
> dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n",
> resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz);
> @@ -298,11 +216,11 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
> * If the message size is greater than our buffer length then return
> * an error.
> */
> - if (unlikely((resp_hdr->msg_sz + crypto->a_len) > sz))
> + if (unlikely((resp_hdr->msg_sz + ctx->authsize) > sz))
> return -EBADMSG;
>
> /* Decrypt the payload */
> - return dec_payload(snp_dev, resp, payload, resp_hdr->msg_sz + crypto->a_len);
> + return dec_payload(ctx, resp, payload, resp_hdr->msg_sz);
> }
>
> static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
> @@ -329,7 +247,7 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
> dev_dbg(snp_dev->dev, "request [seqno %lld type %d version %d sz %d]\n",
> hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
>
> - return __enc_payload(snp_dev, req, payload, sz);
> + return __enc_payload(snp_dev->ctx, req, payload, sz);
> }
>
> static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> @@ -472,7 +390,6 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>
> static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_report_resp *resp;
> struct snp_report_req req;
> int rc, resp_len;
> @@ -490,7 +407,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
> * response payload. Make sure that it has enough space to cover the
> * authtag.
> */
> - resp_len = sizeof(resp->data) + crypto->a_len;
> + resp_len = sizeof(resp->data) + snp_dev->ctx->authsize;
> resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
> if (!resp)
> return -ENOMEM;
> @@ -511,7 +428,6 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
>
> static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_derived_key_resp resp = {0};
> struct snp_derived_key_req req;
> int rc, resp_len;
> @@ -528,7 +444,7 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
> * response payload. Make sure that it has enough space to cover the
> * authtag.
> */
> - resp_len = sizeof(resp.data) + crypto->a_len;
> + resp_len = sizeof(resp.data) + snp_dev->ctx->authsize;
> if (sizeof(buf) < resp_len)
> return -ENOMEM;
>
> @@ -552,7 +468,6 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
>
> static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> - struct snp_guest_crypto *crypto = snp_dev->crypto;
> struct snp_ext_report_req req;
> struct snp_report_resp *resp;
> int ret, npages = 0, resp_len;
> @@ -590,7 +505,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
> * response payload. Make sure that it has enough space to cover the
> * authtag.
> */
> - resp_len = sizeof(resp->data) + crypto->a_len;
> + resp_len = sizeof(resp->data) + snp_dev->ctx->authsize;
> resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
> if (!resp)
> return -ENOMEM;
> @@ -802,8 +717,8 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> goto e_free_response;
>
> ret = -EIO;
> - snp_dev->crypto = init_crypto(snp_dev, snp_dev->vmpck, VMPCK_KEY_LEN);
> - if (!snp_dev->crypto)
> + snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
> + if (!snp_dev->ctx)
> goto e_free_cert_data;
>
> misc = &snp_dev->misc;
> @@ -818,11 +733,13 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>
> ret = misc_register(misc);
> if (ret)
> - goto e_free_cert_data;
> + goto e_free_ctx;
>
> dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id);
> return 0;
>
> +e_free_ctx:
> + kfree(snp_dev->ctx);
> e_free_cert_data:
> free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
> e_free_response:
> @@ -841,7 +758,7 @@ static int __exit sev_guest_remove(struct platform_device *pdev)
> free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
> free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
> free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
> - deinit_crypto(snp_dev->crypto);
> + kfree(snp_dev->ctx);
> misc_deregister(&snp_dev->misc);
>
> return 0;
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/drivers/virt/coco/sev-guest/sev-guest.h
> index 21bda26fdb95..ceb798a404d6 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.h
> +++ b/drivers/virt/coco/sev-guest/sev-guest.h
> @@ -13,6 +13,9 @@
> #include <linux/types.h>
>
> #define MAX_AUTHTAG_LEN 32
> +#define AUTHTAG_LEN 16
> +#define AAD_LEN 48
> +#define MSG_HDR_VER 1
>
> /* See SNP spec SNP_GUEST_REQUEST section for the structure */
> enum msg_type {

2023-10-30 18:16:44

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 04/14] virt: sev-guest: Add SNP guest request structure

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> Add a snp_guest_req structure to simplify the function arguments. The
> structure will be used to call the SNP Guest message request API
> instead of passing a long list of parameters.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>

Some minor comments below.

> ---
> .../x86/include/asm}/sev-guest.h | 11 ++
> arch/x86/include/asm/sev.h | 8 --
> arch/x86/kernel/sev.c | 15 ++-
> drivers/virt/coco/sev-guest/sev-guest.c | 103 +++++++++++-------
> 4 files changed, 84 insertions(+), 53 deletions(-)
> rename {drivers/virt/coco/sev-guest => arch/x86/include/asm}/sev-guest.h (80%)
>
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/arch/x86/include/asm/sev-guest.h
> similarity index 80%
> rename from drivers/virt/coco/sev-guest/sev-guest.h
> rename to arch/x86/include/asm/sev-guest.h
> index ceb798a404d6..22ef97b55069 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.h
> +++ b/arch/x86/include/asm/sev-guest.h
> @@ -63,4 +63,15 @@ struct snp_guest_msg {
> u8 payload[4000];
> } __packed;
>
> +struct snp_guest_req {
> + void *req_buf, *resp_buf, *data;
> + size_t req_sz, resp_sz, *data_npages;

For structures like this, I find it easier to group things and keep it one
item per line, e.g.:

void *req_buf;
size_t req_sz;

void *resp_buf;
size_t resp_sz;

void *data;
size_t *data_npages;

And does data_npages have to be a pointer? It looks like you can just use
this variable as the address on the GHCB call and then set it
appropriately without all the indirection, right?

> + u64 exit_code;
> + unsigned int vmpck_id;
> + u8 msg_version;
> + u8 msg_type;
> +};
> +
> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
> + struct snp_guest_request_ioctl *rio);
> #endif /* __VIRT_SEVGUEST_H__ */
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 5b4a1ce3d368..78465a8c7dc6 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -97,8 +97,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
> struct snp_req_data {
> unsigned long req_gpa;
> unsigned long resp_gpa;
> - unsigned long data_gpa;
> - unsigned int data_npages;
> };
>
> struct sev_guest_platform_data {
> @@ -209,7 +207,6 @@ void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
> void snp_set_wakeup_secondary_cpu(void);
> bool snp_init(struct boot_params *bp);
> void __init __noreturn snp_abort(void);
> -int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
> void snp_accept_memory(phys_addr_t start, phys_addr_t end);
> u64 snp_get_unsupported_features(u64 status);
> u64 sev_get_status(void);
> @@ -233,11 +230,6 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npa
> static inline void snp_set_wakeup_secondary_cpu(void) { }
> static inline bool snp_init(struct boot_params *bp) { return false; }
> static inline void snp_abort(void) { }
> -static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
> -{
> - return -ENOTTY;
> -}
> -

May want to mention in the commit message why this can be deleted vs changed.

> static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
> static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
> static inline u64 sev_get_status(void) { return 0; }
> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
> index 6395bfd87b68..f8caf0a73052 100644
> --- a/arch/x86/kernel/sev.c
> +++ b/arch/x86/kernel/sev.c
> @@ -28,6 +28,7 @@
> #include <asm/cpu_entry_area.h>
> #include <asm/stacktrace.h>
> #include <asm/sev.h>
> +#include <asm/sev-guest.h>
> #include <asm/insn-eval.h>
> #include <asm/fpu/xcr.h>
> #include <asm/processor.h>
> @@ -2167,15 +2168,21 @@ static int __init init_sev_config(char *str)
> }
> __setup("sev=", init_sev_config);
>
> -int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
> + struct snp_guest_request_ioctl *rio)
> {
> struct ghcb_state state;
> struct es_em_ctxt ctxt;
> unsigned long flags;
> struct ghcb *ghcb;
> + u64 exit_code;
> int ret;
>
> rio->exitinfo2 = SEV_RET_NO_FW_CALL;
> + if (!req)
> + return -EINVAL;
> +
> + exit_code = req->exit_code;
>
> /*
> * __sev_get_ghcb() needs to run with IRQs disabled because it is using
> @@ -2192,8 +2199,8 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
> vc_ghcb_invalidate(ghcb);
>
> if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
> - ghcb_set_rax(ghcb, input->data_gpa);
> - ghcb_set_rbx(ghcb, input->data_npages);
> + ghcb_set_rax(ghcb, __pa(req->data));
> + ghcb_set_rbx(ghcb, *req->data_npages);
> }
>
> ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
> @@ -2212,7 +2219,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
> case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
> /* Number of expected pages are returned in RBX */
> if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
> - input->data_npages = ghcb_get_rbx(ghcb);
> + *req->data_npages = ghcb_get_rbx(ghcb);
> ret = -ENOSPC;
> break;
> }
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
> index 49bafd2e9f42..5801dd52ffdf 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -23,8 +23,7 @@
>
> #include <asm/svm.h>
> #include <asm/sev.h>
> -
> -#include "sev-guest.h"
> +#include <asm/sev-guest.h>
>
> #define DEVICE_NAME "sev-guest"
>
> @@ -192,7 +191,7 @@ static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
> return -EBADMSG;
> }
>
> -static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
> +static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req)
> {
> struct snp_guest_msg *resp = &snp_dev->secret_response;
> struct snp_guest_msg *req = &snp_dev->secret_request;
> @@ -220,29 +219,28 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
> * If the message size is greater than our buffer length then return
> * an error.
> */
> - if (unlikely((resp_hdr->msg_sz + ctx->authsize) > sz))
> + if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
> return -EBADMSG;
>
> /* Decrypt the payload */
> - return dec_payload(ctx, resp, payload, resp_hdr->msg_sz);
> + return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
> }
>
> -static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
> - void *payload, size_t sz)
> +static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
> {
> - struct snp_guest_msg *req = &snp_dev->secret_request;
> - struct snp_guest_msg_hdr *hdr = &req->hdr;
> + struct snp_guest_msg *msg = &snp_dev->secret_request;
> + struct snp_guest_msg_hdr *hdr = &msg->hdr;
>
> - memset(req, 0, sizeof(*req));
> + memset(msg, 0, sizeof(*msg));
>
> hdr->algo = SNP_AEAD_AES_256_GCM;
> hdr->hdr_version = MSG_HDR_VER;
> hdr->hdr_sz = sizeof(*hdr);
> - hdr->msg_type = type;
> - hdr->msg_version = version;
> + hdr->msg_type = req->msg_type;
> + hdr->msg_version = req->msg_version;
> hdr->msg_seqno = seqno;
> - hdr->msg_vmpck = vmpck_id;
> - hdr->msg_sz = sz;
> + hdr->msg_vmpck = req->vmpck_id;
> + hdr->msg_sz = req->req_sz;
>
> /* Verify the sequence number is non-zero */
> if (!hdr->msg_seqno)
> @@ -251,10 +249,10 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
> pr_debug("request [seqno %lld type %d version %d sz %d]\n",
> hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
>
> - return __enc_payload(snp_dev->ctx, req, payload, sz);
> + return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
> }
>
> -static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> +static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
> struct snp_guest_request_ioctl *rio)
> {
> unsigned long req_start = jiffies;
> @@ -269,7 +267,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> * sequence number must be incremented or the VMPCK must be deleted to
> * prevent reuse of the IV.
> */
> - rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
> + rc = snp_issue_guest_request(req, &snp_dev->input, rio);
> switch (rc) {
> case -ENOSPC:
> /*
> @@ -279,8 +277,8 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> * order to increment the sequence number and thus avoid
> * IV reuse.
> */
> - override_npages = snp_dev->input.data_npages;
> - exit_code = SVM_VMGEXIT_GUEST_REQUEST;
> + override_npages = *req->data_npages;
> + req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
>
> /*
> * Override the error to inform callers the given extended
> @@ -335,15 +333,13 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> }
>
> if (override_npages)
> - snp_dev->input.data_npages = override_npages;
> + *req->data_npages = override_npages;
>
> return rc;
> }
>
> -static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> - struct snp_guest_request_ioctl *rio, u8 type,
> - void *req_buf, size_t req_sz, void *resp_buf,
> - u32 resp_sz)
> +static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
> + struct snp_guest_request_ioctl *rio)
> {
> u64 seqno;
> int rc;
> @@ -357,7 +353,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
>
> /* Encrypt the userspace provided payload in snp_dev->secret_request. */
> - rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
> + rc = enc_payload(snp_dev, seqno, req);
> if (rc)
> return rc;
>
> @@ -368,7 +364,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> memcpy(snp_dev->request, &snp_dev->secret_request,
> sizeof(snp_dev->secret_request));
>
> - rc = __handle_guest_request(snp_dev, exit_code, rio);
> + rc = __handle_guest_request(snp_dev, req, rio);
> if (rc) {
> if (rc == -EIO &&
> rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
> @@ -377,12 +373,11 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
> dev_alert(snp_dev->dev,
> "Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
> rc, rio->exitinfo2);
> -
> snp_disable_vmpck(snp_dev);
> return rc;
> }
>
> - rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz);
> + rc = verify_and_dec_payload(snp_dev, req);
> if (rc) {
> dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc);
> snp_disable_vmpck(snp_dev);
> @@ -394,6 +389,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>
> static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> + struct snp_guest_req guest_req = {0};
> struct snp_report_resp *resp;
> struct snp_report_req req;
> int rc, resp_len;
> @@ -416,9 +412,16 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
> if (!resp)
> return -ENOMEM;
>
> - rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
> - SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
> - resp_len);
> + guest_req.msg_version = arg->msg_version;
> + guest_req.msg_type = SNP_MSG_REPORT_REQ;
> + guest_req.vmpck_id = vmpck_id;
> + guest_req.req_buf = &req;
> + guest_req.req_sz = sizeof(req);
> + guest_req.resp_buf = resp->data;
> + guest_req.resp_sz = resp_len;
> + guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
> +
> + rc = snp_send_guest_request(snp_dev, &guest_req, arg);
> if (rc)
> goto e_free;
>
> @@ -433,6 +436,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
> static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> struct snp_derived_key_resp resp = {0};
> + struct snp_guest_req guest_req = {0};
> struct snp_derived_key_req req;
> int rc, resp_len;
> /* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
> @@ -455,8 +459,16 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
> if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
> return -EFAULT;
>
> - rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
> - SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len);
> + guest_req.msg_version = arg->msg_version;
> + guest_req.msg_type = SNP_MSG_KEY_REQ;
> + guest_req.vmpck_id = vmpck_id;
> + guest_req.req_buf = &req;
> + guest_req.req_sz = sizeof(req);
> + guest_req.resp_buf = buf;
> + guest_req.resp_sz = resp_len;
> + guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
> +
> + rc = snp_send_guest_request(snp_dev, &guest_req, arg);
> if (rc)
> return rc;
>
> @@ -472,9 +484,11 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
>
> static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
> {
> + struct snp_guest_req guest_req = {0};
> struct snp_ext_report_req req;
> struct snp_report_resp *resp;
> - int ret, npages = 0, resp_len;
> + int ret, resp_len;
> + size_t npages = 0;

This becomes unnecessary if you don't define data_npages as a pointer in
the snp_guest_req structure.

Thanks,
Tom

>
> lockdep_assert_held(&snp_dev->cmd_mutex);
>
> @@ -514,14 +528,22 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
> if (!resp)
> return -ENOMEM;
>
> - snp_dev->input.data_npages = npages;
> - ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg,
> - SNP_MSG_REPORT_REQ, &req.data,
> - sizeof(req.data), resp->data, resp_len);
> + guest_req.msg_version = arg->msg_version;
> + guest_req.msg_type = SNP_MSG_REPORT_REQ;
> + guest_req.vmpck_id = vmpck_id;
> + guest_req.req_buf = &req.data;
> + guest_req.req_sz = sizeof(req.data);
> + guest_req.resp_buf = resp->data;
> + guest_req.resp_sz = resp_len;
> + guest_req.exit_code = SVM_VMGEXIT_EXT_GUEST_REQUEST;
> + guest_req.data = snp_dev->certs_data;
> + guest_req.data_npages = &npages;
> +
> + ret = snp_send_guest_request(snp_dev, &guest_req, arg);
>
> /* If certs length is invalid then copy the returned length */
> if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
> - req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
> + req.certs_len = npages << PAGE_SHIFT;
>
> if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req)))
> ret = -EFAULT;
> @@ -530,7 +552,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
> if (ret)
> goto e_free;
>
> - if (npages &&
> + if (npages && req.certs_len &&
> copy_to_user((void __user *)req.certs_address, snp_dev->certs_data,
> req.certs_len)) {
> ret = -EFAULT;
> @@ -734,7 +756,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> /* initial the input address for guest request */
> snp_dev->input.req_gpa = __pa(snp_dev->request);
> snp_dev->input.resp_gpa = __pa(snp_dev->response);
> - snp_dev->input.data_gpa = __pa(snp_dev->certs_data);
>
> ret = misc_register(misc);
> if (ret)

2023-10-30 18:26:42

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 05/14] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> Drop vmpck and os_area_msg_seqno pointers so that secret page layout
> does not need to be exposed to the sev-guest driver after the rework.
> Instead, add helper APIs to access vmpck and os_area_msg_seqno when
> needed.
>
> Also, change function is_vmpck_empty() to snp_is_vmpck_empty() in
> preparation for moving to sev.c.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>

With the fix to the snp_assign_vmpck() to change the int to an unsigned
int as requested by Dionna...

Reviewed-by: Tom Lendacky <[email protected]>

> ---
> drivers/virt/coco/sev-guest/sev-guest.c | 85 ++++++++++++-------------
> 1 file changed, 42 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
> index 5801dd52ffdf..4dd094c73e2f 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -50,8 +50,7 @@ struct snp_guest_dev {
>
> struct snp_secrets_page_layout *layout;
> struct snp_req_data input;
> - u32 *os_area_msg_seqno;
> - u8 *vmpck;
> + unsigned int vmpck_id;
> };
>
> static u32 vmpck_id;
> @@ -61,14 +60,22 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
> /* Mutex to serialize the shared buffer access and command handling. */
> static DEFINE_MUTEX(snp_cmd_mutex);
>
> -static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
> +static inline u8 *snp_get_vmpck(struct snp_guest_dev *snp_dev)
> {
> - char zero_key[VMPCK_KEY_LEN] = {0};
> + return snp_dev->layout->vmpck0 + snp_dev->vmpck_id * VMPCK_KEY_LEN;
> +}
>
> - if (snp_dev->vmpck)
> - return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
> +static inline u32 *snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
> +{
> + return &snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;
> +}
>
> - return true;
> +static bool snp_is_vmpck_empty(struct snp_guest_dev *snp_dev)
> +{
> + char zero_key[VMPCK_KEY_LEN] = {0};
> + u8 *key = snp_get_vmpck(snp_dev);
> +
> + return !memcmp(key, zero_key, VMPCK_KEY_LEN);
> }
>
> /*
> @@ -90,20 +97,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
> */
> static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
> {
> + u8 *key = snp_get_vmpck(snp_dev);
> +
> dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n",
> - vmpck_id);
> - memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
> - snp_dev->vmpck = NULL;
> + snp_dev->vmpck_id);
> + memzero_explicit(key, VMPCK_KEY_LEN);
> }
>
> static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
> {
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
> u64 count;
>
> lockdep_assert_held(&snp_dev->cmd_mutex);
>
> /* Read the current message sequence counter from secrets pages */
> - count = *snp_dev->os_area_msg_seqno;
> + count = *os_area_msg_seqno;
>
> return count + 1;
> }
> @@ -131,11 +140,13 @@ static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>
> static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
> {
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev);
> +
> /*
> * The counter is also incremented by the PSP, so increment it by 2
> * and save in secrets page.
> */
> - *snp_dev->os_area_msg_seqno += 2;
> + *os_area_msg_seqno += 2;
> }
>
> static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> @@ -145,15 +156,22 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> return container_of(dev, struct snp_guest_dev, misc);
> }
>
> -static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
> +static struct aesgcm_ctx *snp_init_crypto(struct snp_guest_dev *snp_dev)
> {
> struct aesgcm_ctx *ctx;
> + u8 *key;
> +
> + if (snp_is_vmpck_empty(snp_dev)) {
> + pr_err("SNP: vmpck id %d is null\n", snp_dev->vmpck_id);
> + return NULL;
> + }
>
> ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
> if (!ctx)
> return NULL;
>
> - if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
> + key = snp_get_vmpck(snp_dev);
> + if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
> pr_err("SNP: crypto init failed\n");
> kfree(ctx);
> return NULL;
> @@ -586,7 +604,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
> mutex_lock(&snp_dev->cmd_mutex);
>
> /* Check if the VMPCK is not empty */
> - if (is_vmpck_empty(snp_dev)) {
> + if (snp_is_vmpck_empty(snp_dev)) {
> dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
> mutex_unlock(&snp_dev->cmd_mutex);
> return -ENOTTY;
> @@ -656,32 +674,14 @@ static const struct file_operations snp_guest_fops = {
> .unlocked_ioctl = snp_guest_ioctl,
> };
>
> -static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
> +bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
> {
> - u8 *key = NULL;
> + if (WARN_ON(vmpck_id > 3))
> + return false;
>
> - switch (id) {
> - case 0:
> - *seqno = &layout->os_area.msg_seqno_0;
> - key = layout->vmpck0;
> - break;
> - case 1:
> - *seqno = &layout->os_area.msg_seqno_1;
> - key = layout->vmpck1;
> - break;
> - case 2:
> - *seqno = &layout->os_area.msg_seqno_2;
> - key = layout->vmpck2;
> - break;
> - case 3:
> - *seqno = &layout->os_area.msg_seqno_3;
> - key = layout->vmpck3;
> - break;
> - default:
> - break;
> - }
> + dev->vmpck_id = vmpck_id;
>
> - return key;
> + return true;
> }
>
> static int __init sev_guest_probe(struct platform_device *pdev)
> @@ -713,14 +713,14 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> goto e_unmap;
>
> ret = -EINVAL;
> - snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
> - if (!snp_dev->vmpck) {
> + snp_dev->layout = layout;
> + if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
> dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
> goto e_unmap;
> }
>
> /* Verify that VMPCK is not zero. */
> - if (is_vmpck_empty(snp_dev)) {
> + if (snp_is_vmpck_empty(snp_dev)) {
> dev_err(dev, "vmpck id %d is null\n", vmpck_id);
> goto e_unmap;
> }
> @@ -728,7 +728,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> mutex_init(&snp_dev->cmd_mutex);
> platform_set_drvdata(pdev, snp_dev);
> snp_dev->dev = dev;
> - snp_dev->layout = layout;
>
> /* Allocate the shared page used for the request and response message. */
> snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
> @@ -744,7 +743,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> goto e_free_response;
>
> ret = -EIO;
> - snp_dev->ctx = snp_init_crypto(snp_dev->vmpck, VMPCK_KEY_LEN);
> + snp_dev->ctx = snp_init_crypto(snp_dev);
> if (!snp_dev->ctx)
> goto e_free_cert_data;
>

2023-10-30 19:18:58

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 07/14] x86/sev: Move and reorganize sev guest request api

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> For enabling Secure TSC, SEV-SNP guests need to communicate with the
> AMD Security Processor early during boot. Many of the required
> functions are implemented in the sev-guest driver and therefore not
> available at early boot. Move the required functions and provide an
> API to the driver to assign key and send guest request.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/Kconfig | 1 +
> arch/x86/include/asm/sev-guest.h | 84 +++-
> arch/x86/include/asm/sev.h | 10 -
> arch/x86/kernel/sev.c | 466 ++++++++++++++++++++++-
> drivers/virt/coco/sev-guest/Kconfig | 1 -
> drivers/virt/coco/sev-guest/sev-guest.c | 486 +-----------------------
> 6 files changed, 555 insertions(+), 493 deletions(-)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 66bfabae8814..245a18f6910a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1509,6 +1509,7 @@ config AMD_MEM_ENCRYPT
> select ARCH_HAS_CC_PLATFORM
> select X86_MEM_ENCRYPT
> select UNACCEPTED_MEMORY
> + select CRYPTO_LIB_AESGCM
> help
> Say yes to enable support for the encryption of system memory.
> This requires an AMD processor that supports Secure Memory
> diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
> index 22ef97b55069..e6f94208173d 100644
> --- a/arch/x86/include/asm/sev-guest.h
> +++ b/arch/x86/include/asm/sev-guest.h
> @@ -11,6 +11,11 @@
> #define __VIRT_SEVGUEST_H__
>
> #include <linux/types.h>
> +#include <linux/miscdevice.h>
> +#include <asm/sev.h>
> +
> +#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
> +#define SNP_REQ_RETRY_DELAY (2*HZ)
>
> #define MAX_AUTHTAG_LEN 32
> #define AUTHTAG_LEN 16
> @@ -58,11 +63,45 @@ struct snp_guest_msg_hdr {
> u8 rsvd3[35];
> } __packed;
>
> +/* SNP Guest message request */
> +struct snp_req_data {
> + unsigned long req_gpa;
> + unsigned long resp_gpa;
> +};
> +
> struct snp_guest_msg {
> struct snp_guest_msg_hdr hdr;
> u8 payload[4000];
> } __packed;
>
> +struct sev_guest_platform_data {
> + /* request and response are in unencrypted memory */
> + struct snp_guest_msg *request, *response;
> +
> + struct snp_secrets_page_layout *layout;
> + struct snp_req_data input;
> +};
> +
> +struct snp_guest_dev {
> + struct device *dev;
> + struct miscdevice misc;
> +
> + /* Mutex to serialize the shared buffer access and command handling. */
> + struct mutex cmd_mutex;
> +
> + void *certs_data;
> + struct aesgcm_ctx *ctx;
> +
> + /*
> + * Avoid information leakage by double-buffering shared messages
> + * in fields that are in regular encrypted memory
> + */
> + struct snp_guest_msg secret_request, secret_response;
> +
> + struct sev_guest_platform_data *pdata;
> + unsigned int vmpck_id;
> +};
> +
> struct snp_guest_req {
> void *req_buf, *resp_buf, *data;
> size_t req_sz, resp_sz, *data_npages;
> @@ -72,6 +111,47 @@ struct snp_guest_req {
> u8 msg_type;
> };
>
> -int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
> - struct snp_guest_request_ioctl *rio);
> +int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
> +int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
> + struct snp_guest_request_ioctl *rio);
> +bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id);
> +bool snp_is_vmpck_empty(unsigned int vmpck_id);
> +
> +static void free_shared_pages(void *buf, size_t sz)

These should probably be marked __inline if you're going to define them in
a header file.

> +{
> + unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
> + int ret;
> +
> + if (!buf)
> + return;
> +
> + ret = set_memory_encrypted((unsigned long)buf, npages);
> + if (ret) {
> + WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
> + return;
> + }
> +
> + __free_pages(virt_to_page(buf), get_order(sz));
> +}
> +
> +static void *alloc_shared_pages(size_t sz)
> +{
> + unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
> + struct page *page;
> + int ret;
> +
> + page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
> + if (!page)
> + return NULL;
> +
> + ret = set_memory_decrypted((unsigned long)page_address(page), npages);
> + if (ret) {
> + pr_err("%s: failed to mark page shared, ret=%d\n", __func__, ret);
> + __free_pages(page, get_order(sz));
> + return NULL;
> + }
> +
> + return page_address(page);
> +}
> +
> #endif /* __VIRT_SEVGUEST_H__ */
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 78465a8c7dc6..783150458864 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -93,16 +93,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
>
> #define RMPADJUST_VMSA_PAGE_BIT BIT(16)
>
> -/* SNP Guest message request */
> -struct snp_req_data {
> - unsigned long req_gpa;
> - unsigned long resp_gpa;
> -};
> -
> -struct sev_guest_platform_data {
> - u64 secrets_gpa;
> -};
> -
> /*
> * The secrets page contains 96-bytes of reserved field that can be used by
> * the guest OS. The guest OS uses the area to save the message sequence
> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
> index fd3b822fa9e7..fb3b1feb1b84 100644
> --- a/arch/x86/kernel/sev.c
> +++ b/arch/x86/kernel/sev.c
> @@ -24,6 +24,7 @@
> #include <linux/io.h>
> #include <linux/psp-sev.h>
> #include <uapi/linux/sev-guest.h>
> +#include <crypto/gcm.h>
>
> #include <asm/cpu_entry_area.h>
> #include <asm/stacktrace.h>
> @@ -941,6 +942,457 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
> free_page((unsigned long)vmsa);
> }
>
> +static struct sev_guest_platform_data *platform_data;
> +
> +static inline u8 *snp_get_vmpck(unsigned int vmpck_id)
> +{
> + if (!platform_data)
> + return NULL;
> +
> + return platform_data->layout->vmpck0 + vmpck_id * VMPCK_KEY_LEN;
> +}
> +
> +static inline u32 *snp_get_os_area_msg_seqno(unsigned int vmpck_id)
> +{
> + if (!platform_data)
> + return NULL;
> +
> + return &platform_data->layout->os_area.msg_seqno_0 + vmpck_id;
> +}
> +
> +bool snp_is_vmpck_empty(unsigned int vmpck_id)
> +{
> + char zero_key[VMPCK_KEY_LEN] = {0};
> + u8 *key = snp_get_vmpck(vmpck_id);
> +
> + if (key)
> + return !memcmp(key, zero_key, VMPCK_KEY_LEN);
> +
> + return true;
> +}
> +EXPORT_SYMBOL_GPL(snp_is_vmpck_empty);
> +
> +/*
> + * If an error is received from the host or AMD Secure Processor (ASP) there
> + * are two options. Either retry the exact same encrypted request or discontinue
> + * using the VMPCK.
> + *
> + * This is because in the current encryption scheme GHCB v2 uses AES-GCM to
> + * encrypt the requests. The IV for this scheme is the sequence number. GCM
> + * cannot tolerate IV reuse.
> + *
> + * The ASP FW v1.51 only increments the sequence numbers on a successful
> + * guest<->ASP back and forth and only accepts messages at its exact sequence
> + * number.
> + *
> + * So if the sequence number were to be reused the encryption scheme is
> + * vulnerable. If the sequence number were incremented for a fresh IV the ASP
> + * will reject the request.
> + */
> +static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
> +{
> + u8 *key = snp_get_vmpck(snp_dev->vmpck_id);
> +
> + pr_alert("Disabling vmpck_id %d to prevent IV reuse.\n", snp_dev->vmpck_id);
> + memzero_explicit(key, VMPCK_KEY_LEN);
> +}
> +
> +static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
> +{
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
> + u64 count;
> +
> + if (!os_area_msg_seqno) {
> + pr_err("SNP unable to get message sequence counter\n");
> + return 0;
> + }
> +
> + lockdep_assert_held(&snp_dev->cmd_mutex);
> +
> + /* Read the current message sequence counter from secrets pages */
> + count = *os_area_msg_seqno;
> +
> + return count + 1;
> +}
> +
> +/* Return a non-zero on success */
> +static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
> +{
> + u64 count = __snp_get_msg_seqno(snp_dev);
> +
> + /*
> + * The message sequence counter for the SNP guest request is a 64-bit
> + * value but the version 2 of GHCB specification defines a 32-bit storage
> + * for it. If the counter exceeds the 32-bit value then return zero.
> + * The caller should check the return value, but if the caller happens to
> + * not check the value and use it, then the firmware treats zero as an
> + * invalid number and will fail the message request.
> + */
> + if (count >= UINT_MAX) {
> + pr_err("SNP request message sequence counter overflow\n");
> + return 0;
> + }
> +
> + return count;
> +}
> +
> +static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
> +{
> + u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
> +
> + if (!os_area_msg_seqno) {
> + pr_err("SNP unable to get message sequence counter\n");
> + return;
> + }

I probably missed this in the other patch or even when the driver was
first created, but shouldn't we have a lockdep_assert_held() here, too,
before updating the count?

> +
> + /*
> + * The counter is also incremented by the PSP, so increment it by 2
> + * and save in secrets page.
> + */
> + *os_area_msg_seqno += 2;
> +}
> +
> +static struct aesgcm_ctx *snp_init_crypto(unsigned int vmpck_id)
> +{
> + struct aesgcm_ctx *ctx;
> + u8 *key;
> +
> + if (snp_is_vmpck_empty(vmpck_id)) {
> + pr_err("SNP: vmpck id %d is null\n", vmpck_id);
> + return NULL;
> + }
> +
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
> + if (!ctx)
> + return NULL;
> +
> + key = snp_get_vmpck(vmpck_id);
> + if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
> + pr_err("SNP: crypto init failed\n");
> + kfree(ctx);
> + return NULL;
> + }
> +
> + return ctx;
> +}
> +
> +int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev)
> +{
> + struct sev_guest_platform_data *pdata;
> + int ret;
> +
> + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
> + pr_err("SNP not supported\n");
> + return 0;
> + }
> +
> + if (platform_data) {
> + pr_debug("SNP platform data already initialized.\n");
> + goto create_ctx;
> + }
> +
> + if (!secrets_pa) {
> + pr_err("SNP no secrets page\n");

Maybe "SNP secrets page not found\n" ?

> + return -ENODEV;
> + }
> +
> + pdata = kzalloc(sizeof(struct sev_guest_platform_data), GFP_KERNEL);
> + if (!pdata) {
> + pr_err("SNP alloc failed\n");

Maybe "Allocation of SNP guest platform data failed\n" ?

> + return -ENOMEM;
> + }
> +
> + pdata->layout = (__force void *)ioremap_encrypted(secrets_pa, PAGE_SIZE);
> + if (!pdata->layout) {
> + pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");

Maybe "Failed to map SNP secrets page\n" ? Not sure where the AP jump
table came in on this...

> + goto e_free_pdata;
> + }
> +
> + ret = -ENOMEM;
> + /* Allocate the shared page used for the request and response message. */
> + pdata->request = alloc_shared_pages(sizeof(struct snp_guest_msg));
> + if (!pdata->request)
> + goto e_unmap;
> +
> + pdata->response = alloc_shared_pages(sizeof(struct snp_guest_msg));
> + if (!pdata->response)
> + goto e_free_request;
> +
> + /* initial the input address for guest request */
> + pdata->input.req_gpa = __pa(pdata->request);
> + pdata->input.resp_gpa = __pa(pdata->response);
> + platform_data = pdata;
> +
> +create_ctx:
> + ret = -EIO;
> + snp_dev->ctx = snp_init_crypto(snp_dev->vmpck_id);
> + if (!snp_dev->ctx) {
> + pr_err("SNP init crypto failed\n");

Maybe "SNP crypto context initialization failed\n" ?

> + platform_data = NULL;
> + goto e_free_response;
> + }
> +
> + snp_dev->pdata = platform_data;

Add a blank line here.

> + return 0;
> +
> +e_free_response:
> + free_shared_pages(pdata->response, sizeof(struct snp_guest_msg));
> +e_free_request:
> + free_shared_pages(pdata->request, sizeof(struct snp_guest_msg));
> +e_unmap:
> + iounmap(pdata->layout);
> +e_free_pdata:
> + kfree(pdata);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(snp_setup_psp_messaging);
> +
> +static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
> + void *plaintext, size_t len)
> +{
> + struct snp_guest_msg_hdr *hdr = &msg->hdr;
> + u8 iv[GCM_AES_IV_SIZE] = {};
> +
> + if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
> + return -EBADMSG;
> +
> + memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> + aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
> + iv, hdr->authtag);
> + return 0;
> +}
> +
> +static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
> + void *plaintext, size_t len)
> +{
> + struct snp_guest_msg_hdr *hdr = &msg->hdr;
> + u8 iv[GCM_AES_IV_SIZE] = {};
> +
> + memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
> + if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
> + AAD_LEN, iv, hdr->authtag))
> + return 0;
> + else
> + return -EBADMSG;
> +}
> +
> +static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req,
> + struct sev_guest_platform_data *pdata)
> +{
> + struct snp_guest_msg *resp = &snp_dev->secret_response;
> + struct snp_guest_msg *req = &snp_dev->secret_request;
> + struct snp_guest_msg_hdr *req_hdr = &req->hdr;
> + struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
> + struct aesgcm_ctx *ctx = snp_dev->ctx;
> +
> + pr_debug("response [seqno %lld type %d version %d sz %d]\n",
> + resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version,
> + resp_hdr->msg_sz);
> +
> + /* Copy response from shared memory to encrypted memory. */
> + memcpy(resp, pdata->response, sizeof(*resp));
> +
> + /* Verify that the sequence counter is incremented by 1 */
> + if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
> + return -EBADMSG;
> +
> + /* Verify response message type and version number. */
> + if (resp_hdr->msg_type != (req_hdr->msg_type + 1) ||
> + resp_hdr->msg_version != req_hdr->msg_version)
> + return -EBADMSG;
> +
> + /*
> + * If the message size is greater than our buffer length then return
> + * an error.
> + */
> + if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
> + return -EBADMSG;
> +
> + return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
> +}
> +
> +static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
> +{
> + struct snp_guest_msg *msg = &snp_dev->secret_request;
> + struct snp_guest_msg_hdr *hdr = &msg->hdr;
> +
> + memset(msg, 0, sizeof(*msg));
> +
> + hdr->algo = SNP_AEAD_AES_256_GCM;
> + hdr->hdr_version = MSG_HDR_VER;
> + hdr->hdr_sz = sizeof(*hdr);
> + hdr->msg_type = req->msg_type;
> + hdr->msg_version = req->msg_version;
> + hdr->msg_seqno = seqno;
> + hdr->msg_vmpck = req->vmpck_id;
> + hdr->msg_sz = req->req_sz;
> +
> + /* Verify the sequence number is non-zero */
> + if (!hdr->msg_seqno)
> + return -ENOSR;
> +
> + pr_debug("request [seqno %lld type %d version %d sz %d]\n",
> + hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
> +
> + return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
> +}
> +
> +static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
> + struct snp_guest_request_ioctl *rio);

Could all of these routines been moved down closer to the bottom of the
file to avoid this forward declaration?

> +
> +static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
> + struct snp_guest_request_ioctl *rio,
> + struct sev_guest_platform_data *pdata)
> +{
> + unsigned long req_start = jiffies;
> + unsigned int override_npages = 0;
> + u64 override_err = 0;
> + int rc;
> +

...

>
> -e_free_ctx:
> - kfree(snp_dev->ctx);
> e_free_cert_data:
> free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
> -e_free_response:
> - free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
> -e_free_request:
> - free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
> -e_unmap:
> - iounmap(mapping);
> + e_free_ctx:
> + kfree(snp_dev->ctx);
> +e_free_snpdev:
> + kfree(snp_dev);
> return ret;
> }
>
> @@ -780,11 +332,9 @@ static int __exit sev_guest_remove(struct platform_device *pdev)
> {
> struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev);
>
> - free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);

Looks like this one should still be here, right?

Thanks,
Tom

> - free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
> - free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
> - kfree(snp_dev->ctx);
> misc_deregister(&snp_dev->misc);
> + kfree(snp_dev->ctx);
> + kfree(snp_dev);
>
> return 0;
> }

2023-10-30 19:20:05

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 08/14] x86/mm: Add generic guest initialization hook

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> Add generic enc_init guest hook for performing any type of
> initialization that is vendor specific.

I think this commit message should be expanded on a bit... like when it is
intended to be called, etc.

Thanks,
Tom

>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/include/asm/x86_init.h | 2 ++
> arch/x86/kernel/x86_init.c | 2 ++
> arch/x86/mm/mem_encrypt.c | 3 +++
> 3 files changed, 7 insertions(+)
>
> diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
> index 5240d88db52a..6a08dcd1f3c4 100644
> --- a/arch/x86/include/asm/x86_init.h
> +++ b/arch/x86/include/asm/x86_init.h
> @@ -148,12 +148,14 @@ struct x86_init_acpi {
> * @enc_status_change_finish Notify HV after the encryption status of a range is changed
> * @enc_tlb_flush_required Returns true if a TLB flush is needed before changing page encryption status
> * @enc_cache_flush_required Returns true if a cache flush is needed before changing page encryption status
> + * @enc_init Prepare and initialize encryption features
> */
> struct x86_guest {
> bool (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc);
> bool (*enc_status_change_finish)(unsigned long vaddr, int npages, bool enc);
> bool (*enc_tlb_flush_required)(bool enc);
> bool (*enc_cache_flush_required)(void);
> + void (*enc_init)(void);
> };
>
> /**
> diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
> index a37ebd3b4773..a07985a96ca5 100644
> --- a/arch/x86/kernel/x86_init.c
> +++ b/arch/x86/kernel/x86_init.c
> @@ -136,6 +136,7 @@ static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool
> static bool enc_tlb_flush_required_noop(bool enc) { return false; }
> static bool enc_cache_flush_required_noop(void) { return false; }
> static bool is_private_mmio_noop(u64 addr) {return false; }
> +static void enc_init_noop(void) { }
>
> struct x86_platform_ops x86_platform __ro_after_init = {
> .calibrate_cpu = native_calibrate_cpu_early,
> @@ -158,6 +159,7 @@ struct x86_platform_ops x86_platform __ro_after_init = {
> .enc_status_change_finish = enc_status_change_finish_noop,
> .enc_tlb_flush_required = enc_tlb_flush_required_noop,
> .enc_cache_flush_required = enc_cache_flush_required_noop,
> + .enc_init = enc_init_noop,
> },
> };
>
> diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
> index 9f27e14e185f..01abecc9a774 100644
> --- a/arch/x86/mm/mem_encrypt.c
> +++ b/arch/x86/mm/mem_encrypt.c
> @@ -84,5 +84,8 @@ void __init mem_encrypt_init(void)
> /* Call into SWIOTLB to update the SWIOTLB DMA buffers */
> swiotlb_update_mem_attributes();
>
> + if (x86_platform.guest.enc_init)
> + x86_platform.guest.enc_init();
> +
> print_mem_encrypt_feature_info();
> }

2023-10-30 20:27:14

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> Add support for Secure TSC in SNP enabled guests. Secure TSC allows
> guest to securely use RDTSC/RDTSCP instructions as the parameters
> being used cannot be changed by hypervisor once the guest is launched.
>
> During the boot-up of the secondary cpus, SecureTSC enabled guests
> need to query TSC info from AMD Security Processor. This communication
> channel is encrypted between the AMD Security Processor and the guest,
> the hypervisor is just the conduit to deliver the guest messages to
> the AMD Security Processor. Each message is protected with an
> AEAD (AES-256 GCM). Use minimal AES GCM library to encrypt/decrypt SNP
> Guest messages to communicate with the PSP.

Add to this commit message that you're using the enc_init hook to perform
some Secure TSC initialization and why you have to do that.

>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/coco/core.c | 3 ++
> arch/x86/include/asm/sev-guest.h | 18 +++++++
> arch/x86/include/asm/sev.h | 2 +
> arch/x86/include/asm/svm.h | 6 ++-
> arch/x86/kernel/sev.c | 82 ++++++++++++++++++++++++++++++++
> arch/x86/mm/mem_encrypt_amd.c | 6 +++
> include/linux/cc_platform.h | 8 ++++
> 7 files changed, 123 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
> index eeec9986570e..5d5d4d03c543 100644
> --- a/arch/x86/coco/core.c
> +++ b/arch/x86/coco/core.c
> @@ -89,6 +89,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
> case CC_ATTR_GUEST_SEV_SNP:
> return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
>
> + case CC_ATTR_GUEST_SECURE_TSC:
> + return sev_status & MSR_AMD64_SNP_SECURE_TSC;
> +
> default:
> return false;
> }
> diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
> index e6f94208173d..58739173eba9 100644
> --- a/arch/x86/include/asm/sev-guest.h
> +++ b/arch/x86/include/asm/sev-guest.h
> @@ -39,6 +39,8 @@ enum msg_type {
> SNP_MSG_ABSORB_RSP,
> SNP_MSG_VMRK_REQ,
> SNP_MSG_VMRK_RSP,
> + SNP_MSG_TSC_INFO_REQ = 17,
> + SNP_MSG_TSC_INFO_RSP,
>
> SNP_MSG_TYPE_MAX
> };
> @@ -111,6 +113,22 @@ struct snp_guest_req {
> u8 msg_type;
> };
>
> +struct snp_tsc_info_req {
> +#define SNP_TSC_INFO_REQ_SZ 128

Please move this to before the struct definition.

> + /* Must be zero filled */
> + u8 rsvd[SNP_TSC_INFO_REQ_SZ];
> +} __packed;
> +
> +struct snp_tsc_info_resp {
> + /* Status of TSC_INFO message */
> + u32 status;
> + u32 rsvd1;
> + u64 tsc_scale;
> + u64 tsc_offset;
> + u32 tsc_factor;
> + u8 rsvd2[100];
> +} __packed;
> +
> int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
> int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
> struct snp_guest_request_ioctl *rio);
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 783150458864..038a5a15d937 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -200,6 +200,7 @@ void __init __noreturn snp_abort(void);
> void snp_accept_memory(phys_addr_t start, phys_addr_t end);
> u64 snp_get_unsupported_features(u64 status);
> u64 sev_get_status(void);
> +void __init snp_secure_tsc_prepare(void);
> #else
> static inline void sev_es_ist_enter(struct pt_regs *regs) { }
> static inline void sev_es_ist_exit(void) { }
> @@ -223,6 +224,7 @@ static inline void snp_abort(void) { }
> static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
> static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
> static inline u64 sev_get_status(void) { return 0; }
> +static inline void __init snp_secure_tsc_prepare(void) { }
> #endif
>
> #endif
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index 3ac0ffc4f3e2..ee35c0488f56 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -414,7 +414,9 @@ struct sev_es_save_area {
> u8 reserved_0x298[80];
> u32 pkru;
> u32 tsc_aux;
> - u8 reserved_0x2f0[24];
> + u64 tsc_scale;
> + u64 tsc_offset;
> + u8 reserved_0x300[8];
> u64 rcx;
> u64 rdx;
> u64 rbx;
> @@ -546,7 +548,7 @@ static inline void __unused_size_checks(void)
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298);
> - BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0);
> + BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
> BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
> index fb3b1feb1b84..9468809d02c7 100644
> --- a/arch/x86/kernel/sev.c
> +++ b/arch/x86/kernel/sev.c
> @@ -76,6 +76,10 @@ static u64 sev_hv_features __ro_after_init;
> /* Secrets page physical address from the CC blob */
> static u64 secrets_pa __ro_after_init;
>
> +/* Secure TSC values read using TSC_INFO SNP Guest request */
> +static u64 guest_tsc_scale __ro_after_init;
> +static u64 guest_tsc_offset __ro_after_init;

s/guest_/snp_/

> +
> /* #VC handler runtime per-CPU data */
> struct sev_es_runtime_data {
> struct ghcb ghcb_page;
> @@ -1393,6 +1397,78 @@ bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
> }
> EXPORT_SYMBOL_GPL(snp_assign_vmpck);
>
> +static struct snp_guest_dev tsc_snp_dev __initdata;
> +
> +static int __init snp_get_tsc_info(void)
> +{
> + static u8 buf[SNP_TSC_INFO_REQ_SZ + AUTHTAG_LEN];
> + struct snp_guest_request_ioctl rio;
> + struct snp_tsc_info_resp tsc_resp;
> + struct snp_tsc_info_req tsc_req;
> + struct snp_guest_req req;
> + int rc, resp_len;
> +
> + /*
> + * The intermediate response buffer is used while decrypting the
> + * response payload. Make sure that it has enough space to cover the
> + * authtag.
> + */
> + resp_len = sizeof(tsc_resp) + AUTHTAG_LEN;
> + if (sizeof(buf) < resp_len)
> + return -EINVAL;
> +
> + memset(&tsc_req, 0, sizeof(tsc_req));
> + memset(&req, 0, sizeof(req));
> + memset(&rio, 0, sizeof(rio));
> + memset(buf, 0, sizeof(buf));
> +
> + if (!snp_assign_vmpck(&tsc_snp_dev, 0))
> + return -EINVAL;
> +
> + /* Initialize the PSP channel to send snp messages */
> + if (snp_setup_psp_messaging(&tsc_snp_dev))
> + sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

This should just return the non-zero return code from
snp_setup_psp_messaging(), no?

rc = snp_setup_psp_messaging(&tsc_snp_dev);
if (rc)
return rc;

> +
> + req.msg_version = MSG_HDR_VER;
> + req.msg_type = SNP_MSG_TSC_INFO_REQ;
> + req.vmpck_id = tsc_snp_dev.vmpck_id;
> + req.req_buf = &tsc_req;
> + req.req_sz = sizeof(tsc_req);
> + req.resp_buf = buf;
> + req.resp_sz = resp_len;
> + req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
> + rc = snp_send_guest_request(&tsc_snp_dev, &req, &rio);

Aren't you supposed to hold a mutex before calling this since it will
eventually call the message sequence number functions?

> + if (rc)
> + goto err_req;
> +
> + memcpy(&tsc_resp, buf, sizeof(tsc_resp));
> + pr_debug("%s: Valid response status %x scale %llx offset %llx factor %x\n",
> + __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset,
> + tsc_resp.tsc_factor);
> +
> + guest_tsc_scale = tsc_resp.tsc_scale;
> + guest_tsc_offset = tsc_resp.tsc_offset;
> +
> +err_req:
> + /* The response buffer contains the sensitive data, explicitly clear it. */
> + memzero_explicit(buf, sizeof(buf));
> + memzero_explicit(&tsc_resp, sizeof(tsc_resp));
> + memzero_explicit(&req, sizeof(req));
> +
> + return rc;
> +}
> +
> +void __init snp_secure_tsc_prepare(void)
> +{
> + if (!cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
> + return;
> +
> + if (snp_get_tsc_info())
> + sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);

How about using SEV_TERM_SET_LINUX and a new GHCB_TERM_SECURE_TSC_INFO.

> +
> + pr_debug("SecureTSC enabled\n");
> +}
> +
> static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
> {
> struct sev_es_save_area *cur_vmsa, *vmsa;
> @@ -1493,6 +1569,12 @@ static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
> vmsa->vmpl = 0;
> vmsa->sev_features = sev_status >> 2;
>
> + /* Setting Secure TSC parameters */
> + if (cc_platform_has(CC_ATTR_GUEST_SECURE_TSC)) {
> + vmsa->tsc_scale = guest_tsc_scale;
> + vmsa->tsc_offset = guest_tsc_offset;
> + }
> +
> /* Switch the page over to a VMSA page now that it is initialized */
> ret = snp_set_vmsa(vmsa, true);
> if (ret) {
> diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
> index 6faea41e99b6..9935fc506e99 100644
> --- a/arch/x86/mm/mem_encrypt_amd.c
> +++ b/arch/x86/mm/mem_encrypt_amd.c
> @@ -215,6 +215,11 @@ void __init sme_map_bootdata(char *real_mode_data)
> __sme_early_map_unmap_mem(__va(cmdline_paddr), COMMAND_LINE_SIZE, true);
> }
>
> +void __init amd_enc_init(void)
> +{
> + snp_secure_tsc_prepare();
> +}
> +
> void __init sev_setup_arch(void)
> {
> phys_addr_t total_mem = memblock_phys_mem_size();
> @@ -502,6 +507,7 @@ void __init sme_early_init(void)
> x86_platform.guest.enc_status_change_finish = amd_enc_status_change_finish;
> x86_platform.guest.enc_tlb_flush_required = amd_enc_tlb_flush_required;
> x86_platform.guest.enc_cache_flush_required = amd_enc_cache_flush_required;
> + x86_platform.guest.enc_init = amd_enc_init;
>
> /*
> * AMD-SEV-ES intercepts the RDMSR to read the X2APIC ID in the
> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
> index cb0d6cd1c12f..e081ca4d5da2 100644
> --- a/include/linux/cc_platform.h
> +++ b/include/linux/cc_platform.h
> @@ -90,6 +90,14 @@ enum cc_attr {
> * Examples include TDX Guest.
> */
> CC_ATTR_HOTPLUG_DISABLED,
> +
> + /**
> + * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
> + *
> + * The platform/OS is running as a guest/virtual machine and actively
> + * using AMD SEV-SNP Secure TSC feature.

I think TDX also has a secure TSC like feature, so can this be generic?

Thanks,
Tom

> + */
> + CC_ATTR_GUEST_SECURE_TSC,
> };
>
> #ifdef CONFIG_ARCH_HAS_CC_PLATFORM

2023-10-30 20:33:08

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 11/14] x86/sev: Prevent RDTSC/RDTSCP interception for Secure TSC enabled guests

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> The hypervisor should not be intercepting RDTSC/RDTSCP when Secure TSC
> is enabled. A #VC exception will be generated if the RDTSC/RDTSCP
> instructions are being intercepted. If this should occur and Secure
> TSC is enabled, terminate guest execution.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/kernel/sev-shared.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
> index ccb0915e84e1..833b0ae38f0b 100644
> --- a/arch/x86/kernel/sev-shared.c
> +++ b/arch/x86/kernel/sev-shared.c
> @@ -991,6 +991,13 @@ static enum es_result vc_handle_rdtsc(struct ghcb *ghcb,
> bool rdtscp = (exit_code == SVM_EXIT_RDTSCP);
> enum es_result ret;
>
> + /*
> + * RDTSC and RDTSCP should not be intercepted when Secure TSC is
> + * enabled. Terminate the SNP guest when the interception is enabled.
> + */
> + if (sev_status & MSR_AMD64_SNP_SECURE_TSC)

If you have to use sev_status, then please document why cc_platform_has()
can't be used in the comment above.

Thanks,
Tom

> + return ES_VMM_ERROR;
> +
> ret = sev_es_ghcb_hv_call(ghcb, ctxt, exit_code, 0, 0);
> if (ret != ES_OK)
> return ret;

2023-10-30 21:00:54

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 12/14] x86/kvmclock: Skip kvmclock when Secure TSC is available

On 10/30/23 01:36, Nikunj A Dadhania wrote:
> For AMD SNP guests having Secure TSC enabled, skip using the kvmclock.
> The guest kernel will fallback and use Secure TSC based clocksource.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/kernel/kvmclock.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
> index fb8f52149be9..779e7311fa6f 100644
> --- a/arch/x86/kernel/kvmclock.c
> +++ b/arch/x86/kernel/kvmclock.c
> @@ -288,7 +288,7 @@ void __init kvmclock_init(void)
> {
> u8 flags;
>
> - if (!kvm_para_available() || !kvmclock)
> + if (!kvm_para_available() || !kvmclock || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))

And is setting X86_FEATURE_TSC_RELIABLE, as Dave Hansen suggests, enough
to prevent usage of kvmclock?

There was a discussion here:
https://lore.kernel.org/lkml/[email protected]/

Thanks,
Tom

> return;
>
> if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE2)) {

2023-11-02 03:34:26

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 01/14] virt: sev-guest: Use AES GCM crypto library

On 10/30/2023 11:21 PM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> The sev-guest driver encryption code uses Crypto API for SNP guest
>> messaging to interact with AMD Security processor. For enabling SecureTSC,
>> SEV-SNP guests need to send a TSC_INFO request guest message before the
>> smpboot phase starts. Details from the TSC_INFO response will be used to
>> program the VMSA before the secondary CPUs are brought up. The Crypto API
>> is not available this early in the boot phase.
>>
>> In preparation of moving the encryption code out of sev-guest driver to
>> support SecureTSC and make reviewing the diff easier, start using AES GCM
>> library implementation instead of Crypto API.
>>
>> CC: Ard Biesheuvel <[email protected]>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> Reviewed-by: Tom Lendacky <[email protected]>
>
> I just a few nit comments that might be nice to cover if you have to do a v6...

Sure, I will address them in v6.

>>   -static int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
>> +static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
>>                void *plaintext, size_t len)
>>   {
>> -    struct snp_guest_crypto *crypto = snp_dev->crypto;
>>       struct snp_guest_msg_hdr *hdr = &msg->hdr;
>> +    u8 iv[GCM_AES_IV_SIZE] = {};
>>   -    memset(crypto->iv, 0, crypto->iv_len);
>> -    memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> +    if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
>> +        return -EBADMSG;
>>   -    return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true);
>> +    memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> +    aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
>> +               iv, hdr->authtag);
>> +    return 0;
>>   }
>
> __enc_payload() is pretty small now and can probably just be part of the only function that calls it, enc_payload().
>
>>   -static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
>> +static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
>>                  void *plaintext, size_t len)
>>   {
>> -    struct snp_guest_crypto *crypto = snp_dev->crypto;
>>       struct snp_guest_msg_hdr *hdr = &msg->hdr;
>> +    u8 iv[GCM_AES_IV_SIZE] = {};
>>   -    /* Build IV with response buffer sequence number */
>> -    memset(crypto->iv, 0, crypto->iv_len);
>> -    memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> -
>> -    return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false);
>> +    memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> +    if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
>> +               AAD_LEN, iv, hdr->authtag))
>> +        return 0;
>> +    else
>> +        return -EBADMSG;
>
> This would look cleaner / read easier to me to have as:
>
>     if (!aesgcm_decrypt(...))
>         return -EBADMSG;
>
>     return 0;
>
> But just my opinion.
>
> And ditto here on the size now, can probably just be part of verify_and_dec_payload() now.
>
> Thanks,
> Tom

Regards
Nikunj

2023-11-02 04:04:30

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 04/14] virt: sev-guest: Add SNP guest request structure

On 10/30/2023 11:46 PM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> Add a snp_guest_req structure to simplify the function arguments. The
>> structure will be used to call the SNP Guest message request API
>> instead of passing a long list of parameters.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>
> Some minor comments below.
>
>> ---
>>   .../x86/include/asm}/sev-guest.h              |  11 ++
>>   arch/x86/include/asm/sev.h                    |   8 --
>>   arch/x86/kernel/sev.c                         |  15 ++-
>>   drivers/virt/coco/sev-guest/sev-guest.c       | 103 +++++++++++-------
>>   4 files changed, 84 insertions(+), 53 deletions(-)
>>   rename {drivers/virt/coco/sev-guest => arch/x86/include/asm}/sev-guest.h (80%)
>>
>> diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/arch/x86/include/asm/sev-guest.h
>> similarity index 80%
>> rename from drivers/virt/coco/sev-guest/sev-guest.h
>> rename to arch/x86/include/asm/sev-guest.h
>> index ceb798a404d6..22ef97b55069 100644
>> --- a/drivers/virt/coco/sev-guest/sev-guest.h
>> +++ b/arch/x86/include/asm/sev-guest.h
>> @@ -63,4 +63,15 @@ struct snp_guest_msg {
>>       u8 payload[4000];
>>   } __packed;
>>   +struct snp_guest_req {
>> +    void *req_buf, *resp_buf, *data;
>> +    size_t req_sz, resp_sz, *data_npages;
>
> For structures like this, I find it easier to group things and keep it one item per line, e.g.:

Ok, I will change that.

>     void *req_buf;
>     size_t req_sz;
>     
>     void *resp_buf;
>     size_t resp_sz;
>
>     void *data;
>     size_t *data_npages;
>
> And does data_npages have to be a pointer?

Going through the code again, you are right, it need not be a pointer.

> It looks like you can just use this variable as the address on the GHCB call and then set it appropriately without all the indirection, right?

I can use the data_npages value directly in the GHCB call, am I missing something.

@@ -2192,8 +2199,8 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
vc_ghcb_invalidate(ghcb);
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
- ghcb_set_rax(ghcb, input->data_gpa);
- ghcb_set_rbx(ghcb, input->data_npages);
+ ghcb_set_rax(ghcb, __pa(req->data));
+ ghcb_set_rbx(ghcb, req->data_npages);
}
ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
@@ -2212,7 +2219,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
/* Number of expected pages are returned in RBX */
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
- input->data_npages = ghcb_get_rbx(ghcb);
+ req->data_npages = ghcb_get_rbx(ghcb);
ret = -ENOSPC;
break;
}


>
>> +    u64 exit_code;
>> +    unsigned int vmpck_id;
>> +    u8 msg_version;
>> +    u8 msg_type;
>> +};
>> +
>> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
>> +                struct snp_guest_request_ioctl *rio);
>>   #endif /* __VIRT_SEVGUEST_H__ */
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index 5b4a1ce3d368..78465a8c7dc6 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -97,8 +97,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
>>   struct snp_req_data {
>>       unsigned long req_gpa;
>>       unsigned long resp_gpa;
>> -    unsigned long data_gpa;
>> -    unsigned int data_npages;
>>   };
>>     struct sev_guest_platform_data {
>> @@ -209,7 +207,6 @@ void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
>>   void snp_set_wakeup_secondary_cpu(void);
>>   bool snp_init(struct boot_params *bp);
>>   void __init __noreturn snp_abort(void);
>> -int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
>>   void snp_accept_memory(phys_addr_t start, phys_addr_t end);
>>   u64 snp_get_unsupported_features(u64 status);
>>   u64 sev_get_status(void);
>> @@ -233,11 +230,6 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npa
>>   static inline void snp_set_wakeup_secondary_cpu(void) { }
>>   static inline bool snp_init(struct boot_params *bp) { return false; }
>>   static inline void snp_abort(void) { }
>> -static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
>> -{
>> -    return -ENOTTY;
>> -}
>> -
>
> May want to mention in the commit message why this can be deleted vs changed.

Sure will do. It has been moved to sev-guest.h now.

>
>>   static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
>>   static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
>>   static inline u64 sev_get_status(void) { return 0; }
>> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
>> index 6395bfd87b68..f8caf0a73052 100644
>> --- a/arch/x86/kernel/sev.c
>> +++ b/arch/x86/kernel/sev.c
>> @@ -28,6 +28,7 @@
>>   #include <asm/cpu_entry_area.h>
>>   #include <asm/stacktrace.h>
>>   #include <asm/sev.h>
>> +#include <asm/sev-guest.h>
>>   #include <asm/insn-eval.h>
>>   #include <asm/fpu/xcr.h>
>>   #include <asm/processor.h>
>> @@ -2167,15 +2168,21 @@ static int __init init_sev_config(char *str)
>>   }
>>   __setup("sev=", init_sev_config);
>>   -int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
>> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
>> +                struct snp_guest_request_ioctl *rio)
>>   {
>>       struct ghcb_state state;
>>       struct es_em_ctxt ctxt;
>>       unsigned long flags;
>>       struct ghcb *ghcb;
>> +    u64 exit_code;
>>       int ret;
>>         rio->exitinfo2 = SEV_RET_NO_FW_CALL;
>> +    if (!req)
>> +        return -EINVAL;
>> +
>> +    exit_code = req->exit_code;
>>         /*
>>        * __sev_get_ghcb() needs to run with IRQs disabled because it is using
>> @@ -2192,8 +2199,8 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
>>       vc_ghcb_invalidate(ghcb);
>>         if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
>> -        ghcb_set_rax(ghcb, input->data_gpa);
>> -        ghcb_set_rbx(ghcb, input->data_npages);
>> +        ghcb_set_rax(ghcb, __pa(req->data));
>> +        ghcb_set_rbx(ghcb, *req->data_npages);
>>       }
>>         ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
>> @@ -2212,7 +2219,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
>>       case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
>>           /* Number of expected pages are returned in RBX */
>>           if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
>> -            input->data_npages = ghcb_get_rbx(ghcb);
>> +            *req->data_npages = ghcb_get_rbx(ghcb);
>>               ret = -ENOSPC;
>>               break;
>>           }
>> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
>> index 49bafd2e9f42..5801dd52ffdf 100644
>> --- a/drivers/virt/coco/sev-guest/sev-guest.c
>> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
>> @@ -23,8 +23,7 @@
>>     #include <asm/svm.h>
>>   #include <asm/sev.h>
>> -
>> -#include "sev-guest.h"
>> +#include <asm/sev-guest.h>
>>     #define DEVICE_NAME    "sev-guest"
>>   @@ -192,7 +191,7 @@ static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
>>           return -EBADMSG;
>>   }
>>   -static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
>> +static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req)
>>   {
>>       struct snp_guest_msg *resp = &snp_dev->secret_response;
>>       struct snp_guest_msg *req = &snp_dev->secret_request;
>> @@ -220,29 +219,28 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
>>        * If the message size is greater than our buffer length then return
>>        * an error.
>>        */
>> -    if (unlikely((resp_hdr->msg_sz + ctx->authsize) > sz))
>> +    if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
>>           return -EBADMSG;
>>         /* Decrypt the payload */
>> -    return dec_payload(ctx, resp, payload, resp_hdr->msg_sz);
>> +    return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
>>   }
>>   -static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
>> -            void *payload, size_t sz)
>> +static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
>>   {
>> -    struct snp_guest_msg *req = &snp_dev->secret_request;
>> -    struct snp_guest_msg_hdr *hdr = &req->hdr;
>> +    struct snp_guest_msg *msg = &snp_dev->secret_request;
>> +    struct snp_guest_msg_hdr *hdr = &msg->hdr;
>>   -    memset(req, 0, sizeof(*req));
>> +    memset(msg, 0, sizeof(*msg));
>>         hdr->algo = SNP_AEAD_AES_256_GCM;
>>       hdr->hdr_version = MSG_HDR_VER;
>>       hdr->hdr_sz = sizeof(*hdr);
>> -    hdr->msg_type = type;
>> -    hdr->msg_version = version;
>> +    hdr->msg_type = req->msg_type;
>> +    hdr->msg_version = req->msg_version;
>>       hdr->msg_seqno = seqno;
>> -    hdr->msg_vmpck = vmpck_id;
>> -    hdr->msg_sz = sz;
>> +    hdr->msg_vmpck = req->vmpck_id;
>> +    hdr->msg_sz = req->req_sz;
>>         /* Verify the sequence number is non-zero */
>>       if (!hdr->msg_seqno)
>> @@ -251,10 +249,10 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
>>       pr_debug("request [seqno %lld type %d version %d sz %d]\n",
>>            hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
>>   -    return __enc_payload(snp_dev->ctx, req, payload, sz);
>> +    return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
>>   }
>>   -static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>> +static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
>>                     struct snp_guest_request_ioctl *rio)
>>   {
>>       unsigned long req_start = jiffies;
>> @@ -269,7 +267,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>        * sequence number must be incremented or the VMPCK must be deleted to
>>        * prevent reuse of the IV.
>>        */
>> -    rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
>> +    rc = snp_issue_guest_request(req, &snp_dev->input, rio);
>>       switch (rc) {
>>       case -ENOSPC:
>>           /*
>> @@ -279,8 +277,8 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>            * order to increment the sequence number and thus avoid
>>            * IV reuse.
>>            */
>> -        override_npages = snp_dev->input.data_npages;
>> -        exit_code    = SVM_VMGEXIT_GUEST_REQUEST;
>> +        override_npages = *req->data_npages;
>> +        req->exit_code    = SVM_VMGEXIT_GUEST_REQUEST;
>>             /*
>>            * Override the error to inform callers the given extended
>> @@ -335,15 +333,13 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>       }
>>         if (override_npages)
>> -        snp_dev->input.data_npages = override_npages;
>> +        *req->data_npages = override_npages;
>>         return rc;
>>   }
>>   -static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>> -                struct snp_guest_request_ioctl *rio, u8 type,
>> -                void *req_buf, size_t req_sz, void *resp_buf,
>> -                u32 resp_sz)
>> +static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
>> +                  struct snp_guest_request_ioctl *rio)
>>   {
>>       u64 seqno;
>>       int rc;
>> @@ -357,7 +353,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>       memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
>>         /* Encrypt the userspace provided payload in snp_dev->secret_request. */
>> -    rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
>> +    rc = enc_payload(snp_dev, seqno, req);
>>       if (rc)
>>           return rc;
>>   @@ -368,7 +364,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>       memcpy(snp_dev->request, &snp_dev->secret_request,
>>              sizeof(snp_dev->secret_request));
>>   -    rc = __handle_guest_request(snp_dev, exit_code, rio);
>> +    rc = __handle_guest_request(snp_dev, req, rio);
>>       if (rc) {
>>           if (rc == -EIO &&
>>               rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
>> @@ -377,12 +373,11 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>           dev_alert(snp_dev->dev,
>>                 "Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
>>                 rc, rio->exitinfo2);
>> -
>>           snp_disable_vmpck(snp_dev);
>>           return rc;
>>       }
>>   -    rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz);
>> +    rc = verify_and_dec_payload(snp_dev, req);
>>       if (rc) {
>>           dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc);
>>           snp_disable_vmpck(snp_dev);
>> @@ -394,6 +389,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
>>     static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
>>   {
>> +    struct snp_guest_req guest_req = {0};
>>       struct snp_report_resp *resp;
>>       struct snp_report_req req;
>>       int rc, resp_len;
>> @@ -416,9 +412,16 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
>>       if (!resp)
>>           return -ENOMEM;
>>   -    rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
>> -                  SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
>> -                  resp_len);
>> +    guest_req.msg_version = arg->msg_version;
>> +    guest_req.msg_type = SNP_MSG_REPORT_REQ;
>> +    guest_req.vmpck_id = vmpck_id;
>> +    guest_req.req_buf = &req;
>> +    guest_req.req_sz = sizeof(req);
>> +    guest_req.resp_buf = resp->data;
>> +    guest_req.resp_sz = resp_len;
>> +    guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
>> +
>> +    rc = snp_send_guest_request(snp_dev, &guest_req, arg);
>>       if (rc)
>>           goto e_free;
>>   @@ -433,6 +436,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
>>   static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
>>   {
>>       struct snp_derived_key_resp resp = {0};
>> +    struct snp_guest_req guest_req = {0};
>>       struct snp_derived_key_req req;
>>       int rc, resp_len;
>>       /* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
>> @@ -455,8 +459,16 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
>>       if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
>>           return -EFAULT;
>>   -    rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
>> -                  SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len);
>> +    guest_req.msg_version = arg->msg_version;
>> +    guest_req.msg_type = SNP_MSG_KEY_REQ;
>> +    guest_req.vmpck_id = vmpck_id;
>> +    guest_req.req_buf = &req;
>> +    guest_req.req_sz = sizeof(req);
>> +    guest_req.resp_buf = buf;
>> +    guest_req.resp_sz = resp_len;
>> +    guest_req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
>> +
>> +    rc = snp_send_guest_request(snp_dev, &guest_req, arg);
>>       if (rc)
>>           return rc;
>>   @@ -472,9 +484,11 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
>>     static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
>>   {
>> +    struct snp_guest_req guest_req = {0};
>>       struct snp_ext_report_req req;
>>       struct snp_report_resp *resp;
>> -    int ret, npages = 0, resp_len;
>> +    int ret, resp_len;
>> +    size_t npages = 0;
>
> This becomes unnecessary if you don't define data_npages as a pointer in the snp_guest_req structure.

Right, I will change that.

>
> Thanks,
> Tom

Thanks
Nikunj

2023-11-02 04:05:14

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 05/14] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On 10/30/2023 10:42 PM, Tom Lendacky wrote:
> On 10/30/23 11:16, Dionna Amalie Glaze wrote:
>> On Sun, Oct 29, 2023 at 11:38 PM Nikunj A Dadhania <[email protected]> wrote:

>>> @@ -656,32 +674,14 @@ static const struct file_operations snp_guest_fops = {
>>>          .unlocked_ioctl = snp_guest_ioctl,
>>>   };
>>>
>>> -static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
>>> +bool snp_assign_vmpck(struct snp_guest_dev *dev, int vmpck_id)
>>>   {
>>> -       u8 *key = NULL;
>>> +       if (WARN_ON(vmpck_id > 3))
>>> +               return false;
>>
>> The vmpck_id is an int for some reason, so < 0 is also a problem. Can
>> we not use unsigned int?

Yes, I will update that in my next revision,

Thanks
Nikunj

2023-11-02 04:29:41

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 07/14] x86/sev: Move and reorganize sev guest request api

On 10/31/2023 12:46 AM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> For enabling Secure TSC, SEV-SNP guests need to communicate with the
>> AMD Security Processor early during boot. Many of the required
>> functions are implemented in the sev-guest driver and therefore not
>> available at early boot. Move the required functions and provide an
>> API to the driver to assign key and send guest request.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---

>> @@ -72,6 +111,47 @@ struct snp_guest_req {
>>       u8 msg_type;
>>   };
>>   -int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
>> -                struct snp_guest_request_ioctl *rio);
>> +int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
>> +int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
>> +               struct snp_guest_request_ioctl *rio);
>> +bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id);
>> +bool snp_is_vmpck_empty(unsigned int vmpck_id);
>> +
>> +static void free_shared_pages(void *buf, size_t sz)
>
> These should probably be marked __inline if you're going to define them in a header file.

Sure, I will udpate both free_shared_pages() and alloc_shared_pages().

>
>> +{
>> +    unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
>> +    int ret;
>> +
>> +    if (!buf)
>> +        return;
>> +
>> +    ret = set_memory_encrypted((unsigned long)buf, npages);
>> +    if (ret) {
>> +        WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
>> +        return;
>> +    }
>> +
>> +    __free_pages(virt_to_page(buf), get_order(sz));
>> +}
>> +
>> +static void *alloc_shared_pages(size_t sz)
>> +{
>> +    unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
>> +    struct page *page;
>> +    int ret;
>> +
>> +    page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
>> +    if (!page)
>> +        return NULL;
>> +
>> +    ret = set_memory_decrypted((unsigned long)page_address(page), npages);
>> +    if (ret) {
>> +        pr_err("%s: failed to mark page shared, ret=%d\n", __func__, ret);
>> +        __free_pages(page, get_order(sz));
>> +        return NULL;
>> +    }
>> +
>> +    return page_address(page);
>> +}
>> +
>>   #endif /* __VIRT_SEVGUEST_H__ */
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index 78465a8c7dc6..783150458864 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -93,16 +93,6 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
>>     #define RMPADJUST_VMSA_PAGE_BIT        BIT(16)
>>   -/* SNP Guest message request */
>> -struct snp_req_data {
>> -    unsigned long req_gpa;
>> -    unsigned long resp_gpa;
>> -};
>> -
>> -struct sev_guest_platform_data {
>> -    u64 secrets_gpa;
>> -};
>> -
>>   /*
>>    * The secrets page contains 96-bytes of reserved field that can be used by
>>    * the guest OS. The guest OS uses the area to save the message sequence
>> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
>> index fd3b822fa9e7..fb3b1feb1b84 100644
>> --- a/arch/x86/kernel/sev.c
>> +++ b/arch/x86/kernel/sev.c
>> @@ -24,6 +24,7 @@
>>   #include <linux/io.h>
>>   #include <linux/psp-sev.h>
>>   #include <uapi/linux/sev-guest.h>
>> +#include <crypto/gcm.h>
>>     #include <asm/cpu_entry_area.h>
>>   #include <asm/stacktrace.h>
>> @@ -941,6 +942,457 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
>>           free_page((unsigned long)vmsa);
>>   }
>>   +static struct sev_guest_platform_data *platform_data;
>> +
>> +static inline u8 *snp_get_vmpck(unsigned int vmpck_id)
>> +{
>> +    if (!platform_data)
>> +        return NULL;
>> +
>> +    return platform_data->layout->vmpck0 + vmpck_id * VMPCK_KEY_LEN;
>> +}
>> +
>> +static inline u32 *snp_get_os_area_msg_seqno(unsigned int vmpck_id)
>> +{
>> +    if (!platform_data)
>> +        return NULL;
>> +
>> +    return &platform_data->layout->os_area.msg_seqno_0 + vmpck_id;
>> +}
>> +
>> +bool snp_is_vmpck_empty(unsigned int vmpck_id)
>> +{
>> +    char zero_key[VMPCK_KEY_LEN] = {0};
>> +    u8 *key = snp_get_vmpck(vmpck_id);
>> +
>> +    if (key)
>> +        return !memcmp(key, zero_key, VMPCK_KEY_LEN);
>> +
>> +    return true;
>> +}
>> +EXPORT_SYMBOL_GPL(snp_is_vmpck_empty);
>> +
>> +/*
>> + * If an error is received from the host or AMD Secure Processor (ASP) there
>> + * are two options. Either retry the exact same encrypted request or discontinue
>> + * using the VMPCK.
>> + *
>> + * This is because in the current encryption scheme GHCB v2 uses AES-GCM to
>> + * encrypt the requests. The IV for this scheme is the sequence number. GCM
>> + * cannot tolerate IV reuse.
>> + *
>> + * The ASP FW v1.51 only increments the sequence numbers on a successful
>> + * guest<->ASP back and forth and only accepts messages at its exact sequence
>> + * number.
>> + *
>> + * So if the sequence number were to be reused the encryption scheme is
>> + * vulnerable. If the sequence number were incremented for a fresh IV the ASP
>> + * will reject the request.
>> + */
>> +static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
>> +{
>> +    u8 *key = snp_get_vmpck(snp_dev->vmpck_id);
>> +
>> +    pr_alert("Disabling vmpck_id %d to prevent IV reuse.\n", snp_dev->vmpck_id);
>> +    memzero_explicit(key, VMPCK_KEY_LEN);
>> +}
>> +
>> +static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>> +{
>> +    u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
>> +    u64 count;
>> +
>> +    if (!os_area_msg_seqno) {
>> +        pr_err("SNP unable to get message sequence counter\n");
>> +        return 0;
>> +    }
>> +
>> +    lockdep_assert_held(&snp_dev->cmd_mutex);
>> +
>> +    /* Read the current message sequence counter from secrets pages */
>> +    count = *os_area_msg_seqno;
>> +
>> +    return count + 1;
>> +}
>> +
>> +/* Return a non-zero on success */
>> +static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
>> +{
>> +    u64 count = __snp_get_msg_seqno(snp_dev);
>> +
>> +    /*
>> +     * The message sequence counter for the SNP guest request is a  64-bit
>> +     * value but the version 2 of GHCB specification defines a 32-bit storage
>> +     * for it. If the counter exceeds the 32-bit value then return zero.
>> +     * The caller should check the return value, but if the caller happens to
>> +     * not check the value and use it, then the firmware treats zero as an
>> +     * invalid number and will fail the  message request.
>> +     */
>> +    if (count >= UINT_MAX) {
>> +        pr_err("SNP request message sequence counter overflow\n");
>> +        return 0;
>> +    }
>> +
>> +    return count;
>> +}
>> +
>> +static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
>> +{
>> +    u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
>> +
>> +    if (!os_area_msg_seqno) {
>> +        pr_err("SNP unable to get message sequence counter\n");
>> +        return;
>> +    }
>
> I probably missed this in the other patch or even when the driver was first created, but shouldn't we have a lockdep_assert_held() here, too, before updating the count?

As per the current code flow, snp_get_msg_seqno() is always called before snp_inc_msg_seqno(), maybe because of that the check wasnt there. It still makes sense to have a lockdep_assert_held() in snp_inc_msg_seqno().

Should I add this change as a separate fix ?

>
>> +
>> +    /*
>> +     * The counter is also incremented by the PSP, so increment it by 2
>> +     * and save in secrets page.
>> +     */
>> +    *os_area_msg_seqno += 2;
>> +}
>> +
>> +static struct aesgcm_ctx *snp_init_crypto(unsigned int vmpck_id)
>> +{
>> +    struct aesgcm_ctx *ctx;
>> +    u8 *key;
>> +
>> +    if (snp_is_vmpck_empty(vmpck_id)) {
>> +        pr_err("SNP: vmpck id %d is null\n", vmpck_id);
>> +        return NULL;
>> +    }
>> +
>> +    ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
>> +    if (!ctx)
>> +        return NULL;
>> +
>> +    key = snp_get_vmpck(vmpck_id);
>> +    if (aesgcm_expandkey(ctx, key, VMPCK_KEY_LEN, AUTHTAG_LEN)) {
>> +        pr_err("SNP: crypto init failed\n");
>> +        kfree(ctx);
>> +        return NULL;
>> +    }
>> +
>> +    return ctx;
>> +}
>> +
>> +int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev)
>> +{
>> +    struct sev_guest_platform_data *pdata;
>> +    int ret;
>> +
>> +    if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
>> +        pr_err("SNP not supported\n");
>> +        return 0;
>> +    }
>> +
>> +    if (platform_data) {
>> +        pr_debug("SNP platform data already initialized.\n");
>> +        goto create_ctx;
>> +    }
>> +
>> +    if (!secrets_pa) {
>> +        pr_err("SNP no secrets page\n");
>
> Maybe "SNP secrets page not found\n" ?
>
>> +        return -ENODEV;
>> +    }
>> +
>> +    pdata = kzalloc(sizeof(struct sev_guest_platform_data), GFP_KERNEL);
>> +    if (!pdata) {
>> +        pr_err("SNP alloc failed\n");
>
> Maybe "Allocation of SNP guest platform data failed\n" ?
>
>> +        return -ENOMEM;
>> +    }
>> +
>> +    pdata->layout = (__force void *)ioremap_encrypted(secrets_pa, PAGE_SIZE);
>> +    if (!pdata->layout) {
>> +        pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
>
> Maybe "Failed to map SNP secrets page\n" ? Not sure where the AP jump table came in on this...
>
>> +        goto e_free_pdata;
>> +    }
>> +
>> +    ret = -ENOMEM;
>> +    /* Allocate the shared page used for the request and response message. */
>> +    pdata->request = alloc_shared_pages(sizeof(struct snp_guest_msg));
>> +    if (!pdata->request)
>> +        goto e_unmap;
>> +
>> +    pdata->response = alloc_shared_pages(sizeof(struct snp_guest_msg));
>> +    if (!pdata->response)
>> +        goto e_free_request;
>> +
>> +    /* initial the input address for guest request */
>> +    pdata->input.req_gpa = __pa(pdata->request);
>> +    pdata->input.resp_gpa = __pa(pdata->response);
>> +    platform_data = pdata;
>> +
>> +create_ctx:
>> +    ret = -EIO;
>> +    snp_dev->ctx = snp_init_crypto(snp_dev->vmpck_id);
>> +    if (!snp_dev->ctx) {
>> +        pr_err("SNP init crypto failed\n");
>
> Maybe "SNP crypto context initialization failed\n" ?
>
>> +        platform_data = NULL;
>> +        goto e_free_response;
>> +    }
>> +
>> +    snp_dev->pdata = platform_data;
>
> Add a blank line here.
>
>> +    return 0;
>> +
>> +e_free_response:
>> +    free_shared_pages(pdata->response, sizeof(struct snp_guest_msg));
>> +e_free_request:
>> +    free_shared_pages(pdata->request, sizeof(struct snp_guest_msg));
>> +e_unmap:
>> +    iounmap(pdata->layout);
>> +e_free_pdata:
>> +    kfree(pdata);
>> +
>> +    return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(snp_setup_psp_messaging);
>> +
>> +static int __enc_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
>> +             void *plaintext, size_t len)
>> +{
>> +    struct snp_guest_msg_hdr *hdr = &msg->hdr;
>> +    u8 iv[GCM_AES_IV_SIZE] = {};
>> +
>> +    if (WARN_ON((hdr->msg_sz + ctx->authsize) > sizeof(msg->payload)))
>> +        return -EBADMSG;
>> +
>> +    memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> +    aesgcm_encrypt(ctx, msg->payload, plaintext, len, &hdr->algo, AAD_LEN,
>> +               iv, hdr->authtag);
>> +    return 0;
>> +}
>> +
>> +static int dec_payload(struct aesgcm_ctx *ctx, struct snp_guest_msg *msg,
>> +               void *plaintext, size_t len)
>> +{
>> +    struct snp_guest_msg_hdr *hdr = &msg->hdr;
>> +    u8 iv[GCM_AES_IV_SIZE] = {};
>> +
>> +    memcpy(iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
>> +    if (aesgcm_decrypt(ctx, plaintext, msg->payload, len, &hdr->algo,
>> +               AAD_LEN, iv, hdr->authtag))
>> +        return 0;
>> +    else
>> +        return -EBADMSG;
>> +}
>> +
>> +static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *guest_req,
>> +                  struct sev_guest_platform_data *pdata)
>> +{
>> +    struct snp_guest_msg *resp = &snp_dev->secret_response;
>> +    struct snp_guest_msg *req = &snp_dev->secret_request;
>> +    struct snp_guest_msg_hdr *req_hdr = &req->hdr;
>> +    struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
>> +    struct aesgcm_ctx *ctx = snp_dev->ctx;
>> +
>> +    pr_debug("response [seqno %lld type %d version %d sz %d]\n",
>> +         resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version,
>> +         resp_hdr->msg_sz);
>> +
>> +    /* Copy response from shared memory to encrypted memory. */
>> +    memcpy(resp, pdata->response, sizeof(*resp));
>> +
>> +    /* Verify that the sequence counter is incremented by 1 */
>> +    if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
>> +        return -EBADMSG;
>> +
>> +    /* Verify response message type and version number. */
>> +    if (resp_hdr->msg_type != (req_hdr->msg_type + 1) ||
>> +        resp_hdr->msg_version != req_hdr->msg_version)
>> +        return -EBADMSG;
>> +
>> +    /*
>> +     * If the message size is greater than our buffer length then return
>> +     * an error.
>> +     */
>> +    if (unlikely((resp_hdr->msg_sz + ctx->authsize) > guest_req->resp_sz))
>> +        return -EBADMSG;
>> +
>> +    return dec_payload(ctx, resp, guest_req->resp_buf, resp_hdr->msg_sz);
>> +}
>> +
>> +static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_guest_req *req)
>> +{
>> +    struct snp_guest_msg *msg = &snp_dev->secret_request;
>> +    struct snp_guest_msg_hdr *hdr = &msg->hdr;
>> +
>> +    memset(msg, 0, sizeof(*msg));
>> +
>> +    hdr->algo = SNP_AEAD_AES_256_GCM;
>> +    hdr->hdr_version = MSG_HDR_VER;
>> +    hdr->hdr_sz = sizeof(*hdr);
>> +    hdr->msg_type = req->msg_type;
>> +    hdr->msg_version = req->msg_version;
>> +    hdr->msg_seqno = seqno;
>> +    hdr->msg_vmpck = req->vmpck_id;
>> +    hdr->msg_sz = req->req_sz;
>> +
>> +    /* Verify the sequence number is non-zero */
>> +    if (!hdr->msg_seqno)
>> +        return -ENOSR;
>> +
>> +    pr_debug("request [seqno %lld type %d version %d sz %d]\n",
>> +         hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
>> +
>> +    return __enc_payload(snp_dev->ctx, msg, req->req_buf, req->req_sz);
>> +}
>> +
>> +static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
>> +                   struct snp_guest_request_ioctl *rio);
>
> Could all of these routines been moved down closer to the bottom of the file to avoid this forward declaration?

Looks possible, I will try it out.

>
>> +
>> +static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
>> +                  struct snp_guest_request_ioctl *rio,
>> +                  struct sev_guest_platform_data *pdata)
>> +{
>> +    unsigned long req_start = jiffies;
>> +    unsigned int override_npages = 0;
>> +    u64 override_err = 0;
>> +    int rc;
>> +
>
> ...
>
>>   -e_free_ctx:
>> -    kfree(snp_dev->ctx);
>>   e_free_cert_data:
>>       free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
>> -e_free_response:
>> -    free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
>> -e_free_request:
>> -    free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
>> -e_unmap:
>> -    iounmap(mapping);
>> + e_free_ctx:
>> +    kfree(snp_dev->ctx);
>> +e_free_snpdev:
>> +    kfree(snp_dev);
>>       return ret;
>>   }
>>   @@ -780,11 +332,9 @@ static int __exit sev_guest_remove(struct platform_device *pdev)
>>   {
>>       struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev);
>>   -    free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
>
> Looks like this one should still be here, right?

Yes, this should still be there.

>
> Thanks,
> Tom

Regards
Nikunj

2023-11-02 04:34:03

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 08/14] x86/mm: Add generic guest initialization hook

On 10/30/2023 10:53 PM, Dave Hansen wrote:
> On 10/29/23 23:36, Nikunj A Dadhania wrote:
>> diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
>> index a37ebd3b4773..a07985a96ca5 100644
>> --- a/arch/x86/kernel/x86_init.c
>> +++ b/arch/x86/kernel/x86_init.c
>> @@ -136,6 +136,7 @@ static bool enc_status_change_finish_noop(unsigned long vaddr, int npages, bool
>> static bool enc_tlb_flush_required_noop(bool enc) { return false; }
>> static bool enc_cache_flush_required_noop(void) { return false; }
>> static bool is_private_mmio_noop(u64 addr) {return false; }
>> +static void enc_init_noop(void) { }
>>
>> struct x86_platform_ops x86_platform __ro_after_init = {
>> .calibrate_cpu = native_calibrate_cpu_early,
>> @@ -158,6 +159,7 @@ struct x86_platform_ops x86_platform __ro_after_init = {
>> .enc_status_change_finish = enc_status_change_finish_noop,
>> .enc_tlb_flush_required = enc_tlb_flush_required_noop,
>> .enc_cache_flush_required = enc_cache_flush_required_noop,
>> + .enc_init = enc_init_noop,
>> },
>> };
>>
>> diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
>> index 9f27e14e185f..01abecc9a774 100644
>> --- a/arch/x86/mm/mem_encrypt.c
>> +++ b/arch/x86/mm/mem_encrypt.c
>> @@ -84,5 +84,8 @@ void __init mem_encrypt_init(void)
>> /* Call into SWIOTLB to update the SWIOTLB DMA buffers */
>> swiotlb_update_mem_attributes();
>>
>> + if (x86_platform.guest.enc_init)
>> + x86_platform.guest.enc_init();
>> +
>> print_mem_encrypt_feature_info();
>> }
>
> How does '.enc_init' ever get set to NULL? Isn't the point of having
> and using a 'noop' function so that you don't have to do NULL checks?

True, I will remove the check.

Regards
Nikunj

2023-11-02 05:09:08

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 08/14] x86/mm: Add generic guest initialization hook

On 10/31/2023 12:49 AM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> Add generic enc_init guest hook for performing any type of
>> initialization that is vendor specific.
>
> I think this commit message should be expanded on a bit... like when it is intended to be called, etc.

Sure

Regards
Nikunj

2023-11-02 05:14:11

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 10/30/2023 10:16 PM, Dionna Amalie Glaze wrote:
> On Sun, Oct 29, 2023 at 11:38 PM Nikunj A Dadhania <[email protected]> wrote:
>>

>> @@ -1393,6 +1397,78 @@ bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
>> }
>> EXPORT_SYMBOL_GPL(snp_assign_vmpck);
>>
>> +static struct snp_guest_dev tsc_snp_dev __initdata;
>> +
>> +static int __init snp_get_tsc_info(void)
>> +{
>> + static u8 buf[SNP_TSC_INFO_REQ_SZ + AUTHTAG_LEN];
>> + struct snp_guest_request_ioctl rio;
>> + struct snp_tsc_info_resp tsc_resp;
>> + struct snp_tsc_info_req tsc_req;
>> + struct snp_guest_req req;
>> + int rc, resp_len;
>> +
>> + /*
>> + * The intermediate response buffer is used while decrypting the
>> + * response payload. Make sure that it has enough space to cover the
>> + * authtag.
>> + */
>> + resp_len = sizeof(tsc_resp) + AUTHTAG_LEN;
>> + if (sizeof(buf) < resp_len)
>> + return -EINVAL;
>> +
>> + memset(&tsc_req, 0, sizeof(tsc_req));
>> + memset(&req, 0, sizeof(req));
>> + memset(&rio, 0, sizeof(rio));
>> + memset(buf, 0, sizeof(buf));
>> +
>> + if (!snp_assign_vmpck(&tsc_snp_dev, 0))
>> + return -EINVAL;
>> +
>
> I don't see a requirement for VMPL0 in the API docs. I just see "When
> a guest creates its own VMSA, it must query the PSP for information
> with the TSC_INFO message to determine the correct values to write
> into GUEST_TSC_SCALE and GUEST_TSC_OFFSET".

The request should work irrespective of the VMPL level.

> In that case, I don't see
> a particular use for this request in Linux. I would expect it either
> in the UEFI or in SVSM. Is this code path explicitly for direct boot
> to Linux? If so, did I miss that documentation in this patch series?

This works with UEFI boot. I havent tried this with SVSM yet.

Thanks
Nikunj

2023-11-02 05:37:56

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 10/31/2023 1:56 AM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> Add support for Secure TSC in SNP enabled guests. Secure TSC allows
>> guest to securely use RDTSC/RDTSCP instructions as the parameters
>> being used cannot be changed by hypervisor once the guest is launched.
>>
>> During the boot-up of the secondary cpus, SecureTSC enabled guests
>> need to query TSC info from AMD Security Processor. This communication
>> channel is encrypted between the AMD Security Processor and the guest,
>> the hypervisor is just the conduit to deliver the guest messages to
>> the AMD Security Processor. Each message is protected with an
>> AEAD (AES-256 GCM). Use minimal AES GCM library to encrypt/decrypt SNP
>> Guest messages to communicate with the PSP.
>
> Add to this commit message that you're using the enc_init hook to perform some Secure TSC initialization and why you have to do that.

Sure, will add.

>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---
>>   arch/x86/coco/core.c             |  3 ++
>>   arch/x86/include/asm/sev-guest.h | 18 +++++++
>>   arch/x86/include/asm/sev.h       |  2 +
>>   arch/x86/include/asm/svm.h       |  6 ++-
>>   arch/x86/kernel/sev.c            | 82 ++++++++++++++++++++++++++++++++
>>   arch/x86/mm/mem_encrypt_amd.c    |  6 +++
>>   include/linux/cc_platform.h      |  8 ++++
>>   7 files changed, 123 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
>> index eeec9986570e..5d5d4d03c543 100644
>> --- a/arch/x86/coco/core.c
>> +++ b/arch/x86/coco/core.c
>> @@ -89,6 +89,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
>>       case CC_ATTR_GUEST_SEV_SNP:
>>           return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
>>   +    case CC_ATTR_GUEST_SECURE_TSC:
>> +        return sev_status & MSR_AMD64_SNP_SECURE_TSC;
>> +
>>       default:
>>           return false;
>>       }
>> diff --git a/arch/x86/include/asm/sev-guest.h b/arch/x86/include/asm/sev-guest.h
>> index e6f94208173d..58739173eba9 100644
>> --- a/arch/x86/include/asm/sev-guest.h
>> +++ b/arch/x86/include/asm/sev-guest.h
>> @@ -39,6 +39,8 @@ enum msg_type {
>>       SNP_MSG_ABSORB_RSP,
>>       SNP_MSG_VMRK_REQ,
>>       SNP_MSG_VMRK_RSP,
>> +    SNP_MSG_TSC_INFO_REQ = 17,
>> +    SNP_MSG_TSC_INFO_RSP,
>>         SNP_MSG_TYPE_MAX
>>   };
>> @@ -111,6 +113,22 @@ struct snp_guest_req {
>>       u8 msg_type;
>>   };
>>   +struct snp_tsc_info_req {
>> +#define SNP_TSC_INFO_REQ_SZ 128
>
> Please move this to before the struct definition.
>
>> +    /* Must be zero filled */
>> +    u8 rsvd[SNP_TSC_INFO_REQ_SZ];
>> +} __packed;
>> +
>> +struct snp_tsc_info_resp {
>> +    /* Status of TSC_INFO message */
>> +    u32 status;
>> +    u32 rsvd1;
>> +    u64 tsc_scale;
>> +    u64 tsc_offset;
>> +    u32 tsc_factor;
>> +    u8 rsvd2[100];
>> +} __packed;
>> +
>>   int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev);
>>   int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
>>                  struct snp_guest_request_ioctl *rio);
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index 783150458864..038a5a15d937 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -200,6 +200,7 @@ void __init __noreturn snp_abort(void);
>>   void snp_accept_memory(phys_addr_t start, phys_addr_t end);
>>   u64 snp_get_unsupported_features(u64 status);
>>   u64 sev_get_status(void);
>> +void __init snp_secure_tsc_prepare(void);
>>   #else
>>   static inline void sev_es_ist_enter(struct pt_regs *regs) { }
>>   static inline void sev_es_ist_exit(void) { }
>> @@ -223,6 +224,7 @@ static inline void snp_abort(void) { }
>>   static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
>>   static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
>>   static inline u64 sev_get_status(void) { return 0; }
>> +static inline void __init snp_secure_tsc_prepare(void) { }
>>   #endif
>>     #endif
>> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
>> index 3ac0ffc4f3e2..ee35c0488f56 100644
>> --- a/arch/x86/include/asm/svm.h
>> +++ b/arch/x86/include/asm/svm.h
>> @@ -414,7 +414,9 @@ struct sev_es_save_area {
>>       u8 reserved_0x298[80];
>>       u32 pkru;
>>       u32 tsc_aux;
>> -    u8 reserved_0x2f0[24];
>> +    u64 tsc_scale;
>> +    u64 tsc_offset;
>> +    u8 reserved_0x300[8];
>>       u64 rcx;
>>       u64 rdx;
>>       u64 rbx;
>> @@ -546,7 +548,7 @@ static inline void __unused_size_checks(void)
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x1c0);
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x248);
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x298);
>> -    BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x2f0);
>> +    BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x300);
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x320);
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x380);
>>       BUILD_BUG_RESERVED_OFFSET(sev_es_save_area, 0x3f0);
>> diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
>> index fb3b1feb1b84..9468809d02c7 100644
>> --- a/arch/x86/kernel/sev.c
>> +++ b/arch/x86/kernel/sev.c
>> @@ -76,6 +76,10 @@ static u64 sev_hv_features __ro_after_init;
>>   /* Secrets page physical address from the CC blob */
>>   static u64 secrets_pa __ro_after_init;
>>   +/* Secure TSC values read using TSC_INFO SNP Guest request */
>> +static u64 guest_tsc_scale __ro_after_init;
>> +static u64 guest_tsc_offset __ro_after_init;
>
> s/guest_/snp_/
>
>> +
>>   /* #VC handler runtime per-CPU data */
>>   struct sev_es_runtime_data {
>>       struct ghcb ghcb_page;
>> @@ -1393,6 +1397,78 @@ bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
>>   }
>>   EXPORT_SYMBOL_GPL(snp_assign_vmpck);
>>   +static struct snp_guest_dev tsc_snp_dev __initdata;
>> +
>> +static int __init snp_get_tsc_info(void)
>> +{
>> +    static u8 buf[SNP_TSC_INFO_REQ_SZ + AUTHTAG_LEN];
>> +    struct snp_guest_request_ioctl rio;
>> +    struct snp_tsc_info_resp tsc_resp;
>> +    struct snp_tsc_info_req tsc_req;
>> +    struct snp_guest_req req;
>> +    int rc, resp_len;
>> +
>> +    /*
>> +     * The intermediate response buffer is used while decrypting the
>> +     * response payload. Make sure that it has enough space to cover the
>> +     * authtag.
>> +     */
>> +    resp_len = sizeof(tsc_resp) + AUTHTAG_LEN;
>> +    if (sizeof(buf) < resp_len)
>> +        return -EINVAL;
>> +
>> +    memset(&tsc_req, 0, sizeof(tsc_req));
>> +    memset(&req, 0, sizeof(req));
>> +    memset(&rio, 0, sizeof(rio));
>> +    memset(buf, 0, sizeof(buf));
>> +
>> +    if (!snp_assign_vmpck(&tsc_snp_dev, 0))
>> +        return -EINVAL;
>> +
>> +    /* Initialize the PSP channel to send snp messages */
>> +    if (snp_setup_psp_messaging(&tsc_snp_dev))
>> +        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>
> This should just return the non-zero return code from snp_setup_psp_messaging(), no?
>
>     rc = snp_setup_psp_messaging(&tsc_snp_dev);
>     if (rc)
>         return rc;

Yes, that will also have the same behaviour, snp_get_tsc_info() will send the termination request.

>> +
>> +    req.msg_version = MSG_HDR_VER;
>> +    req.msg_type = SNP_MSG_TSC_INFO_REQ;
>> +    req.vmpck_id = tsc_snp_dev.vmpck_id;
>> +    req.req_buf = &tsc_req;
>> +    req.req_sz = sizeof(tsc_req);
>> +    req.resp_buf = buf;
>> +    req.resp_sz = resp_len;
>> +    req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
>> +    rc = snp_send_guest_request(&tsc_snp_dev, &req, &rio);
>
> Aren't you supposed to hold a mutex before calling this since it will eventually call the message sequence number functions?

Yes, I will need to otherwise lockdep will complain. This is being called from boot processor, so there is no parallel execution.

>> +    if (rc)
>> +        goto err_req;
>> +
>> +    memcpy(&tsc_resp, buf, sizeof(tsc_resp));
>> +    pr_debug("%s: Valid response status %x scale %llx offset %llx factor %x\n",
>> +         __func__, tsc_resp.status, tsc_resp.tsc_scale, tsc_resp.tsc_offset,
>> +         tsc_resp.tsc_factor);
>> +
>> +    guest_tsc_scale = tsc_resp.tsc_scale;
>> +    guest_tsc_offset = tsc_resp.tsc_offset;
>> +
>> +err_req:
>> +    /* The response buffer contains the sensitive data, explicitly clear it. */
>> +    memzero_explicit(buf, sizeof(buf));
>> +    memzero_explicit(&tsc_resp, sizeof(tsc_resp));
>> +    memzero_explicit(&req, sizeof(req));
>> +
>> +    return rc;
>> +}
>> +
>> +void __init snp_secure_tsc_prepare(void)
>> +{
>> +    if (!cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>> +        return;
>> +
>> +    if (snp_get_tsc_info())
>> +        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>
> How about using SEV_TERM_SET_LINUX and a new GHCB_TERM_SECURE_TSC_INFO.

Yes, we can do that, I remember you had said this will required GHCB spec change and then thought of sticking with the current return code.

>
>> +
>> +    pr_debug("SecureTSC enabled\n");
>> +}
>> +
>>   static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
>>   {
>>       struct sev_es_save_area *cur_vmsa, *vmsa;
>> @@ -1493,6 +1569,12 @@ static int wakeup_cpu_via_vmgexit(int apic_id, unsigned long start_ip)
>>       vmsa->vmpl        = 0;
>>       vmsa->sev_features    = sev_status >> 2;
>>   +    /* Setting Secure TSC parameters */
>> +    if (cc_platform_has(CC_ATTR_GUEST_SECURE_TSC)) {
>> +        vmsa->tsc_scale = guest_tsc_scale;
>> +        vmsa->tsc_offset = guest_tsc_offset;
>> +    }
>> +
>>       /* Switch the page over to a VMSA page now that it is initialized */
>>       ret = snp_set_vmsa(vmsa, true);
>>       if (ret) {
>> diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
>> index 6faea41e99b6..9935fc506e99 100644
>> --- a/arch/x86/mm/mem_encrypt_amd.c
>> +++ b/arch/x86/mm/mem_encrypt_amd.c
>> @@ -215,6 +215,11 @@ void __init sme_map_bootdata(char *real_mode_data)
>>       __sme_early_map_unmap_mem(__va(cmdline_paddr), COMMAND_LINE_SIZE, true);
>>   }
>>   +void __init amd_enc_init(void)
>> +{
>> +    snp_secure_tsc_prepare();
>> +}
>> +
>>   void __init sev_setup_arch(void)
>>   {
>>       phys_addr_t total_mem = memblock_phys_mem_size();
>> @@ -502,6 +507,7 @@ void __init sme_early_init(void)
>>       x86_platform.guest.enc_status_change_finish  = amd_enc_status_change_finish;
>>       x86_platform.guest.enc_tlb_flush_required    = amd_enc_tlb_flush_required;
>>       x86_platform.guest.enc_cache_flush_required  = amd_enc_cache_flush_required;
>> +    x86_platform.guest.enc_init                  = amd_enc_init;
>>         /*
>>        * AMD-SEV-ES intercepts the RDMSR to read the X2APIC ID in the

Regards
Nikunj

2023-11-02 05:39:50

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 12/14] x86/kvmclock: Skip kvmclock when Secure TSC is available

On 10/31/2023 2:30 AM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> For AMD SNP guests having Secure TSC enabled, skip using the kvmclock.
>> The guest kernel will fallback and use Secure TSC based clocksource.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---
>>   arch/x86/kernel/kvmclock.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
>> index fb8f52149be9..779e7311fa6f 100644
>> --- a/arch/x86/kernel/kvmclock.c
>> +++ b/arch/x86/kernel/kvmclock.c
>> @@ -288,7 +288,7 @@ void __init kvmclock_init(void)
>>   {
>>       u8 flags;
>>   -    if (!kvm_para_available() || !kvmclock)
>> +    if (!kvm_para_available() || !kvmclock || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>
> And is setting X86_FEATURE_TSC_RELIABLE, as Dave Hansen suggests, enough
> to prevent usage of kvmclock?

No that wasn't sufficient. kvmclock was always selected before SecureTSC even when X86_FEATURE_TSC_RELIABLE was selected.

>
> There was a discussion here:
>  https://lore.kernel.org/lkml/[email protected]/
>
> Thanks,
> Tom

Regards
Nikunj

2023-11-02 05:42:35

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 10/31/2023 1:56 AM, Tom Lendacky wrote:
>> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
>> index cb0d6cd1c12f..e081ca4d5da2 100644
>> --- a/include/linux/cc_platform.h
>> +++ b/include/linux/cc_platform.h
>> @@ -90,6 +90,14 @@ enum cc_attr {
>>        * Examples include TDX Guest.
>>        */
>>       CC_ATTR_HOTPLUG_DISABLED,
>> +
>> +    /**
>> +     * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
>> +     *
>> +     * The platform/OS is running as a guest/virtual machine and actively
>> +     * using AMD SEV-SNP Secure TSC feature.
>
> I think TDX also has a secure TSC like feature, so can this be generic?

Yes, we can do that. In SNP case SecureTSC is an optional feature, not sure if that is the case for TDX as well.

Kirill any inputs ?

>
> Thanks,
> Tom
>
>> +     */
>> +    CC_ATTR_GUEST_SECURE_TSC,
>>   };
>>     #ifdef CONFIG_ARCH_HAS_CC_PLATFORM

Regards
Nikunj

2023-11-02 05:48:27

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 11/14] x86/sev: Prevent RDTSC/RDTSCP interception for Secure TSC enabled guests

On 10/31/2023 2:02 AM, Tom Lendacky wrote:
> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>> The hypervisor should not be intercepting RDTSC/RDTSCP when Secure TSC
>> is enabled. A #VC exception will be generated if the RDTSC/RDTSCP
>> instructions are being intercepted. If this should occur and Secure
>> TSC is enabled, terminate guest execution.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---
>>   arch/x86/kernel/sev-shared.c | 7 +++++++
>>   1 file changed, 7 insertions(+)
>>
>> diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
>> index ccb0915e84e1..833b0ae38f0b 100644
>> --- a/arch/x86/kernel/sev-shared.c
>> +++ b/arch/x86/kernel/sev-shared.c
>> @@ -991,6 +991,13 @@ static enum es_result vc_handle_rdtsc(struct ghcb *ghcb,
>>       bool rdtscp = (exit_code == SVM_EXIT_RDTSCP);
>>       enum es_result ret;
>>   +    /*
>> +     * RDTSC and RDTSCP should not be intercepted when Secure TSC is
>> +     * enabled. Terminate the SNP guest when the interception is enabled.
>> +     */
>> +    if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
>
> If you have to use sev_status, then please document why cc_platform_has() can't be used in the comment above.

Right, for sev-shared.c, cc_platform_has() is not available when compiling boot/compressed.

Regards
Nikunj

2023-11-02 05:54:14

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On 10/30/2023 10:48 PM, Dave Hansen wrote:
> On 10/29/23 23:36, Nikunj A Dadhania wrote:
> ...
>> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
>> index 15f97c0abc9d..b0a8546d3703 100644
>> --- a/arch/x86/kernel/tsc.c
>> +++ b/arch/x86/kernel/tsc.c
>> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
>> tsc_clocksource_reliable = 1;
>> }
>> #endif
>> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
>> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>> tsc_clocksource_reliable = 1;
>
> Why can't you just set X86_FEATURE_TSC_RELIABLE?

Last time when I tried, I had removed my kvmclock changes and I had set the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not select the SecureTSC.

Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for skipping kvmclock.

Regards
Nikunj

1: https://lore.kernel.org/lkml/[email protected]/

2023-11-02 10:33:39

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On Thu, Nov 02, 2023 at 11:23:34AM +0530, Nikunj A. Dadhania wrote:
> On 10/30/2023 10:48 PM, Dave Hansen wrote:
> > On 10/29/23 23:36, Nikunj A Dadhania wrote:
> > ...
> >> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
> >> index 15f97c0abc9d..b0a8546d3703 100644
> >> --- a/arch/x86/kernel/tsc.c
> >> +++ b/arch/x86/kernel/tsc.c
> >> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
> >> tsc_clocksource_reliable = 1;
> >> }
> >> #endif
> >> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
> >> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
> >> tsc_clocksource_reliable = 1;
> >
> > Why can't you just set X86_FEATURE_TSC_RELIABLE?
>
> Last time when I tried, I had removed my kvmclock changes and I had set
> the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not
> select the SecureTSC.
>
> Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for
> skipping kvmclock.

kvmclock lowers its rating if TSC is good enough:

if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) &&
boot_cpu_has(X86_FEATURE_NONSTOP_TSC) &&
!check_tsc_unstable())
kvm_clock.rating = 299;

Does your TSC meet the requirements?

--
Kiryl Shutsemau / Kirill A. Shutemov

2023-11-02 10:37:04

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On Thu, Nov 02, 2023 at 11:11:52AM +0530, Nikunj A. Dadhania wrote:
> On 10/31/2023 1:56 AM, Tom Lendacky wrote:
> >> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
> >> index cb0d6cd1c12f..e081ca4d5da2 100644
> >> --- a/include/linux/cc_platform.h
> >> +++ b/include/linux/cc_platform.h
> >> @@ -90,6 +90,14 @@ enum cc_attr {
> >> ?????? * Examples include TDX Guest.
> >> ?????? */
> >> ????? CC_ATTR_HOTPLUG_DISABLED,
> >> +
> >> +??? /**
> >> +???? * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
> >> +???? *
> >> +???? * The platform/OS is running as a guest/virtual machine and actively
> >> +???? * using AMD SEV-SNP Secure TSC feature.
> >
> > I think TDX also has a secure TSC like feature, so can this be generic?
>
> Yes, we can do that. In SNP case SecureTSC is an optional feature, not sure if that is the case for TDX as well.
>
> Kirill any inputs ?

We have several X86_FEATURE_ flags to indicate quality of TSC. Do we
really need a CC_ATTR on top of that? Maybe SEV code could just set
X86_FEATURE_ according to what its TSC can do?

--
Kiryl Shutsemau / Kirill A. Shutemov

2023-11-02 12:08:24

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On 11/2/2023 4:03 PM, Kirill A. Shutemov wrote:
> On Thu, Nov 02, 2023 at 11:23:34AM +0530, Nikunj A. Dadhania wrote:
>> On 10/30/2023 10:48 PM, Dave Hansen wrote:
>>> On 10/29/23 23:36, Nikunj A Dadhania wrote:
>>> ...
>>>> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
>>>> index 15f97c0abc9d..b0a8546d3703 100644
>>>> --- a/arch/x86/kernel/tsc.c
>>>> +++ b/arch/x86/kernel/tsc.c
>>>> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
>>>> tsc_clocksource_reliable = 1;
>>>> }
>>>> #endif
>>>> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
>>>> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>>>> tsc_clocksource_reliable = 1;
>>>
>>> Why can't you just set X86_FEATURE_TSC_RELIABLE?
>>
>> Last time when I tried, I had removed my kvmclock changes and I had set
>> the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not
>> select the SecureTSC.
>>
>> Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for
>> skipping kvmclock.
>
> kvmclock lowers its rating if TSC is good enough:
>
> if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) &&
> boot_cpu_has(X86_FEATURE_NONSTOP_TSC) &&
> !check_tsc_unstable())
> kvm_clock.rating = 299;
>
> Does your TSC meet the requirements?

I have set TscInvariant (bit 8) in CPUID_8000_0007_edx and TSC is set as reliable.

With this I see kvm_clock rating being lowered, but kvm-clock is still being picked as clock-source.

[ 0.000834] kvmclock_init: lowering kvm_clock rating
[ 0.002623] clocksource: kvm-clock: mask: 0xffffffffffffffff max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
[ 2.295082] clocksource: Switched to clocksource kvm-clock

Regards
Nikunj


2023-11-02 12:17:59

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On 11/2/2023 5:37 PM, Nikunj A. Dadhania wrote:
> On 11/2/2023 4:03 PM, Kirill A. Shutemov wrote:
>> On Thu, Nov 02, 2023 at 11:23:34AM +0530, Nikunj A. Dadhania wrote:
>>> On 10/30/2023 10:48 PM, Dave Hansen wrote:
>>>> On 10/29/23 23:36, Nikunj A Dadhania wrote:
>>>> ...
>>>>> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
>>>>> index 15f97c0abc9d..b0a8546d3703 100644
>>>>> --- a/arch/x86/kernel/tsc.c
>>>>> +++ b/arch/x86/kernel/tsc.c
>>>>> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
>>>>> tsc_clocksource_reliable = 1;
>>>>> }
>>>>> #endif
>>>>> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
>>>>> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>>>>> tsc_clocksource_reliable = 1;
>>>>
>>>> Why can't you just set X86_FEATURE_TSC_RELIABLE?
>>>
>>> Last time when I tried, I had removed my kvmclock changes and I had set
>>> the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not
>>> select the SecureTSC.
>>>
>>> Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for
>>> skipping kvmclock.
>>
>> kvmclock lowers its rating if TSC is good enough:
>>
>> if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) &&
>> boot_cpu_has(X86_FEATURE_NONSTOP_TSC) &&
>> !check_tsc_unstable())
>> kvm_clock.rating = 299;
>>
>> Does your TSC meet the requirements?
>
> I have set TscInvariant (bit 8) in CPUID_8000_0007_edx and TSC is set as reliable.
>
> With this I see kvm_clock rating being lowered, but kvm-clock is still being picked as clock-source.

Ah.. at later point TSC is picked up, is this expected ?

[ 2.564052] clocksource: Switched to clocksource kvm-clock
[ 2.678136] clocksource: Switched to clocksource tsc

Regards
Nikunj


2023-11-02 12:41:45

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On Thu, Nov 02, 2023 at 05:46:26PM +0530, Nikunj A. Dadhania wrote:
> On 11/2/2023 5:37 PM, Nikunj A. Dadhania wrote:
> > On 11/2/2023 4:03 PM, Kirill A. Shutemov wrote:
> >> On Thu, Nov 02, 2023 at 11:23:34AM +0530, Nikunj A. Dadhania wrote:
> >>> On 10/30/2023 10:48 PM, Dave Hansen wrote:
> >>>> On 10/29/23 23:36, Nikunj A Dadhania wrote:
> >>>> ...
> >>>>> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
> >>>>> index 15f97c0abc9d..b0a8546d3703 100644
> >>>>> --- a/arch/x86/kernel/tsc.c
> >>>>> +++ b/arch/x86/kernel/tsc.c
> >>>>> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
> >>>>> tsc_clocksource_reliable = 1;
> >>>>> }
> >>>>> #endif
> >>>>> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
> >>>>> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
> >>>>> tsc_clocksource_reliable = 1;
> >>>>
> >>>> Why can't you just set X86_FEATURE_TSC_RELIABLE?
> >>>
> >>> Last time when I tried, I had removed my kvmclock changes and I had set
> >>> the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not
> >>> select the SecureTSC.
> >>>
> >>> Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for
> >>> skipping kvmclock.
> >>
> >> kvmclock lowers its rating if TSC is good enough:
> >>
> >> if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) &&
> >> boot_cpu_has(X86_FEATURE_NONSTOP_TSC) &&
> >> !check_tsc_unstable())
> >> kvm_clock.rating = 299;
> >>
> >> Does your TSC meet the requirements?
> >
> > I have set TscInvariant (bit 8) in CPUID_8000_0007_edx and TSC is set as reliable.
> >
> > With this I see kvm_clock rating being lowered, but kvm-clock is still being picked as clock-source.
>
> Ah.. at later point TSC is picked up, is this expected ?
>
> [ 2.564052] clocksource: Switched to clocksource kvm-clock
> [ 2.678136] clocksource: Switched to clocksource tsc

On bare metal I see switch from tsc-early to tsc. tsc-early rating is
equal to kvmclock rating after it gets lowered.

Maybe kvmclock rating has to be even lower after detecting sane TSC?

Sean, Paolo, any comments?

--
Kiryl Shutsemau / Kirill A. Shutemov

2023-11-02 14:18:02

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 07/14] x86/sev: Move and reorganize sev guest request api

On 11/1/23 23:28, Nikunj A. Dadhania wrote:
> On 10/31/2023 12:46 AM, Tom Lendacky wrote:
>> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>>> For enabling Secure TSC, SEV-SNP guests need to communicate with the
>>> AMD Security Processor early during boot. Many of the required
>>> functions are implemented in the sev-guest driver and therefore not
>>> available at early boot. Move the required functions and provide an
>>> API to the driver to assign key and send guest request.
>>>
>>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>>> ---

>>> +static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
>>> +{
>>> +    u32 *os_area_msg_seqno = snp_get_os_area_msg_seqno(snp_dev->vmpck_id);
>>> +
>>> +    if (!os_area_msg_seqno) {
>>> +        pr_err("SNP unable to get message sequence counter\n");
>>> +        return;
>>> +    }
>>
>> I probably missed this in the other patch or even when the driver was first created, but shouldn't we have a lockdep_assert_held() here, too, before updating the count?
>
> As per the current code flow, snp_get_msg_seqno() is always called before snp_inc_msg_seqno(), maybe because of that the check wasnt there. It still makes sense to have a lockdep_assert_held() in snp_inc_msg_seqno().
>
> Should I add this change as a separate fix ?

It can be sent as a separate patch (I don't think it is a fix) either
before or after this series.

Thanks,
Tom

>
>>

2023-11-02 14:30:37

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 11/2/23 00:36, Nikunj A. Dadhania wrote:
> On 10/31/2023 1:56 AM, Tom Lendacky wrote:
>> On 10/30/23 01:36, Nikunj A Dadhania wrote:
>>> Add support for Secure TSC in SNP enabled guests. Secure TSC allows
>>> guest to securely use RDTSC/RDTSCP instructions as the parameters
>>> being used cannot be changed by hypervisor once the guest is launched.
>>>
>>> During the boot-up of the secondary cpus, SecureTSC enabled guests
>>> need to query TSC info from AMD Security Processor. This communication
>>> channel is encrypted between the AMD Security Processor and the guest,
>>> the hypervisor is just the conduit to deliver the guest messages to
>>> the AMD Security Processor. Each message is protected with an
>>> AEAD (AES-256 GCM). Use minimal AES GCM library to encrypt/decrypt SNP
>>> Guest messages to communicate with the PSP.
>>
>> Add to this commit message that you're using the enc_init hook to perform some Secure TSC initialization and why you have to do that.
>
> Sure, will add.
>
>>>
>>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>>> ---
>>>   arch/x86/coco/core.c             |  3 ++
>>>   arch/x86/include/asm/sev-guest.h | 18 +++++++
>>>   arch/x86/include/asm/sev.h       |  2 +
>>>   arch/x86/include/asm/svm.h       |  6 ++-
>>>   arch/x86/kernel/sev.c            | 82 ++++++++++++++++++++++++++++++++
>>>   arch/x86/mm/mem_encrypt_amd.c    |  6 +++
>>>   include/linux/cc_platform.h      |  8 ++++
>>>   7 files changed, 123 insertions(+), 2 deletions(-)
>>>

>>> +void __init snp_secure_tsc_prepare(void)
>>> +{
>>> +    if (!cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>>> +        return;
>>> +
>>> +    if (snp_get_tsc_info())
>>> +        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
>>
>> How about using SEV_TERM_SET_LINUX and a new GHCB_TERM_SECURE_TSC_INFO.
>
> Yes, we can do that, I remember you had said this will required GHCB spec change and then thought of sticking with the current return code.

No spec change needed. The base SNP support is already using it, so not an
issue to add a new error code.

Thanks,
Tom

2023-11-06 10:46:40

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On 11/2/2023 4:06 PM, Kirill A. Shutemov wrote:
> On Thu, Nov 02, 2023 at 11:11:52AM +0530, Nikunj A. Dadhania wrote:
>> On 10/31/2023 1:56 AM, Tom Lendacky wrote:
>>>> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
>>>> index cb0d6cd1c12f..e081ca4d5da2 100644
>>>> --- a/include/linux/cc_platform.h
>>>> +++ b/include/linux/cc_platform.h
>>>> @@ -90,6 +90,14 @@ enum cc_attr {
>>>>        * Examples include TDX Guest.
>>>>        */
>>>>       CC_ATTR_HOTPLUG_DISABLED,
>>>> +
>>>> +    /**
>>>> +     * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
>>>> +     *
>>>> +     * The platform/OS is running as a guest/virtual machine and actively
>>>> +     * using AMD SEV-SNP Secure TSC feature.
>>>
>>> I think TDX also has a secure TSC like feature, so can this be generic?
>>
>> Yes, we can do that. In SNP case SecureTSC is an optional feature, not sure if that is the case for TDX as well.
>>
>> Kirill any inputs ?
>
> We have several X86_FEATURE_ flags to indicate quality of TSC. Do we
> really need a CC_ATTR on top of that? Maybe SEV code could just set
> X86_FEATURE_ according to what its TSC can do?

For SEV-SNP, SEV_STATUS MSR has the information of various features
that have been enabled by the hypervisor. We will need a CC_ATTR for
these optional features.

Regards
Nikunj

2023-11-06 11:54:22

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On 11/2/2023 6:08 PM, Kirill A. Shutemov wrote:
> On Thu, Nov 02, 2023 at 05:46:26PM +0530, Nikunj A. Dadhania wrote:
>> On 11/2/2023 5:37 PM, Nikunj A. Dadhania wrote:
>>> On 11/2/2023 4:03 PM, Kirill A. Shutemov wrote:
>>>> On Thu, Nov 02, 2023 at 11:23:34AM +0530, Nikunj A. Dadhania wrote:
>>>>> On 10/30/2023 10:48 PM, Dave Hansen wrote:
>>>>>> On 10/29/23 23:36, Nikunj A Dadhania wrote:
>>>>>> ...
>>>>>>> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
>>>>>>> index 15f97c0abc9d..b0a8546d3703 100644
>>>>>>> --- a/arch/x86/kernel/tsc.c
>>>>>>> +++ b/arch/x86/kernel/tsc.c
>>>>>>> @@ -1241,7 +1241,7 @@ static void __init check_system_tsc_reliable(void)
>>>>>>> tsc_clocksource_reliable = 1;
>>>>>>> }
>>>>>>> #endif
>>>>>>> - if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
>>>>>>> + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE) || cc_platform_has(CC_ATTR_GUEST_SECURE_TSC))
>>>>>>> tsc_clocksource_reliable = 1;
>>>>>>
>>>>>> Why can't you just set X86_FEATURE_TSC_RELIABLE?
>>>>>
>>>>> Last time when I tried, I had removed my kvmclock changes and I had set
>>>>> the X86_FEATURE_TSC_RELIABLE similar to Kirill's patch[1], this did not
>>>>> select the SecureTSC.
>>>>>
>>>>> Let me try setting X86_FEATURE_TSC_RELIABLE and retaining my patch for
>>>>> skipping kvmclock.
>>>>
>>>> kvmclock lowers its rating if TSC is good enough:
>>>>
>>>> if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC) &&
>>>> boot_cpu_has(X86_FEATURE_NONSTOP_TSC) &&
>>>> !check_tsc_unstable())
>>>> kvm_clock.rating = 299;
>>>>
>>>> Does your TSC meet the requirements?
>>>
>>> I have set TscInvariant (bit 8) in CPUID_8000_0007_edx and TSC is set as reliable.
>>>
>>> With this I see kvm_clock rating being lowered, but kvm-clock is still being picked as clock-source.
>>
>> Ah.. at later point TSC is picked up, is this expected ?
>>
>> [ 2.564052] clocksource: Switched to clocksource kvm-clock
>> [ 2.678136] clocksource: Switched to clocksource tsc
>
> On bare metal I see switch from tsc-early to tsc. tsc-early rating is
> equal to kvmclock rating after it gets lowered.

For SNP guest with secure tsc enabled, kvm-clock and tsc-early both are at 299.
Initially, kvm-clock is selected as clocksource and when tsc with 300 rating is enqueued,
clocksource then switches to tsc.

[ 0.004231] clocksource: clocksource_enqueue: name kvm-clock rating 299
[...]
[ 2.046319] clocksource: clocksource_enqueue: name tsc-early rating 299
[...]
[ 3.399179] clocksource: Switched to clocksource kvm-clock
[...]
[ 3.513652] clocksource: clocksource_enqueue: name tsc rating 300
[ 3.517314] clocksource: Switched to clocksource tsc

> Maybe kvmclock rating has to be even lower after detecting sane TSC?

If I set kvmclock rating to 298, I do see exact behavior as you have seen on the bare-metal.

[ 0.004520] clocksource: clocksource_enqueue: name kvm-clock rating 298
[...]
[ 1.827422] clocksource: clocksource_enqueue: name tsc-early rating 299
[...]
[ 3.485059] clocksource: Switched to clocksource tsc-early
[...]
[ 3.623625] clocksource: clocksource_enqueue: name tsc rating 300
[ 3.628954] clocksource: Switched to clocksource tsc

Regards
Nikunj

2023-11-06 13:01:45

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH v5 09/14] x86/sev: Add Secure TSC support for SNP guests

On Mon, Nov 06, 2023 at 04:15:59PM +0530, Nikunj A. Dadhania wrote:
> On 11/2/2023 4:06 PM, Kirill A. Shutemov wrote:
> > On Thu, Nov 02, 2023 at 11:11:52AM +0530, Nikunj A. Dadhania wrote:
> >> On 10/31/2023 1:56 AM, Tom Lendacky wrote:
> >>>> diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
> >>>> index cb0d6cd1c12f..e081ca4d5da2 100644
> >>>> --- a/include/linux/cc_platform.h
> >>>> +++ b/include/linux/cc_platform.h
> >>>> @@ -90,6 +90,14 @@ enum cc_attr {
> >>>> ?????? * Examples include TDX Guest.
> >>>> ?????? */
> >>>> ????? CC_ATTR_HOTPLUG_DISABLED,
> >>>> +
> >>>> +??? /**
> >>>> +???? * @CC_ATTR_GUEST_SECURE_TSC: Secure TSC is active.
> >>>> +???? *
> >>>> +???? * The platform/OS is running as a guest/virtual machine and actively
> >>>> +???? * using AMD SEV-SNP Secure TSC feature.
> >>>
> >>> I think TDX also has a secure TSC like feature, so can this be generic?
> >>
> >> Yes, we can do that. In SNP case SecureTSC is an optional feature, not sure if that is the case for TDX as well.
> >>
> >> Kirill any inputs ?
> >
> > We have several X86_FEATURE_ flags to indicate quality of TSC. Do we
> > really need a CC_ATTR on top of that? Maybe SEV code could just set
> > X86_FEATURE_ according to what its TSC can do?
>
> For SEV-SNP, SEV_STATUS MSR has the information of various features
> that have been enabled by the hypervisor. We will need a CC_ATTR for
> these optional features.

If all users of the attribute is withing x86, I would rather add synthetic
X86_FEATURE_ flags than CC_ATTR_. We have better instrumentation around
features.

--
Kiryl Shutsemau / Kirill A. Shutemov

2023-11-06 13:04:28

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCH v5 13/14] x86/tsc: Mark Secure TSC as reliable clocksource

On Mon, Nov 06, 2023 at 05:23:44PM +0530, Nikunj A. Dadhania wrote:
> > Maybe kvmclock rating has to be even lower after detecting sane TSC?
>
> If I set kvmclock rating to 298, I do see exact behavior as you have seen on the bare-metal.
>
> [ 0.004520] clocksource: clocksource_enqueue: name kvm-clock rating 298
> [...]
> [ 1.827422] clocksource: clocksource_enqueue: name tsc-early rating 299
> [...]
> [ 3.485059] clocksource: Switched to clocksource tsc-early
> [...]
> [ 3.623625] clocksource: clocksource_enqueue: name tsc rating 300
> [ 3.628954] clocksource: Switched to clocksource tsc

This looks more reasonable to me. But I don't really understand
timekeeping. It would be nice to hear from someone who knows what he
saying.

--
Kiryl Shutsemau / Kirill A. Shutemov