2024-02-15 11:32:41

by Nikunj A. Dadhania

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

This patchset is also available at:

https://github.com/AMDESE/linux-kvm/tree/sectsc-guest-latest

and is based on tip/x86/sev base commit ee8ff8768735
("crypto: ccp - Have it depend on AMD_IOMMU")

Overview
--------

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-08: Preparation and movement of sev-guest driver code
09-16: SecureTSC enablement patches.

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

SecureTSC hypervisor patches based on top of SEV-SNP Guest MEMFD series:
https://github.com/AMDESE/linux-kvm/tree/sectsc-host-latest

QEMU changes:
https://github.com/nikunjad/qemu/tree/snp-securetsc-latest

QEMU commandline SEV-SNP with SecureTSC:

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

Changelog:
----------
v8:
* Rebased on top of tip/x86/sev
* Use minimum size of IV or msg_seqno in memcpy
* Use arch/x86/include/asm/sev.h instead of sev-guest.h
* Use DEFINE_MUTEX for snp_guest_cmd_mutex
* Added Reviewed-by from Tom.
* Dropped Tested-by from patch 3/16

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

v7:
* Drop mutex from the snp_dev and add snp_guest_cmd_{lock,unlock} API
* Added comments for secrets page failure
* Added define for maximum supported VMPCK
* Updated comments why sev_status is used directly instead of
cpu_feature_enabled()

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

v6: https://lore.kernel.org/lkml/[email protected]/
v5: https://lore.kernel.org/lkml/[email protected]/
v4: https://lore.kernel.org/lkml/[email protected]/
v3: https://lore.kernel.org/lkml/[email protected]/
v2: https://lore.kernel.org/r/[email protected]/
v1: https://lore.kernel.org/r/[email protected]

Nikunj A Dadhania (16):
virt: sev-guest: Use AES GCM crypto library
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
virt: sev-guest: Move SNP Guest command mutex
x86/sev: Move and reorganize sev guest request api
x86/mm: Add generic guest initialization hook
x86/cpufeatures: Add synthetic Secure TSC bit
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/sev: Mark Secure TSC as reliable
x86/cpu/amd: Do not print FW_BUG for Secure TSC
x86/sev: Enable Secure TSC for SNP guests

arch/x86/Kconfig | 1 +
arch/x86/boot/compressed/sev.c | 3 +-
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/sev-common.h | 1 +
arch/x86/include/asm/sev.h | 206 ++++++-
arch/x86/include/asm/svm.h | 6 +-
arch/x86/include/asm/x86_init.h | 2 +
arch/x86/kernel/cpu/amd.c | 3 +-
arch/x86/kernel/kvmclock.c | 2 +-
arch/x86/kernel/sev-shared.c | 10 +
arch/x86/kernel/sev.c | 648 +++++++++++++++++++--
arch/x86/kernel/x86_init.c | 2 +
arch/x86/mm/mem_encrypt.c | 12 +-
arch/x86/mm/mem_encrypt_amd.c | 12 +
drivers/virt/coco/sev-guest/Kconfig | 3 -
drivers/virt/coco/sev-guest/sev-guest.c | 725 ++++--------------------
drivers/virt/coco/sev-guest/sev-guest.h | 63 --
17 files changed, 937 insertions(+), 763 deletions(-)
delete mode 100644 drivers/virt/coco/sev-guest/sev-guest.h


base-commit: ee8ff8768735edc3e013837c4416f819543ddc17
--
2.34.1



2024-02-15 11:32:47

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 02/16] 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]>
Tested-by: Peter Gonda <[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 af35259bc584..01b565170729 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -178,8 +178,9 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
struct aesgcm_ctx *ctx = snp_dev->ctx;
u8 iv[GCM_AES_IV_SIZE] = {};

- 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));
@@ -232,8 +233,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);

if (WARN_ON((sz + ctx->authsize) > sizeof(req->payload)))
return -EBADMSG;
--
2.34.1


2024-02-15 11:33:17

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 03/16] 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.

Update snp_issue_guest_request() prototype to include the new guest request
structure and move the prototype to sev.h.

Signed-off-by: Nikunj A Dadhania <[email protected]>
---
arch/x86/include/asm/sev.h | 75 ++++++++-
arch/x86/kernel/sev.c | 15 +-
drivers/virt/coco/sev-guest/sev-guest.c | 195 +++++++++++++-----------
drivers/virt/coco/sev-guest/sev-guest.h | 66 --------
4 files changed, 187 insertions(+), 164 deletions(-)
delete mode 100644 drivers/virt/coco/sev-guest/sev-guest.h

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index bed95e1f4d52..0c0b11af9f89 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -111,8 +111,6 @@ struct rmp_state {
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 {
@@ -154,6 +152,73 @@ struct snp_secrets_page_layout {
u8 rsvd3[3840];
} __packed;

+#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 {
+ SNP_MSG_TYPE_INVALID = 0,
+ SNP_MSG_CPUID_REQ,
+ SNP_MSG_CPUID_RSP,
+ SNP_MSG_KEY_REQ,
+ SNP_MSG_KEY_RSP,
+ SNP_MSG_REPORT_REQ,
+ SNP_MSG_REPORT_RSP,
+ SNP_MSG_EXPORT_REQ,
+ SNP_MSG_EXPORT_RSP,
+ SNP_MSG_IMPORT_REQ,
+ SNP_MSG_IMPORT_RSP,
+ SNP_MSG_ABSORB_REQ,
+ SNP_MSG_ABSORB_RSP,
+ SNP_MSG_VMRK_REQ,
+ SNP_MSG_VMRK_RSP,
+
+ SNP_MSG_TYPE_MAX
+};
+
+enum aead_algo {
+ SNP_AEAD_INVALID,
+ SNP_AEAD_AES_256_GCM,
+};
+
+struct snp_guest_msg_hdr {
+ u8 authtag[MAX_AUTHTAG_LEN];
+ u64 msg_seqno;
+ u8 rsvd1[8];
+ u8 algo;
+ u8 hdr_version;
+ u16 hdr_sz;
+ u8 msg_type;
+ u8 msg_version;
+ u16 msg_sz;
+ u32 rsvd2;
+ u8 msg_vmpck;
+ u8 rsvd3[35];
+} __packed;
+
+struct snp_guest_msg {
+ struct snp_guest_msg_hdr hdr;
+ u8 payload[4000];
+} __packed;
+
+struct snp_guest_req {
+ void *req_buf;
+ size_t req_sz;
+
+ void *resp_buf;
+ size_t resp_sz;
+
+ void *data;
+ size_t data_npages;
+
+ u64 exit_code;
+ unsigned int vmpck_id;
+ u8 msg_version;
+ u8 msg_type;
+};
+
#ifdef CONFIG_AMD_MEM_ENCRYPT
extern void __sev_es_ist_enter(struct pt_regs *regs);
extern void __sev_es_ist_exit(void);
@@ -223,7 +288,8 @@ 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);
+int snp_issue_guest_request(struct snp_guest_req *req, 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);
@@ -248,7 +314,8 @@ 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)
+static inline int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
+ struct snp_guest_request_ioctl *rio)
{
return -ENOTTY;
}
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 1ef7ae806a01..479b68e00a54 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -2173,7 +2173,8 @@ 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;
@@ -2197,12 +2198,12 @@ 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);
+ if (req->exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
+ 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);
+ ret = sev_es_ghcb_hv_call(ghcb, &ctxt, req->exit_code, input->req_gpa, input->resp_gpa);
if (ret)
goto e_put;

@@ -2217,8 +2218,8 @@ 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);
+ if (req->exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
+ 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 01b565170729..596cec03f9eb 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -28,8 +28,6 @@
#include <asm/svm.h>
#include <asm/sev.h>

-#include "sev-guest.h"
-
#define DEVICE_NAME "sev-guest"

#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
@@ -169,65 +167,64 @@ static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
return ctx;
}

-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 *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 snp_guest_msg *resp_msg = &snp_dev->secret_response;
+ struct snp_guest_msg *req_msg = &snp_dev->secret_request;
+ struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
+ struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
struct aesgcm_ctx *ctx = snp_dev->ctx;
u8 iv[GCM_AES_IV_SIZE] = {};

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);
+ resp_msg_hdr->msg_seqno, resp_msg_hdr->msg_type, resp_msg_hdr->msg_version,
+ resp_msg_hdr->msg_sz);

/* Copy response from shared memory to encrypted memory. */
- memcpy(resp, snp_dev->response, sizeof(*resp));
+ memcpy(resp_msg, snp_dev->response, sizeof(*resp_msg));

/* Verify that the sequence counter is incremented by 1 */
- if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
+ if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_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)
+ if (resp_msg_hdr->msg_type != (req_msg_hdr->msg_type + 1) ||
+ resp_msg_hdr->msg_version != req_msg_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) > sz))
+ if (unlikely((resp_msg_hdr->msg_sz + ctx->authsize) > req->resp_sz))
return -EBADMSG;

/* Decrypt the payload */
- memcpy(iv, &resp_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_hdr->msg_seqno)));
- if (!aesgcm_decrypt(ctx, payload, resp->payload, resp_hdr->msg_sz,
- &resp_hdr->algo, AAD_LEN, iv, resp_hdr->authtag))
+ memcpy(iv, &resp_msg_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_msg_hdr->msg_seqno)));
+ if (!aesgcm_decrypt(ctx, req->resp_buf, resp_msg->payload, resp_msg_hdr->msg_sz,
+ &resp_msg_hdr->algo, AAD_LEN, iv, resp_msg_hdr->authtag))
return -EBADMSG;

return 0;
}

-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;
struct aesgcm_ctx *ctx = snp_dev->ctx;
u8 iv[GCM_AES_IV_SIZE] = {};

- 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)
@@ -236,17 +233,17 @@ 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);

- if (WARN_ON((sz + ctx->authsize) > sizeof(req->payload)))
+ if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
return -EBADMSG;

memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
- aesgcm_encrypt(ctx, req->payload, payload, sz, &hdr->algo, AAD_LEN,
- iv, hdr->authtag);
+ aesgcm_encrypt(ctx, msg->payload, req->req_buf, req->req_sz, &hdr->algo,
+ AAD_LEN, iv, hdr->authtag);

return 0;
}

-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;
@@ -261,7 +258,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:
/*
@@ -271,8 +268,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
@@ -327,15 +324,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;
@@ -349,7 +344,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;

@@ -360,7 +355,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))
@@ -369,12 +364,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);
@@ -391,8 +385,9 @@ struct snp_req_resp {

static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
- struct snp_report_req *req = &snp_dev->req.report;
- struct snp_report_resp *resp;
+ struct snp_report_req *report_req = &snp_dev->req.report;
+ struct snp_guest_req req = {0};
+ struct snp_report_resp *report_resp;
int rc, resp_len;

lockdep_assert_held(&snp_cmd_mutex);
@@ -400,7 +395,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
if (!arg->req_data || !arg->resp_data)
return -EINVAL;

- if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req)))
+ if (copy_from_user(report_req, (void __user *)arg->req_data, sizeof(*report_req)))
return -EFAULT;

/*
@@ -408,29 +403,37 @@ 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) + snp_dev->ctx->authsize;
- resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
- if (!resp)
+ resp_len = sizeof(report_resp->data) + snp_dev->ctx->authsize;
+ report_resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
+ if (!report_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);
+ req.msg_version = arg->msg_version;
+ req.msg_type = SNP_MSG_REPORT_REQ;
+ req.vmpck_id = vmpck_id;
+ req.req_buf = report_req;
+ req.req_sz = sizeof(*report_req);
+ req.resp_buf = report_resp->data;
+ req.resp_sz = resp_len;
+ req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
+
+ rc = snp_send_guest_request(snp_dev, &req, arg);
if (rc)
goto e_free;

- if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp)))
+ if (copy_to_user((void __user *)arg->resp_data, report_resp, sizeof(*report_resp)))
rc = -EFAULT;

e_free:
- kfree(resp);
+ kfree(report_resp);
return rc;
}

static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
{
- struct snp_derived_key_req *req = &snp_dev->req.derived_key;
- struct snp_derived_key_resp resp = {0};
+ struct snp_derived_key_req *derived_key_req = &snp_dev->req.derived_key;
+ struct snp_derived_key_resp derived_key_resp = {0};
+ struct snp_guest_req req = {0};
int rc, resp_len;
/* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
u8 buf[64 + 16];
@@ -445,25 +448,35 @@ 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) + snp_dev->ctx->authsize;
+ resp_len = sizeof(derived_key_resp.data) + snp_dev->ctx->authsize;
if (sizeof(buf) < resp_len)
return -ENOMEM;

- if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req)))
+ if (copy_from_user(derived_key_req, (void __user *)arg->req_data,
+ sizeof(*derived_key_req)))
return -EFAULT;

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

- memcpy(resp.data, buf, sizeof(resp.data));
- if (copy_to_user((void __user *)arg->resp_data, &resp, sizeof(resp)))
+ memcpy(derived_key_resp.data, buf, sizeof(derived_key_resp.data));
+ if (copy_to_user((void __user *)arg->resp_data, &derived_key_resp,
+ sizeof(derived_key_resp)))
rc = -EFAULT;

/* The response buffer contains the sensitive data, explicitly clear it. */
memzero_explicit(buf, sizeof(buf));
- memzero_explicit(&resp, sizeof(resp));
+ memzero_explicit(&derived_key_resp, sizeof(derived_key_resp));
return rc;
}

@@ -471,32 +484,33 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
struct snp_req_resp *io)

{
- struct snp_ext_report_req *req = &snp_dev->req.ext_report;
- struct snp_report_resp *resp;
- int ret, npages = 0, resp_len;
+ struct snp_ext_report_req *report_req = &snp_dev->req.ext_report;
+ struct snp_guest_req req = {0};
+ struct snp_report_resp *report_resp;
sockptr_t certs_address;
+ int ret, resp_len;

lockdep_assert_held(&snp_cmd_mutex);

if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data))
return -EINVAL;

- if (copy_from_sockptr(req, io->req_data, sizeof(*req)))
+ if (copy_from_sockptr(report_req, io->req_data, sizeof(*report_req)))
return -EFAULT;

/* caller does not want certificate data */
- if (!req->certs_len || !req->certs_address)
+ if (!report_req->certs_len || !report_req->certs_address)
goto cmd;

- if (req->certs_len > SEV_FW_BLOB_MAX_SIZE ||
- !IS_ALIGNED(req->certs_len, PAGE_SIZE))
+ if (report_req->certs_len > SEV_FW_BLOB_MAX_SIZE ||
+ !IS_ALIGNED(report_req->certs_len, PAGE_SIZE))
return -EINVAL;

if (sockptr_is_kernel(io->resp_data)) {
- certs_address = KERNEL_SOCKPTR((void *)req->certs_address);
+ certs_address = KERNEL_SOCKPTR((void *)report_req->certs_address);
} else {
- certs_address = USER_SOCKPTR((void __user *)req->certs_address);
- if (!access_ok(certs_address.user, req->certs_len))
+ certs_address = USER_SOCKPTR((void __user *)report_req->certs_address);
+ if (!access_ok(certs_address.user, report_req->certs_len))
return -EFAULT;
}

@@ -506,45 +520,53 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
* the host. If host does not supply any certs in it, then copy
* zeros to indicate that certificate data was not provided.
*/
- memset(snp_dev->certs_data, 0, req->certs_len);
- npages = req->certs_len >> PAGE_SHIFT;
+ memset(snp_dev->certs_data, 0, report_req->certs_len);
+ req.data_npages = report_req->certs_len >> PAGE_SHIFT;
cmd:
/*
* 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(resp->data) + snp_dev->ctx->authsize;
- resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
- if (!resp)
+ resp_len = sizeof(report_resp->data) + snp_dev->ctx->authsize;
+ report_resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
+ if (!report_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);
+ req.msg_version = arg->msg_version;
+ req.msg_type = SNP_MSG_REPORT_REQ;
+ req.vmpck_id = vmpck_id;
+ req.req_buf = &report_req->data;
+ req.req_sz = sizeof(report_req->data);
+ req.resp_buf = report_resp->data;
+ req.resp_sz = resp_len;
+ req.exit_code = SVM_VMGEXIT_EXT_GUEST_REQUEST;
+ req.data = snp_dev->certs_data;
+
+ ret = snp_send_guest_request(snp_dev, &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;
+ report_req->certs_len = req.data_npages << PAGE_SHIFT;

- if (copy_to_sockptr(io->req_data, req, sizeof(*req)))
+ if (copy_to_sockptr(io->req_data, report_req, sizeof(*report_req)))
ret = -EFAULT;
}

if (ret)
goto e_free;

- if (npages && copy_to_sockptr(certs_address, snp_dev->certs_data, req->certs_len)) {
+ if (req.data_npages && report_req->certs_len &&
+ copy_to_sockptr(certs_address, snp_dev->certs_data, report_req->certs_len)) {
ret = -EFAULT;
goto e_free;
}

- if (copy_to_sockptr(io->resp_data, resp, sizeof(*resp)))
+ if (copy_to_sockptr(io->resp_data, report_resp, sizeof(*report_resp)))
ret = -EFAULT;

e_free:
- kfree(resp);
+ kfree(report_resp);
return ret;
}

@@ -868,7 +890,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 = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type);
if (ret)
diff --git a/drivers/virt/coco/sev-guest/sev-guest.h b/drivers/virt/coco/sev-guest/sev-guest.h
deleted file mode 100644
index ceb798a404d6..000000000000
--- a/drivers/virt/coco/sev-guest/sev-guest.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2021 Advanced Micro Devices, Inc.
- *
- * Author: Brijesh Singh <[email protected]>
- *
- * SEV-SNP API spec is available at https://developer.amd.com/sev
- */
-
-#ifndef __VIRT_SEVGUEST_H__
-#define __VIRT_SEVGUEST_H__
-
-#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 {
- SNP_MSG_TYPE_INVALID = 0,
- SNP_MSG_CPUID_REQ,
- SNP_MSG_CPUID_RSP,
- SNP_MSG_KEY_REQ,
- SNP_MSG_KEY_RSP,
- SNP_MSG_REPORT_REQ,
- SNP_MSG_REPORT_RSP,
- SNP_MSG_EXPORT_REQ,
- SNP_MSG_EXPORT_RSP,
- SNP_MSG_IMPORT_REQ,
- SNP_MSG_IMPORT_RSP,
- SNP_MSG_ABSORB_REQ,
- SNP_MSG_ABSORB_RSP,
- SNP_MSG_VMRK_REQ,
- SNP_MSG_VMRK_RSP,
-
- SNP_MSG_TYPE_MAX
-};
-
-enum aead_algo {
- SNP_AEAD_INVALID,
- SNP_AEAD_AES_256_GCM,
-};
-
-struct snp_guest_msg_hdr {
- u8 authtag[MAX_AUTHTAG_LEN];
- u64 msg_seqno;
- u8 rsvd1[8];
- u8 algo;
- u8 hdr_version;
- u16 hdr_sz;
- u8 msg_type;
- u8 msg_version;
- u16 msg_sz;
- u32 rsvd2;
- u8 msg_vmpck;
- u8 rsvd3[35];
-} __packed;
-
-struct snp_guest_msg {
- struct snp_guest_msg_hdr hdr;
- u8 payload[4000];
-} __packed;
-
-#endif /* __VIRT_SEVGUEST_H__ */
--
2.34.1


2024-02-15 11:33:34

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 04/16] 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. Added define for maximum supported VMPCK.

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]>
Reviewed-by: Tom Lendacky <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/include/asm/sev.h | 1 +
drivers/virt/coco/sev-guest/sev-guest.c | 95 ++++++++++++-------------
2 files changed, 48 insertions(+), 48 deletions(-)

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 0c0b11af9f89..e4f52a606487 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -135,6 +135,7 @@ struct secrets_os_area {
} __packed;

#define VMPCK_KEY_LEN 32
+#define VMPCK_MAX_NUM 4

/* See the SNP spec version 0.9 for secrets page format */
struct snp_secrets_page_layout {
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 596cec03f9eb..646eb215f3c7 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -55,8 +55,7 @@ struct snp_guest_dev {
struct snp_derived_key_req derived_key;
struct snp_ext_report_req ext_report;
} req;
- u32 *os_area_msg_seqno;
- u8 *vmpck;
+ unsigned int vmpck_id;
};

static u32 vmpck_id;
@@ -66,14 +65,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);
}

/*
@@ -95,20 +102,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
*/
static void snp_disable_vmpck(struct snp_guest_dev *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;
+ u8 *key = snp_get_vmpck(snp_dev);
+
+ dev_alert(snp_dev->dev, "Disabling vmpck_id %u 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_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;
}
@@ -136,11 +145,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)
@@ -150,15 +161,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("VM communication key VMPCK%u is null\n", 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("Crypto context initialization failed\n");
kfree(ctx);
return NULL;
@@ -590,7 +608,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
mutex_lock(&snp_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_cmd_mutex);
return -ENOTTY;
@@ -667,32 +685,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)
+static bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
{
- u8 *key = NULL;
+ if (WARN_ON((vmpck_id + 1) > VMPCK_MAX_NUM))
+ 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;
}

struct snp_msg_report_resp_hdr {
@@ -728,7 +728,7 @@ static int sev_report_new(struct tsm_report *report, void *data)
guard(mutex)(&snp_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");
return -ENOTTY;
}
@@ -848,21 +848,20 @@ 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) {
- dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
+ snp_dev->layout = layout;
+ if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
+ dev_err(dev, "invalid vmpck id %u\n", vmpck_id);
goto e_unmap;
}

/* Verify that VMPCK is not zero. */
- if (is_vmpck_empty(snp_dev)) {
- dev_err(dev, "vmpck id %d is null\n", vmpck_id);
+ if (snp_is_vmpck_empty(snp_dev)) {
+ dev_err(dev, "vmpck id %u is null\n", vmpck_id);
goto e_unmap;
}

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));
@@ -878,7 +877,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;

@@ -903,7 +902,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
if (ret)
goto e_free_ctx;

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

e_free_ctx:
--
2.34.1


2024-02-15 11:33:47

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 05/16] 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]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/kernel/sev.c | 54 +++++++++++++++----------------------------
1 file changed, 18 insertions(+), 36 deletions(-)

diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 479b68e00a54..eda43c35a9f2 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -71,6 +71,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;
@@ -597,45 +600,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;
@@ -2088,6 +2062,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;
@@ -2099,6 +2079,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,16 @@ 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) {
+ pr_err("SNP secrets page not found\n");
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


2024-02-15 11:34:41

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 07/16] 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
API to the sev guest driver for sending guest message and vmpck
routines.

As there is no external caller for snp_issue_guest_request() anymore,
make it static and drop the prototype from the header.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/sev.h | 111 +++++-
arch/x86/kernel/sev.c | 459 ++++++++++++++++++++++-
drivers/virt/coco/sev-guest/Kconfig | 1 -
drivers/virt/coco/sev-guest/sev-guest.c | 476 ++----------------------
5 files changed, 561 insertions(+), 487 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 58d3593bc4f2..200fcf68b1e4 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1534,6 +1534,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.h b/arch/x86/include/asm/sev.h
index 8578b33d8fc4..d950a3ac5694 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -10,11 +10,13 @@

#include <linux/types.h>
#include <linux/sev-guest.h>
+#include <linux/miscdevice.h>

#include <asm/insn.h>
#include <asm/sev-common.h>
#include <asm/bootparam.h>
#include <asm/coco.h>
+#include <asm/set_memory.h>

#define GHCB_PROTOCOL_MIN 1ULL
#define GHCB_PROTOCOL_MAX 2ULL
@@ -107,16 +109,6 @@ struct rmp_state {

#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
@@ -153,6 +145,9 @@ struct snp_secrets_page_layout {
u8 rsvd3[3840];
} __packed;

+#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
+#define SNP_REQ_RETRY_DELAY (2*HZ)
+
#define MAX_AUTHTAG_LEN 32
#define AUTHTAG_LEN 16
#define AAD_LEN 48
@@ -199,11 +194,49 @@ 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;
+ struct snp_guest_msg *response;
+
+ struct snp_secrets_page_layout *layout;
+ struct snp_req_data input;
+};
+
+struct snp_guest_dev {
+ struct device *dev;
+ struct miscdevice misc;
+
+ 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;
+ struct snp_guest_msg secret_response;
+
+ struct sev_guest_platform_data *pdata;
+ union {
+ struct snp_report_req report;
+ struct snp_derived_key_req derived_key;
+ struct snp_ext_report_req ext_report;
+ } req;
+ unsigned int vmpck_id;
+};
+
struct snp_guest_req {
void *req_buf;
size_t req_sz;
@@ -289,14 +322,54 @@ 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(struct snp_guest_req *req, 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);
void kdump_sev_callback(void);
void snp_guest_cmd_lock(void);
void snp_guest_cmd_unlock(void);
+bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id);
+bool snp_is_vmpck_empty(unsigned int vmpck_id);
+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);
+
+static inline 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 inline 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);
+}
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
@@ -317,18 +390,20 @@ 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(struct snp_guest_req *req, 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; }
static inline void kdump_sev_callback(void) { }
static inline void snp_guest_cmd_lock(void) { }
static inline void snp_guest_cmd_unlock(void) { }
+static inline bool snp_assign_vmpck(struct snp_guest_dev *dev,
+ unsigned int vmpck_id) { return false; }
+static inline bool snp_is_vmpck_empty(unsigned int vmpck_id) { return true; }
+static inline int snp_setup_psp_messaging(struct snp_guest_dev *snp_dev) { return 0; }
+static inline int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio) { return 0; }
+static inline void free_shared_pages(void *buf, size_t sz) { }
+static inline void *alloc_shared_pages(size_t sz) { return NULL; }
#endif

#ifdef CONFIG_KVM_AMD_SEV
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index bc4a705d989c..a9c1efd6d4e3 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>
@@ -2170,8 +2171,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;
@@ -2233,7 +2234,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",
@@ -2242,20 +2242,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) {
- pr_err("SNP secrets page not found\n");
- 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;

@@ -2273,3 +2262,445 @@ void kdump_sev_callback(void)
if (cpu_feature_enabled(X86_FEATURE_SEV_SNP))
wbinvd();
}
+
+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 %u 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;
+ }
+
+ /* 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("VM communication key VMPCK%u 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("Crypto context initialization 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 secrets page not found\n");
+ return -ENODEV;
+ }
+
+ pdata = kzalloc(sizeof(struct sev_guest_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("Allocation of SNP guest platform data failed\n");
+ return -ENOMEM;
+ }
+
+ ret = -ENOMEM;
+ pdata->layout = (__force void *)ioremap_encrypted(secrets_pa, PAGE_SIZE);
+ if (!pdata->layout) {
+ pr_err("Failed to map SNP secrets page.\n");
+ goto e_free_pdata;
+ }
+
+ /* 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;
+
+ /*
+ * Initialize snp command mutex that is used to serialize the shared
+ * buffer access and use of the vmpck and message sequence number
+ */
+ mutex_init(&snp_guest_cmd_mutex);
+
+ /* 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 crypto context initialization 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 verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct sev_guest_platform_data *pdata)
+{
+ struct snp_guest_msg *resp_msg = &snp_dev->secret_response;
+ struct snp_guest_msg *req_msg = &snp_dev->secret_request;
+ struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
+ struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
+ struct aesgcm_ctx *ctx = snp_dev->ctx;
+ u8 iv[GCM_AES_IV_SIZE] = {};
+
+ pr_debug("response [seqno %lld type %d version %d sz %d]\n",
+ resp_msg_hdr->msg_seqno, resp_msg_hdr->msg_type, resp_msg_hdr->msg_version,
+ resp_msg_hdr->msg_sz);
+
+ /* Copy response from shared memory to encrypted memory. */
+ memcpy(resp_msg, pdata->response, sizeof(*resp_msg));
+
+ /* Verify that the sequence counter is incremented by 1 */
+ if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_hdr->msg_seqno + 1)))
+ return -EBADMSG;
+
+ /* Verify response message type and version number. */
+ if (resp_msg_hdr->msg_type != (req_msg_hdr->msg_type + 1) ||
+ resp_msg_hdr->msg_version != req_msg_hdr->msg_version)
+ return -EBADMSG;
+
+ /*
+ * If the message size is greater than our buffer length then return
+ * an error.
+ */
+ if (unlikely((resp_msg_hdr->msg_sz + ctx->authsize) > req->resp_sz))
+ return -EBADMSG;
+
+ /* Decrypt the payload */
+ memcpy(iv, &resp_msg_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_msg_hdr->msg_seqno)));
+ if (!aesgcm_decrypt(ctx, req->resp_buf, resp_msg->payload, resp_msg_hdr->msg_sz,
+ &resp_msg_hdr->algo, AAD_LEN, iv, resp_msg_hdr->authtag))
+ return -EBADMSG;
+
+ return 0;
+}
+
+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;
+ struct aesgcm_ctx *ctx = snp_dev->ctx;
+ u8 iv[GCM_AES_IV_SIZE] = {};
+
+ 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);
+
+ if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
+ return -EBADMSG;
+
+ memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
+ aesgcm_encrypt(ctx, msg->payload, req->req_buf, req->req_sz, &hdr->algo,
+ AAD_LEN, iv, hdr->authtag);
+
+ return 0;
+}
+
+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;
+
+ lockdep_assert_held(&snp_guest_cmd_mutex);
+
+ 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 + 1) > VMPCK_MAX_NUM))
+ return false;
+
+ dev->vmpck_id = vmpck_id;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snp_assign_vmpck);
diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig
index 0b772bd921d8..a6405ab6c2c3 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
select TSM_REPORTS
help
SEV-SNP firmware provides the guest a mechanism to communicate with
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index ba9ffaee647c..dc078fdb9039 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -30,125 +30,10 @@

#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;
-
- 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;
- union {
- struct snp_report_req report;
- struct snp_derived_key_req derived_key;
- struct snp_ext_report_req ext_report;
- } req;
- 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.");

-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 %u 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;
-
- /* 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,241 +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("VM communication key VMPCK%u is null\n", 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("Crypto context initialization failed\n");
- kfree(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *req)
-{
- struct snp_guest_msg *resp_msg = &snp_dev->secret_response;
- struct snp_guest_msg *req_msg = &snp_dev->secret_request;
- struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
- struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
- struct aesgcm_ctx *ctx = snp_dev->ctx;
- u8 iv[GCM_AES_IV_SIZE] = {};
-
- pr_debug("response [seqno %lld type %d version %d sz %d]\n",
- resp_msg_hdr->msg_seqno, resp_msg_hdr->msg_type, resp_msg_hdr->msg_version,
- resp_msg_hdr->msg_sz);
-
- /* Copy response from shared memory to encrypted memory. */
- memcpy(resp_msg, snp_dev->response, sizeof(*resp_msg));
-
- /* Verify that the sequence counter is incremented by 1 */
- if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_hdr->msg_seqno + 1)))
- return -EBADMSG;
-
- /* Verify response message type and version number. */
- if (resp_msg_hdr->msg_type != (req_msg_hdr->msg_type + 1) ||
- resp_msg_hdr->msg_version != req_msg_hdr->msg_version)
- return -EBADMSG;
-
- /*
- * If the message size is greater than our buffer length then return
- * an error.
- */
- if (unlikely((resp_msg_hdr->msg_sz + ctx->authsize) > req->resp_sz))
- return -EBADMSG;
-
- /* Decrypt the payload */
- memcpy(iv, &resp_msg_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_msg_hdr->msg_seqno)));
- if (!aesgcm_decrypt(ctx, req->resp_buf, resp_msg->payload, resp_msg_hdr->msg_sz,
- &resp_msg_hdr->algo, AAD_LEN, iv, resp_msg_hdr->authtag))
- return -EBADMSG;
-
- return 0;
-}
-
-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;
- struct aesgcm_ctx *ctx = snp_dev->ctx;
- u8 iv[GCM_AES_IV_SIZE] = {};
-
- 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);
-
- if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
- return -EBADMSG;
-
- memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
- aesgcm_encrypt(ctx, msg->payload, req->req_buf, req->req_sz, &hdr->algo,
- AAD_LEN, iv, hdr->authtag);
-
- return 0;
-}
-
-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;
-}
-
struct snp_req_resp {
sockptr_t req_data;
sockptr_t resp_data;
@@ -597,7 +247,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
snp_guest_cmd_lock();

/* 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");
snp_guest_cmd_unlock();
return -ENOTTY;
@@ -632,58 +282,11 @@ 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,
};

-static bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
-{
- if (WARN_ON((vmpck_id + 1) > VMPCK_MAX_NUM))
- return false;
-
- dev->vmpck_id = vmpck_id;
-
- return true;
-}
-
struct snp_msg_report_resp_hdr {
u32 status;
u32 report_size;
@@ -715,7 +318,7 @@ static int sev_report_new(struct tsm_report *report, void *data)
return -ENOMEM;

/* 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");
return -ENOTTY;
}
@@ -812,75 +415,44 @@ static void unregister_sev_tsm(void *data)

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 %u\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 %u 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 %u\n", snp_dev->vmpck_id);
+ ret = -ENODEV;
+ goto e_free_snpdev;
}

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);
- 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;
+ snp_dev->certs_data = alloc_shared_pages(SEV_FW_BLOB_MAX_SIZE);
+ if (!snp_dev->certs_data) {
+ ret = -ENOMEM;
+ 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 = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type);
if (ret)
goto e_free_cert_data;
@@ -891,21 +463,18 @@ static int __init sev_guest_probe(struct platform_device *pdev)

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 %u)\n", snp_dev->vmpck_id);

- dev_info(dev, "Initialized SEV guest driver (using vmpck_id %u)\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:
- 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;
}

@@ -914,10 +483,9 @@ static void __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);
}

/*
--
2.34.1


2024-02-15 11:34:58

by Nikunj A. Dadhania

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

Add generic enc_init guest hook for performing any type of initialization
that is vendor specific. Generic enc_init hook can be used for early guest
feature initialization before secondary processors are up.

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

diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index c878616a18b8..8095553e14a7 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 d035bce3a2b0..68aa06852466 100644
--- a/arch/x86/mm/mem_encrypt.c
+++ b/arch/x86/mm/mem_encrypt.c
@@ -89,6 +89,8 @@ void __init mem_encrypt_init(void)
/* Call into SWIOTLB to update the SWIOTLB DMA buffers */
swiotlb_update_mem_attributes();

+ x86_platform.guest.enc_init();
+
print_mem_encrypt_feature_info();
}

--
2.34.1


2024-02-15 11:35:02

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 09/16] x86/cpufeatures: Add synthetic Secure TSC bit

Add support for the synthetic CPUID flag which indicates that the SNP
guest is running with secure tsc enabled (MSR_AMD64_SEV Bit 11 -
SecureTsc_Enabled) . This flag is there so that this capability in the
guests can be detected easily without reading MSRs every time accessors.

Suggested-by: Kirill A. Shutemov <[email protected]>
Signed-off-by: Nikunj A Dadhania <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 0fa702673e73..24ac7fe97806 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -236,6 +236,7 @@
#define X86_FEATURE_PVUNLOCK ( 8*32+20) /* "" PV unlock function */
#define X86_FEATURE_VCPUPREEMPT ( 8*32+21) /* "" PV vcpu_is_preempted function */
#define X86_FEATURE_TDX_GUEST ( 8*32+22) /* Intel Trust Domain Extensions Guest */
+#define X86_FEATURE_SNP_SECURE_TSC ( 8*32+23) /* "" AMD SNP Secure TSC */

/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
--
2.34.1


2024-02-15 11:35:48

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 10/16] 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.

Use the guest enc_init hook to fetch SNP TSC info from the AMD Security
Processor and initialize the snp_tsc_scale and snp_tsc_offset. During
secondary CPU initialization set VMSA fields GUEST_TSC_SCALE (offset 2F0h)
and GUEST_TSC_OFFSET(offset 2F8h) with snp_tsc_scale and snp_tsc_offset
respectively.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/include/asm/sev-common.h | 1 +
arch/x86/include/asm/sev.h | 23 +++++++
arch/x86/include/asm/svm.h | 6 +-
arch/x86/kernel/sev.c | 107 ++++++++++++++++++++++++++++--
arch/x86/mm/mem_encrypt_amd.c | 6 ++
5 files changed, 134 insertions(+), 9 deletions(-)

diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
index b463fcbd4b90..6adc8e27feeb 100644
--- a/arch/x86/include/asm/sev-common.h
+++ b/arch/x86/include/asm/sev-common.h
@@ -159,6 +159,7 @@ struct snp_psc_desc {
#define GHCB_TERM_NOT_VMPL0 3 /* SNP guest is not running at VMPL-0 */
#define GHCB_TERM_CPUID 4 /* CPUID-validation failure */
#define GHCB_TERM_CPUID_HV 5 /* CPUID failure during hypervisor fallback */
+#define GHCB_TERM_SECURE_TSC 6 /* Secure TSC initialization failed */

#define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index d950a3ac5694..16bf5afa7731 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -170,6 +170,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
};
@@ -214,6 +216,23 @@ struct sev_guest_platform_data {
struct snp_req_data input;
};

+#define SNP_TSC_INFO_REQ_SZ 128
+
+struct snp_tsc_info_req {
+ /* 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;
+
struct snp_guest_dev {
struct device *dev;
struct miscdevice misc;
@@ -233,6 +252,7 @@ struct snp_guest_dev {
struct snp_report_req report;
struct snp_derived_key_req derived_key;
struct snp_ext_report_req ext_report;
+ struct snp_tsc_info_req tsc_info;
} req;
unsigned int vmpck_id;
};
@@ -370,6 +390,8 @@ static inline void *alloc_shared_pages(size_t sz)

return page_address(page);
}
+
+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) { }
@@ -404,6 +426,7 @@ static inline int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_g
struct snp_guest_request_ioctl *rio) { return 0; }
static inline void free_shared_pages(void *buf, size_t sz) { }
static inline void *alloc_shared_pages(size_t sz) { return NULL; }
+static inline void __init snp_secure_tsc_prepare(void) { }
#endif

#ifdef CONFIG_KVM_AMD_SEV
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 87a7b917d30e..3a8294bbd109 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -410,7 +410,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;
@@ -542,7 +544,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 a9c1efd6d4e3..20a1e50b7638 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -75,6 +75,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 snp_tsc_scale __ro_after_init;
+static u64 snp_tsc_offset __ro_after_init;
+
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
struct ghcb ghcb_page;
@@ -956,6 +960,83 @@ void snp_guest_cmd_unlock(void)
}
EXPORT_SYMBOL_GPL(snp_guest_cmd_unlock);

+static struct snp_guest_dev tsc_snp_dev __initdata;
+
+static int __snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio);
+
+static int __init snp_get_tsc_info(void)
+{
+ struct snp_tsc_info_req *tsc_req = &tsc_snp_dev.req.tsc_info;
+ 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_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 */
+ 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);
+ 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);
+
+ snp_tsc_scale = tsc_resp.tsc_scale;
+ snp_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 (!cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
+ return;
+
+ if (snp_get_tsc_info())
+ sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECURE_TSC);
+
+ pr_debug("SecureTSC enabled\n");
+}
+
static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip)
{
struct sev_es_save_area *cur_vmsa, *vmsa;
@@ -1056,6 +1137,12 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip)
vmsa->vmpl = 0;
vmsa->sev_features = sev_status >> 2;

+ /* Setting Secure TSC parameters */
+ if (cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC)) {
+ vmsa->tsc_scale = snp_tsc_scale;
+ vmsa->tsc_offset = snp_tsc_offset;
+ }
+
/* Switch the page over to a VMSA page now that it is initialized */
ret = snp_set_vmsa(vmsa, true);
if (ret) {
@@ -2638,18 +2725,13 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, struct snp_gues
return rc;
}

-int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
- struct snp_guest_request_ioctl *rio)
+static 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;
-
- lockdep_assert_held(&snp_guest_cmd_mutex);
-
pdata = snp_dev->pdata;

/* Get message sequence and verify that its a non-zero */
@@ -2692,6 +2774,17 @@ int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *

return 0;
}
+
+int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
+ struct snp_guest_request_ioctl *rio)
+{
+ if (!snp_dev || !snp_dev->pdata || !req || !rio)
+ return -ENODEV;
+
+ lockdep_assert_held(&snp_guest_cmd_mutex);
+
+ return __snp_send_guest_request(snp_dev, req, rio);
+}
EXPORT_SYMBOL_GPL(snp_send_guest_request);

bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index 70b91de2e053..c81b57ca03b6 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -214,6 +214,11 @@ void __init sme_map_bootdata(char *real_mode_data)
__sme_early_map_unmap_mem(__va(cmdline_paddr), COMMAND_LINE_SIZE, true);
}

+static void __init amd_enc_init(void)
+{
+ snp_secure_tsc_prepare();
+}
+
static unsigned long pg_level_to_pfn(int level, pte_t *kpte, pgprot_t *ret_prot)
{
unsigned long pfn = 0;
@@ -467,6 +472,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
--
2.34.1


2024-02-15 11:36:09

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 11/16] 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]>
Tested-by: Peter Gonda <[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 20a1e50b7638..64243c44a7d3 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -1279,6 +1279,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 && cpu_feature_enabled(X86_FEATURE_SNP_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


2024-02-15 11:36:29

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 12/16] 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]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/kernel/sev-shared.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
index ae79f9505298..26379c70878b 100644
--- a/arch/x86/kernel/sev-shared.c
+++ b/arch/x86/kernel/sev-shared.c
@@ -1000,6 +1000,16 @@ 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.
+ * This file is included from kernel/sev.c and boot/compressed/sev.c,
+ * use sev_status here as cpu_feature_enabled() is not available when
+ * compiling boot/compressed/sev.c.
+ */
+ 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


2024-02-15 11:36:38

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 13/16] 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]>
Tested-by: Peter Gonda <[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 5bb395551c44..855ef65dbc4a 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -289,7 +289,7 @@ void __init kvmclock_init(void)
{
u8 flags;

- if (!kvm_para_available() || !kvmclock)
+ if (!kvm_para_available() || !kvmclock || cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
return;

if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE2)) {
--
2.34.1


2024-02-15 11:46:18

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 14/16] x86/sev: Mark Secure TSC as reliable

AMD SNP guests may have Secure TSC feature enabled. Use the Secure TSC
as the only reliable clock source in SEV-SNP guests when enabled,
bypassing unstable calibration.

Signed-off-by: Nikunj A Dadhania <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/mm/mem_encrypt_amd.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index c81b57ca03b6..cc936999efc8 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -498,6 +498,10 @@ void __init sme_early_init(void)
*/
if (sev_status & MSR_AMD64_SEV_ENABLED)
ia32_disable();
+
+ /* Mark the TSC as reliable when Secure TSC is enabled */
+ if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
+ setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
}

void __init mem_encrypt_free_decrypted_mem(void)
--
2.34.1


2024-02-15 11:46:26

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 15/16] x86/cpu/amd: Do not print FW_BUG for Secure TSC

When SecureTSC is enabled and TscInvariant (bit 8) in CPUID_8000_0007_edx
is set, kernel complains with the below firmware bug:

[Firmware Bug]: TSC doesn't count with P0 frequency!

Secure TSC need not run at P0 frequency, the TSC frequency is set by the
VMM as part of the SNP_LAUNCH_START command. Avoid the check when Secure
TSC is enabled

Signed-off-by: Nikunj A Dadhania <[email protected]>
Tested-by: Peter Gonda <[email protected]>
---
arch/x86/kernel/cpu/amd.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index ce89281640a9..2e7d67e1f795 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -471,7 +471,8 @@ static void early_init_amd_mc(struct cpuinfo_x86 *c)

static void bsp_init_amd(struct cpuinfo_x86 *c)
{
- if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) {
+ if (cpu_has(c, X86_FEATURE_CONSTANT_TSC) &&
+ !cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC)) {

if (c->x86 > 0x10 ||
(c->x86 == 0x10 && c->x86_model >= 0x2)) {
--
2.34.1


2024-02-15 11:47:30

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 16/16] 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.

Set the CPUID feature bit (X86_FEATURE_SNP_SECURE_TSC) when SNP guest is
started with Secure TSC.

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

diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index 073291832f44..d7e28084333a 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -379,7 +379,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 68aa06852466..350ba605509d 100644
--- a/arch/x86/mm/mem_encrypt.c
+++ b/arch/x86/mm/mem_encrypt.c
@@ -70,8 +70,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 (cpu_feature_enabled(X86_FEATURE_SNP_SECURE_TSC))
+ pr_cont(" SECURE-TSC");
+ }

pr_cont("\n");
break;
diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index cc936999efc8..7ee0a537a22e 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -500,8 +500,10 @@ void __init sme_early_init(void)
ia32_disable();

/* Mark the TSC as reliable when Secure TSC is enabled */
- if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
+ if (sev_status & MSR_AMD64_SNP_SECURE_TSC) {
+ setup_force_cpu_cap(X86_FEATURE_SNP_SECURE_TSC);
setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
+ }
}

void __init mem_encrypt_free_decrypted_mem(void)
--
2.34.1


2024-02-15 11:58:27

by Nikunj A. Dadhania

[permalink] [raw]
Subject: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

SNP command mutex is used to serialize the shared buffer access, command
handling and message sequence number races. Move the SNP guest command
mutex out of the sev guest driver and provide accessors to sev-guest
driver. Remove multiple lockdep check in sev-guest driver, next patch adds
a single lockdep check in snp_send_guest_request().

Signed-off-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
---
arch/x86/include/asm/sev.h | 4 ++++
arch/x86/kernel/sev.c | 15 +++++++++++++++
drivers/virt/coco/sev-guest/sev-guest.c | 23 +++++++----------------
3 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index e4f52a606487..8578b33d8fc4 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -295,6 +295,8 @@ 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 kdump_sev_callback(void);
+void snp_guest_cmd_lock(void);
+void snp_guest_cmd_unlock(void);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
@@ -325,6 +327,8 @@ 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 kdump_sev_callback(void) { }
+static inline void snp_guest_cmd_lock(void) { }
+static inline void snp_guest_cmd_unlock(void) { }
#endif

#ifdef CONFIG_KVM_AMD_SEV
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index eda43c35a9f2..bc4a705d989c 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -940,6 +940,21 @@ static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa)
free_page((unsigned long)vmsa);
}

+/* SNP Guest command mutex to serialize the shared buffer access and command handling. */
+static DEFINE_MUTEX(snp_guest_cmd_mutex);
+
+void snp_guest_cmd_lock(void)
+{
+ mutex_lock(&snp_guest_cmd_mutex);
+}
+EXPORT_SYMBOL_GPL(snp_guest_cmd_lock);
+
+void snp_guest_cmd_unlock(void)
+{
+ mutex_unlock(&snp_guest_cmd_mutex);
+}
+EXPORT_SYMBOL_GPL(snp_guest_cmd_unlock);
+
static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip)
{
struct sev_es_save_area *cur_vmsa, *vmsa;
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 646eb215f3c7..ba9ffaee647c 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -62,9 +62,6 @@ 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.");

-/* 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;
@@ -114,8 +111,6 @@ 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_cmd_mutex);
-
/* Read the current message sequence counter from secrets pages */
count = *os_area_msg_seqno;

@@ -408,8 +403,6 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
struct snp_report_resp *report_resp;
int rc, resp_len;

- lockdep_assert_held(&snp_cmd_mutex);
-
if (!arg->req_data || !arg->resp_data)
return -EINVAL;

@@ -456,8 +449,6 @@ 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);
-
if (!arg->req_data || !arg->resp_data)
return -EINVAL;

@@ -508,8 +499,6 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
sockptr_t certs_address;
int ret, resp_len;

- lockdep_assert_held(&snp_cmd_mutex);
-
if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data))
return -EINVAL;

@@ -605,12 +594,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);
+ snp_guest_cmd_lock();

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

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

- mutex_unlock(&snp_cmd_mutex);
+ snp_guest_cmd_unlock();

if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
return -EFAULT;
@@ -725,14 +714,14 @@ static int sev_report_new(struct tsm_report *report, void *data)
if (!buf)
return -ENOMEM;

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

+ snp_guest_cmd_lock();
+
cert_table = buf + report_size;
struct snp_ext_report_req ext_req = {
.data = { .vmpl = desc->privlevel },
@@ -753,6 +742,8 @@ static int sev_report_new(struct tsm_report *report, void *data)
};

ret = get_ext_report(snp_dev, &input, &io);
+ snp_guest_cmd_unlock();
+
if (ret)
return ret;

--
2.34.1


2024-02-27 18:29:14

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 02/16] virt: sev-guest: Replace dev_dbg with pr_debug

On Thu, Feb 15, 2024 at 05:01:14PM +0530, Nikunj A Dadhania wrote:
> 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]>
> Tested-by: Peter Gonda <[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 af35259bc584..01b565170729 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -178,8 +178,9 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
> struct aesgcm_ctx *ctx = snp_dev->ctx;
> u8 iv[GCM_AES_IV_SIZE] = {};
>
> - 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));
> @@ -232,8 +233,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);
>
> if (WARN_ON((sz + ctx->authsize) > sizeof(req->payload)))
> return -EBADMSG;
> --

Reviewed-by: Borislav Petkov (AMD) <[email protected]>

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-02-27 22:23:24

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCH v8 03/16] virt: sev-guest: Add SNP guest request structure

On 2/15/24 05:31, 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.
>
> Update snp_issue_guest_request() prototype to include the new guest request
> structure and move the prototype to sev.h.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> ---
> arch/x86/include/asm/sev.h | 75 ++++++++-
> arch/x86/kernel/sev.c | 15 +-
> drivers/virt/coco/sev-guest/sev-guest.c | 195 +++++++++++++-----------
> drivers/virt/coco/sev-guest/sev-guest.h | 66 --------
> 4 files changed, 187 insertions(+), 164 deletions(-)
> delete mode 100644 drivers/virt/coco/sev-guest/sev-guest.h
>
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index bed95e1f4d52..0c0b11af9f89 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -111,8 +111,6 @@ struct rmp_state {
> 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 {
> @@ -154,6 +152,73 @@ struct snp_secrets_page_layout {
> u8 rsvd3[3840];
> } __packed;
>
> +#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 {
> + SNP_MSG_TYPE_INVALID = 0,
> + SNP_MSG_CPUID_REQ,
> + SNP_MSG_CPUID_RSP,
> + SNP_MSG_KEY_REQ,
> + SNP_MSG_KEY_RSP,
> + SNP_MSG_REPORT_REQ,
> + SNP_MSG_REPORT_RSP,
> + SNP_MSG_EXPORT_REQ,
> + SNP_MSG_EXPORT_RSP,
> + SNP_MSG_IMPORT_REQ,
> + SNP_MSG_IMPORT_RSP,
> + SNP_MSG_ABSORB_REQ,
> + SNP_MSG_ABSORB_RSP,
> + SNP_MSG_VMRK_REQ,
> + SNP_MSG_VMRK_RSP,
> +
> + SNP_MSG_TYPE_MAX
> +};
> +
> +enum aead_algo {
> + SNP_AEAD_INVALID,
> + SNP_AEAD_AES_256_GCM,
> +};
> +
> +struct snp_guest_msg_hdr {
> + u8 authtag[MAX_AUTHTAG_LEN];
> + u64 msg_seqno;
> + u8 rsvd1[8];
> + u8 algo;
> + u8 hdr_version;
> + u16 hdr_sz;
> + u8 msg_type;
> + u8 msg_version;
> + u16 msg_sz;
> + u32 rsvd2;
> + u8 msg_vmpck;
> + u8 rsvd3[35];
> +} __packed;
> +
> +struct snp_guest_msg {
> + struct snp_guest_msg_hdr hdr;
> + u8 payload[4000];

If the idea is to ensure that payload never goes beyond a page boundary
(assuming page allocation/backing), it would be better to have:

u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)];

instead of hard-coding 4000 (I realize this is existing code). Although,
since you probably want to ensure that you don't exceed the page
allocation by testing against the size or page offset, you can just make
this a variable length array:

u8 payload[];

and ensure that you don't overrun.

Thanks,
Tom

> +} __packed;
> +

2024-02-28 12:05:21

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 03/16] virt: sev-guest: Add SNP guest request structure

On Thu, Feb 15, 2024 at 05:01:15PM +0530, Nikunj A Dadhania wrote:
> +enum aead_algo {

Looks unused.

Add new struct definitions, etc, together with their first user - not
preemptively.

Please audit all your patches.

> + SNP_AEAD_INVALID,
> + SNP_AEAD_AES_256_GCM,
> +};
> +
> +struct snp_guest_msg_hdr {
> + u8 authtag[MAX_AUTHTAG_LEN];
> + u64 msg_seqno;
> + u8 rsvd1[8];
> + u8 algo;
> + u8 hdr_version;
> + u16 hdr_sz;
> + u8 msg_type;
> + u8 msg_version;
> + u16 msg_sz;
> + u32 rsvd2;
> + u8 msg_vmpck;
> + u8 rsvd3[35];
> +} __packed;
> +
> +struct snp_guest_msg {
> + struct snp_guest_msg_hdr hdr;
> + u8 payload[4000];

What Tom said.

> +} __packed;
> +
> +struct snp_guest_req {
> + void *req_buf;
> + size_t req_sz;
> +
> + void *resp_buf;
> + size_t resp_sz;
> +
> + void *data;
> + size_t data_npages;
> +
> + u64 exit_code;
> + unsigned int vmpck_id;
> + u8 msg_version;
> + u8 msg_type;
> +};
> +
> #ifdef CONFIG_AMD_MEM_ENCRYPT
> extern void __sev_es_ist_enter(struct pt_regs *regs);
> extern void __sev_es_ist_exit(void);
> @@ -223,7 +288,8 @@ 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);
> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
> + struct snp_guest_request_ioctl *rio);

Much nicer!

..

> @@ -868,7 +890,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> /* initial the input address for guest request */

That comment reads wrong - might fix it while here.

> 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 = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type);
> if (ret)

Rest looks ok. I'd chose shorter local vars if I were you but yours are
ok too.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-02-29 09:13:36

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 03/16] virt: sev-guest: Add SNP guest request structure

On 2/28/2024 3:50 AM, Tom Lendacky wrote:
> On 2/15/24 05:31, 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.
>>
>> Update snp_issue_guest_request() prototype to include the new guest request
>> structure and move the prototype to sev.h.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> ---
>>   arch/x86/include/asm/sev.h              |  75 ++++++++-
>>   arch/x86/kernel/sev.c                   |  15 +-
>>   drivers/virt/coco/sev-guest/sev-guest.c | 195 +++++++++++++-----------
>>   drivers/virt/coco/sev-guest/sev-guest.h |  66 --------
>>   4 files changed, 187 insertions(+), 164 deletions(-)
>>   delete mode 100644 drivers/virt/coco/sev-guest/sev-guest.h
>>
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index bed95e1f4d52..0c0b11af9f89 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -111,8 +111,6 @@ struct rmp_state {
>>   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 {
>> @@ -154,6 +152,73 @@ struct snp_secrets_page_layout {
>>       u8 rsvd3[3840];
>>   } __packed;
>>   +#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 {
>> +    SNP_MSG_TYPE_INVALID = 0,
>> +    SNP_MSG_CPUID_REQ,
>> +    SNP_MSG_CPUID_RSP,
>> +    SNP_MSG_KEY_REQ,
>> +    SNP_MSG_KEY_RSP,
>> +    SNP_MSG_REPORT_REQ,
>> +    SNP_MSG_REPORT_RSP,
>> +    SNP_MSG_EXPORT_REQ,
>> +    SNP_MSG_EXPORT_RSP,
>> +    SNP_MSG_IMPORT_REQ,
>> +    SNP_MSG_IMPORT_RSP,
>> +    SNP_MSG_ABSORB_REQ,
>> +    SNP_MSG_ABSORB_RSP,
>> +    SNP_MSG_VMRK_REQ,
>> +    SNP_MSG_VMRK_RSP,
>> +
>> +    SNP_MSG_TYPE_MAX
>> +};
>> +
>> +enum aead_algo {
>> +    SNP_AEAD_INVALID,
>> +    SNP_AEAD_AES_256_GCM,
>> +};
>> +
>> +struct snp_guest_msg_hdr {
>> +    u8 authtag[MAX_AUTHTAG_LEN];
>> +    u64 msg_seqno;
>> +    u8 rsvd1[8];
>> +    u8 algo;
>> +    u8 hdr_version;
>> +    u16 hdr_sz;
>> +    u8 msg_type;
>> +    u8 msg_version;
>> +    u16 msg_sz;
>> +    u32 rsvd2;
>> +    u8 msg_vmpck;
>> +    u8 rsvd3[35];
>> +} __packed;
>> +
>> +struct snp_guest_msg {
>> +    struct snp_guest_msg_hdr hdr;
>> +    u8 payload[4000];
>
> If the idea is to ensure that payload never goes beyond a page boundary (assuming page allocation/backing), it would be better to have:
>
>     u8 payload[PAGE_SIZE - sizeof(struct snp_guest_msg_hdr)];
>
> instead of hard-coding 4000 (I realize this is existing code). Although, since you probably want to ensure that you don't exceed the page allocation by testing against the size or page offset, you can just make this a variable length array:
>
>     u8 payload[];
>
> and ensure that you don't overrun.

Sure, below is the delta to make payload a variable length array. I will squash it with current patch.

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 0c0b11af9f89..85cf160f6203 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -200,9 +200,12 @@ struct snp_guest_msg_hdr {

struct snp_guest_msg {
struct snp_guest_msg_hdr hdr;
- u8 payload[4000];
+ u8 payload[];
} __packed;

+#define SNP_GUEST_MSG_SIZE 4096
+#define SNP_GUEST_MSG_PAYLOAD_SIZE (SNP_GUEST_MSG_SIZE - sizeof(struct snp_guest_msg))
+
struct snp_guest_req {
void *req_buf;
size_t req_sz;
diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index 596cec03f9eb..da9a616c76cf 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -46,7 +46,7 @@ struct snp_guest_dev {
* 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_guest_msg *secret_request, *secret_response;

struct snp_secrets_page_layout *layout;
struct snp_req_data input;
@@ -169,8 +169,8 @@ static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)

static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_req *req)
{
- struct snp_guest_msg *resp_msg = &snp_dev->secret_response;
- struct snp_guest_msg *req_msg = &snp_dev->secret_request;
+ struct snp_guest_msg *resp_msg = snp_dev->secret_response;
+ struct snp_guest_msg *req_msg = snp_dev->secret_request;
struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
struct aesgcm_ctx *ctx = snp_dev->ctx;
@@ -181,7 +181,7 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_gues
resp_msg_hdr->msg_sz);

/* Copy response from shared memory to encrypted memory. */
- memcpy(resp_msg, snp_dev->response, sizeof(*resp_msg));
+ memcpy(resp_msg, snp_dev->response, SNP_GUEST_MSG_SIZE);

/* Verify that the sequence counter is incremented by 1 */
if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_hdr->msg_seqno + 1)))
@@ -210,7 +210,7 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, struct snp_gues

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 *msg = snp_dev->secret_request;
struct snp_guest_msg_hdr *hdr = &msg->hdr;
struct aesgcm_ctx *ctx = snp_dev->ctx;
u8 iv[GCM_AES_IV_SIZE] = {};
@@ -233,7 +233,7 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, struct snp_gues
pr_debug("request [seqno %lld type %d version %d sz %d]\n",
hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);

- if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
+ if (WARN_ON((req->req_sz + ctx->authsize) > SNP_GUEST_MSG_PAYLOAD_SIZE))
return -EBADMSG;

memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
@@ -341,7 +341,7 @@ static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_gues
return -EIO;

/* Clear shared memory's response for the host to populate. */
- memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
+ memset(snp_dev->response, 0, SNP_GUEST_MSG_SIZE);

/* Encrypt the userspace provided payload in snp_dev->secret_request. */
rc = enc_payload(snp_dev, seqno, req);
@@ -352,8 +352,7 @@ static int snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_gues
* Write the fully encrypted request to the shared unencrypted
* request page.
*/
- memcpy(snp_dev->request, &snp_dev->secret_request,
- sizeof(snp_dev->secret_request));
+ memcpy(snp_dev->request, snp_dev->secret_request, SNP_GUEST_MSG_SIZE);

rc = __handle_guest_request(snp_dev, req, rio);
if (rc) {
@@ -864,12 +863,21 @@ static int __init sev_guest_probe(struct platform_device *pdev)
snp_dev->dev = dev;
snp_dev->layout = layout;

+ /* Allocate secret request and response message for double buffering */
+ snp_dev->secret_request = kzalloc(SNP_GUEST_MSG_SIZE, GFP_KERNEL);
+ if (!snp_dev->secret_request)
+ goto e_unmap;
+
+ snp_dev->secret_response = kzalloc(SNP_GUEST_MSG_SIZE, GFP_KERNEL);
+ if (!snp_dev->secret_response)
+ goto e_free_secret_req;
+
/* Allocate the shared page used for the request and response message. */
- snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
+ snp_dev->request = alloc_shared_pages(dev, SNP_GUEST_MSG_SIZE);
if (!snp_dev->request)
- goto e_unmap;
+ goto e_free_secret_resp;

- snp_dev->response = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
+ snp_dev->response = alloc_shared_pages(dev, SNP_GUEST_MSG_SIZE);
if (!snp_dev->response)
goto e_free_request;

@@ -911,9 +919,13 @@ static int __init sev_guest_probe(struct platform_device *pdev)
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));
+ free_shared_pages(snp_dev->response, SNP_GUEST_MSG_SIZE);
e_free_request:
- free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
+ free_shared_pages(snp_dev->request, SNP_GUEST_MSG_SIZE);
+e_free_secret_resp:
+ kfree(snp_dev->secret_response);
+e_free_secret_req:
+ kfree(snp_dev->secret_request);
e_unmap:
iounmap(mapping);
return ret;
@@ -924,9 +936,11 @@ static void __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));
+ free_shared_pages(snp_dev->response, SNP_GUEST_MSG_SIZE);
+ free_shared_pages(snp_dev->request, SNP_GUEST_MSG_SIZE);
kfree(snp_dev->ctx);
+ kfree(snp_dev->secret_response);
+ kfree(snp_dev->secret_request);
misc_deregister(&snp_dev->misc);
}



2024-02-29 09:26:54

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 03/16] virt: sev-guest: Add SNP guest request structure

On 2/28/2024 5:20 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:15PM +0530, Nikunj A Dadhania wrote:
>> +enum aead_algo {
>
> Looks unused.

This is being moved from drivers/virt/coco/sev-guest/sev-guest.h and is
used in drivers/virt/coco/sev-guest/sev-guest.c::enc_payload()

hdr->algo = SNP_AEAD_AES_256_GCM;

> Add new struct definitions, etc, together with their first user - not
> preemptively.
>
> Please audit all your patches.

Sure.

>
>> + SNP_AEAD_INVALID,
>> + SNP_AEAD_AES_256_GCM,
>> +};
>> +
>> +struct snp_guest_msg_hdr {
>> + u8 authtag[MAX_AUTHTAG_LEN];
>> + u64 msg_seqno;
>> + u8 rsvd1[8];
>> + u8 algo;
>> + u8 hdr_version;
>> + u16 hdr_sz;
>> + u8 msg_type;
>> + u8 msg_version;
>> + u16 msg_sz;
>> + u32 rsvd2;
>> + u8 msg_vmpck;
>> + u8 rsvd3[35];
>> +} __packed;
>> +
>> +struct snp_guest_msg {
>> + struct snp_guest_msg_hdr hdr;
>> + u8 payload[4000];
>
> What Tom said.

Responded on that thread.

>
>> +} __packed;
>> +
>> +struct snp_guest_req {
>> + void *req_buf;
>> + size_t req_sz;
>> +
>> + void *resp_buf;
>> + size_t resp_sz;
>> +
>> + void *data;
>> + size_t data_npages;
>> +
>> + u64 exit_code;
>> + unsigned int vmpck_id;
>> + u8 msg_version;
>> + u8 msg_type;
>> +};
>> +
>> #ifdef CONFIG_AMD_MEM_ENCRYPT
>> extern void __sev_es_ist_enter(struct pt_regs *regs);
>> extern void __sev_es_ist_exit(void);
>> @@ -223,7 +288,8 @@ 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);
>> +int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
>> + struct snp_guest_request_ioctl *rio);
>
> Much nicer!

Thank you.

>
> ...
>
>> @@ -868,7 +890,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>> /* initial the input address for guest request */
>
> That comment reads wrong - might fix it while here.

Will update.

>> 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 = tsm_register(&sev_tsm_ops, snp_dev, &tsm_report_extra_type);
>> if (ret)
>
> Rest looks ok. I'd chose shorter local vars if I were you but yours are
> ok too.
>

Regards
Nikunj


2024-04-09 10:24:48

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 04/16] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On Thu, Feb 15, 2024 at 05:01:16PM +0530, 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. Added define for maximum supported VMPCK.

Do not talk about *what* the patch is doing in the commit message - that
should be obvious from the diff itself. Rather, concentrate on the *why*
it needs to be done.

Imagine one fine day you're doing git archeology, you find the place in
the code about which you want to find out why it was changed the way it
is now.

You do git annotate <filename> ... find the line, see the commit id and
you do:

git show <commit id>

You read the commit message and there's just gibberish and nothing's
explaining *why* that change was done. And you start scratching your
head, trying to figure out why...

I'm sure you're getting the idea.

> 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]>
> Reviewed-by: Tom Lendacky <[email protected]>
> Tested-by: Peter Gonda <[email protected]>
> ---
> arch/x86/include/asm/sev.h | 1 +
> drivers/virt/coco/sev-guest/sev-guest.c | 95 ++++++++++++-------------
> 2 files changed, 48 insertions(+), 48 deletions(-)
>
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 0c0b11af9f89..e4f52a606487 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -135,6 +135,7 @@ struct secrets_os_area {
> } __packed;
>
> #define VMPCK_KEY_LEN 32
> +#define VMPCK_MAX_NUM 4
>
> /* See the SNP spec version 0.9 for secrets page format */
> struct snp_secrets_page_layout {
> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
> index 596cec03f9eb..646eb215f3c7 100644
> --- a/drivers/virt/coco/sev-guest/sev-guest.c
> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
> @@ -55,8 +55,7 @@ struct snp_guest_dev {
> struct snp_derived_key_req derived_key;
> struct snp_ext_report_req ext_report;
> } req;
> - u32 *os_area_msg_seqno;
> - u8 *vmpck;
> + unsigned int vmpck_id;
> };
>
> static u32 vmpck_id;
> @@ -66,14 +65,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)

static functions don't need a prefix like "snp_".

> {
> - 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)

Ditto.

> +{
> + 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);
> }
>
> /*
> @@ -95,20 +102,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
> */
> static void snp_disable_vmpck(struct snp_guest_dev *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;
> + u8 *key = snp_get_vmpck(snp_dev);

Check whether is_vmpck_empty before you disable?

> +
> + dev_alert(snp_dev->dev, "Disabling vmpck_id %u 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_cmd_mutex);
>
> /* Read the current message sequence counter from secrets pages */
> - count = *snp_dev->os_area_msg_seqno;
> + count = *os_area_msg_seqno;

Why does that snp_get_os_area_msg_seqno() returns a pointer when you
deref it here again?

A function which returns a sequence number should return that number
- not a pointer to it.

Which then makes that u32 *os_area_msg_seqno redundant and you can use
the function directly.

IOW:

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;
}

Simple.

>
> return count + 1;
> }
> @@ -136,11 +145,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;

Yah, you have a getter but not a setter. You're setting it through the
pointer. Do you see the imbalance in the APIs?

> }
>
> static inline struct snp_guest_dev *to_snp_dev(struct file *file)
> @@ -150,15 +161,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("VM communication key VMPCK%u is null\n", vmpck_id);

"Empty/invalid VMPCK%u communication key"

or so.

In a pre-patch, fix all your user-visible strings to say "VMPCK"
- capitalized as it is an abbreviation.

> + 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("Crypto context initialization failed\n");
> kfree(ctx);
> return NULL;
> @@ -590,7 +608,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
> mutex_lock(&snp_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_cmd_mutex);
> return -ENOTTY;
> @@ -667,32 +685,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)
> +static bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
> {
> - u8 *key = NULL;
> + if (WARN_ON((vmpck_id + 1) > VMPCK_MAX_NUM))
> + return false;

So this will warn *and*, at the call site too. Let's tone that down.

>
> - 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;
> - }

Your commit message could explain why this is not needed, all of
a sudden.

> + dev->vmpck_id = vmpck_id;
>
> - return key;
> + return true;
> }
>
> struct snp_msg_report_resp_hdr {
> @@ -728,7 +728,7 @@ static int sev_report_new(struct tsm_report *report, void *data)
> guard(mutex)(&snp_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");
> return -ENOTTY;
> }
> @@ -848,21 +848,20 @@ 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) {
> - dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
> + snp_dev->layout = layout;
> + if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
> + dev_err(dev, "invalid vmpck id %u\n", vmpck_id);
> goto e_unmap;
> }
>
> /* Verify that VMPCK is not zero. */
> - if (is_vmpck_empty(snp_dev)) {
> - dev_err(dev, "vmpck id %d is null\n", vmpck_id);
> + if (snp_is_vmpck_empty(snp_dev)) {
> + dev_err(dev, "vmpck id %u is null\n", vmpck_id);

s!null!Invalid/Empty!

> goto e_unmap;
> }
>
> 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));
> @@ -878,7 +877,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;
>
> @@ -903,7 +902,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
> if (ret)
> goto e_free_ctx;
>
> - dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id);
> + dev_info(dev, "Initialized SEV guest driver (using vmpck_id %u)\n", vmpck_id);

Yet another spelling: "vmpck_id". Unify all those in a pre-patch pls
because it looks stupid.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-16 05:59:33

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 04/16] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On 4/9/2024 3:53 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:16PM +0530, 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. Added define for maximum supported VMPCK.
>
> Do not talk about *what* the patch is doing in the commit message - that
> should be obvious from the diff itself. Rather, concentrate on the *why*
> it needs to be done.
>
> Imagine one fine day you're doing git archeology, you find the place in
> the code about which you want to find out why it was changed the way it
> is now.
>
> You do git annotate <filename> ... find the line, see the commit id and
> you do:
>
> git show <commit id>
>
> You read the commit message and there's just gibberish and nothing's
> explaining *why* that change was done. And you start scratching your
> head, trying to figure out why...
>
> I'm sure you're getting the idea.

Sure, will reword the commit message and send the patch.

>
>> 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]>
>> Reviewed-by: Tom Lendacky <[email protected]>
>> Tested-by: Peter Gonda <[email protected]>
>> ---
>> arch/x86/include/asm/sev.h | 1 +
>> drivers/virt/coco/sev-guest/sev-guest.c | 95 ++++++++++++-------------
>> 2 files changed, 48 insertions(+), 48 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index 0c0b11af9f89..e4f52a606487 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -135,6 +135,7 @@ struct secrets_os_area {
>> } __packed;
>>
>> #define VMPCK_KEY_LEN 32
>> +#define VMPCK_MAX_NUM 4
>>
>> /* See the SNP spec version 0.9 for secrets page format */
>> struct snp_secrets_page_layout {
>> diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
>> index 596cec03f9eb..646eb215f3c7 100644
>> --- a/drivers/virt/coco/sev-guest/sev-guest.c
>> +++ b/drivers/virt/coco/sev-guest/sev-guest.c
>> @@ -55,8 +55,7 @@ struct snp_guest_dev {
>> struct snp_derived_key_req derived_key;
>> struct snp_ext_report_req ext_report;
>> } req;
>> - u32 *os_area_msg_seqno;
>> - u8 *vmpck;
>> + unsigned int vmpck_id;
>> };
>>
>> static u32 vmpck_id;
>> @@ -66,14 +65,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)
>
> static functions don't need a prefix like "snp_".

Sure

>
>> {
>> - 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)
>
> Ditto.

Sure

>
>> +{
>> + 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);
>> }
>>
>> /*
>> @@ -95,20 +102,22 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
>> */
>> static void snp_disable_vmpck(struct snp_guest_dev *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;
>> + u8 *key = snp_get_vmpck(snp_dev);
>
> Check whether is_vmpck_empty before you disable?
>
>> +
>> + dev_alert(snp_dev->dev, "Disabling vmpck_id %u 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_cmd_mutex);
>>
>> /* Read the current message sequence counter from secrets pages */
>> - count = *snp_dev->os_area_msg_seqno;
>> + count = *os_area_msg_seqno;
>
> Why does that snp_get_os_area_msg_seqno() returns a pointer when you
> deref it here again?
>
> A function which returns a sequence number should return that number
> - not a pointer to it.
>
> Which then makes that u32 *os_area_msg_seqno redundant and you can use
> the function directly.
>
> IOW:
>
> 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;

This patch removes setting of layour page in snp_dev structure.

static inline u32 snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
{
if (!platform_data)
return NULL;

return *(&platform_data->layout->os_area.msg_seqno_0 + vmpck_id);
}

> }
>

> Simple.
>
>>
>> return count + 1;
>> }
>> @@ -136,11 +145,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;
>
> Yah, you have a getter but not a setter. You're setting it through the
> pointer.

I had a getter for getting the os_area_msg_seqno pointer, probably not a good function name.

> Do you see the imbalance in the APIs?

The msg_seqno should only be incremented by 2 (always), that was the reason to avoid a setter.

>
>> }
>>
>> static inline struct snp_guest_dev *to_snp_dev(struct file *file)
>> @@ -150,15 +161,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("VM communication key VMPCK%u is null\n", vmpck_id);
>
> "Empty/invalid VMPCK%u communication key"
>
> or so.
>
> In a pre-patch, fix all your user-visible strings to say "VMPCK"
> - capitalized as it is an abbreviation.

Sure, will do.

>
>> + 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("Crypto context initialization failed\n");
>> kfree(ctx);
>> return NULL;
>> @@ -590,7 +608,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
>> mutex_lock(&snp_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_cmd_mutex);
>> return -ENOTTY;
>> @@ -667,32 +685,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)
>> +static bool snp_assign_vmpck(struct snp_guest_dev *dev, unsigned int vmpck_id)
>> {
>> - u8 *key = NULL;
>> + if (WARN_ON((vmpck_id + 1) > VMPCK_MAX_NUM))
>> + return false;
>
> So this will warn *and*, at the call site too. Let's tone that down.

Sure.

>
>>
>> - 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;
>> - }
>
> Your commit message could explain why this is not needed, all of
> a sudden.

This was replaced by two independent APIs returning pointers to VMPCK and seqno pointer.

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;
}

I will add more details.

>
>> + dev->vmpck_id = vmpck_id;
>>
>> - return key;
>> + return true;
>> }
>>
>> struct snp_msg_report_resp_hdr {
>> @@ -728,7 +728,7 @@ static int sev_report_new(struct tsm_report *report, void *data)
>> guard(mutex)(&snp_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");
>> return -ENOTTY;
>> }
>> @@ -848,21 +848,20 @@ 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) {
>> - dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
>> + snp_dev->layout = layout;
>> + if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
>> + dev_err(dev, "invalid vmpck id %u\n", vmpck_id);
>> goto e_unmap;
>> }
>>
>> /* Verify that VMPCK is not zero. */
>> - if (is_vmpck_empty(snp_dev)) {
>> - dev_err(dev, "vmpck id %d is null\n", vmpck_id);
>> + if (snp_is_vmpck_empty(snp_dev)) {
>> + dev_err(dev, "vmpck id %u is null\n", vmpck_id);
>
> s!null!Invalid/Empty!

Okay

>
>> goto e_unmap;
>> }
>>
>> 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));
>> @@ -878,7 +877,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;
>>
>> @@ -903,7 +902,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
>> if (ret)
>> goto e_free_ctx;
>>
>> - dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id);
>> + dev_info(dev, "Initialized SEV guest driver (using vmpck_id %u)\n", vmpck_id);
>
> Yet another spelling: "vmpck_id". Unify all those in a pre-patch pls
> because it looks stupid.

Sure
>
> Thx.
>

Thanks for the review.

Regards
Nikunj


2024-04-16 09:07:34

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 04/16] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On Tue, Apr 16, 2024 at 11:27:24AM +0530, Nikunj A. Dadhania wrote:
> > Why does that snp_get_os_area_msg_seqno() returns a pointer when you
> > deref it here again?
> >
> > A function which returns a sequence number should return that number
> > - not a pointer to it.
> >
> > Which then makes that u32 *os_area_msg_seqno redundant and you can use
> > the function directly.
> >
> > IOW:
> >
> > 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;
>
> This patch removes setting of layour page in snp_dev structure.

So?

> static inline u32 snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
> {
> if (!platform_data)
> return NULL;
>
> return *(&platform_data->layout->os_area.msg_seqno_0 + vmpck_id);
> }

What?!

This snp_get_os_area_msg_seqno() is a new function added by this patch.

> I had a getter for getting the os_area_msg_seqno pointer, probably not
> a good function name.

Probably you need to go back to the drawing board and think about how
this thing should look like.

> > Do you see the imbalance in the APIs?
>
> The msg_seqno should only be incremented by 2 (always), that was the reason to avoid a setter.

And what's wrong with the setter doing the incrementation so that
callers can't even get it wrong?

It sounds to me like you should redesign this sequence number handling
in a *separate* patch.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-16 14:46:57

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 05/16] x86/sev: Cache the secrets page address

On Thu, Feb 15, 2024 at 05:01:17PM +0530, Nikunj A Dadhania wrote:
> +/* Secrets page physical address from the CC blob */
> +static u64 secrets_pa __ro_after_init;

Since you're going to use this during runtime (are you?), why don't you
put in here the result of:

ioremap_encrypted(secrets_pa, PAGE_SIZE);

so that you can have it ready and not even have to ioremap each time?

And then you iounmap on driver teardown.

> +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;
> +}

Why is this a separate function if it is called only once and it is
a trivial function at that?

Also, can the driver continue without secrets page?

If not, then you need to unwind.

> bool __init snp_init(struct boot_params *bp)
> {
> struct cc_blob_sev_info *cc_info;
> @@ -2099,6 +2079,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,16 @@ 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) {
> + pr_err("SNP secrets page not found\n");
> return -ENODEV;
> + }

Yeah, no, you need to error out in snp_init() and not drag it around to
snp_init_platform_device().

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-17 04:18:36

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 04/16] virt: sev-guest: Add vmpck_id to snp_guest_dev struct

On 4/16/2024 2:36 PM, Borislav Petkov wrote:
> On Tue, Apr 16, 2024 at 11:27:24AM +0530, Nikunj A. Dadhania wrote:
>>> Why does that snp_get_os_area_msg_seqno() returns a pointer when you
>>> deref it here again?
>>>
>>> A function which returns a sequence number should return that number
>>> - not a pointer to it.
>>>
>>> Which then makes that u32 *os_area_msg_seqno redundant and you can use
>>> the function directly.
>>>
>>> IOW:
>>>
>>> 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;
>>
>> This patch removes setting of layour page in snp_dev structure.
>
> So?

* Instead of using snp_dev->layout, we will need to access it using platform_data->layout structure.
* Below will give incorrect value of sequence number, it will get VMPCK_0's sequence number and will add vmpck_id to that. Will work by fluke for VMPCK=0, but will fail for all other keys.

return snp_dev->layout->os_area.msg_seqno_0 + snp_dev->vmpck_id;


struct secrets_os_area {
..
u32 msg_seqno_0;
u32 msg_seqno_1;
u32 msg_seqno_2;
u32 msg_seqno_3;
..
}

* I am using vmpck_id to index to correct msg_seqno_*


Changing this to

struct secrets_os_area {
..
u32 msg_seqno[VMPCK_MAX_NUM];
..
}


>
>> static inline u32 snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
>> {
>> if (!platform_data)
>> return NULL;
>>
>> return *(&platform_data->layout->os_area.msg_seqno_0 + vmpck_id);
>> }
>
> What?!

I can change the secrets_os_area like below to simplify things:

struct secrets_os_area {
..
u32 msg_seqno[VMPCK_MAX_NUM];
..
}


static inline u32 snp_get_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
{
if (!platform_data)
return NULL;

return platform_data->layout->os_area.msg_seqno[snp_dev->vmpck_id];
}


>
> This snp_get_os_area_msg_seqno() is a new function added by this patch.
>
>> I had a getter for getting the os_area_msg_seqno pointer, probably not
>> a good function name.
>
> Probably you need to go back to the drawing board and think about how
> this thing should look like.
>
>>> Do you see the imbalance in the APIs?
>>
>> The msg_seqno should only be incremented by 2 (always), that was the reason to avoid a setter.
>
> And what's wrong with the setter doing the incrementation so that
> callers can't even get it wrong?

Are you suggesting that setter should always increment by 2?

static inline u32 snp_set_os_area_msg_seqno(struct snp_guest_dev *snp_dev)
{
..
os_area.msg_seqno[snp_dev->vmpck_id] += 2;
..

}

>
> It sounds to me like you should redesign this sequence number handling
> in a *separate* patch.

Sure, let me rethink and will post it as separate patch.

Regards
Nikunj


2024-04-17 05:28:13

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 05/16] x86/sev: Cache the secrets page address

On 4/16/2024 8:15 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:17PM +0530, Nikunj A Dadhania wrote:
>> +/* Secrets page physical address from the CC blob */
>> +static u64 secrets_pa __ro_after_init;
>
> Since you're going to use this during runtime (are you?),

Yes, this is used during runtime, during initial boot will be used by Secure TSC and later by sev-guest driver.

> why don't you put in here the result of:
>
> ioremap_encrypted(secrets_pa, PAGE_SIZE);
>
> so that you can have it ready and not even have to ioremap each time?

Yes, that is a good idea. If I map in sev.c, what is the right place to iounmap ? Is it safe to keep it mapped until reboot/shutdown ?

> And then you iounmap on driver teardown.
>
>> +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;
>> +}
>
> Why is this a separate function if it is called only once and it is
> a trivial function at that?

Sure, I will change it.

>
> Also, can the driver continue without secrets page?

No.

> If not, then you need to unwind.
>
By unwind, do you mean unmapping in the driver?

>> bool __init snp_init(struct boot_params *bp)
>> {
>> struct cc_blob_sev_info *cc_info;
>> @@ -2099,6 +2079,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,16 @@ 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) {
>> + pr_err("SNP secrets page not found\n");
>> return -ENODEV;
>> + }
>
> Yeah, no, you need to error out in snp_init() and not drag it around to
> snp_init_platform_device().

snp_init() is called from sme_enable(), and does not handle failure from snp_init()

How about the below diff?

diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index e9925df21010..5e052f972138 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -114,7 +114,7 @@ struct snp_req_data {
};

struct sev_guest_platform_data {
- u64 secrets_gpa;
+ void *secrets_page;
};

/*
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 95003b809438..14c88e4f98ba 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -90,6 +90,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 address mapped from the CC blob physical address */
+static void *secrets_page __ro_after_init;
+
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
struct ghcb ghcb_page;
@@ -616,54 +619,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;
-
- pa = get_secrets_page();
- if (!pa)
- return 0;
+ u64 addr;

- mem = ioremap_encrypted(pa, PAGE_SIZE);
- if (!mem) {
- pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
+ if (!secrets_page)
return 0;
- }
-
- layout = (__force struct snp_secrets_page_layout *)mem;

+ layout = (__force struct snp_secrets_page_layout *)secrets_page;
addr = layout->os_area.ap_jump_table_pa;
- iounmap(mem);

return addr;
}
@@ -2118,6 +2083,14 @@ bool __init snp_init(struct boot_params *bp)
if (!cc_info)
return false;

+ if (cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE) {
+ secrets_page = ioremap_encrypted(cc_info->secrets_phys, PAGE_SIZE);
+ if (!secrets_page) {
+ pr_err("Unable to map secrets page\n");
+ return false;
+ }
+ }
+
setup_cpuid_table(cc_info);

/*
@@ -2265,16 +2238,11 @@ 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 (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP) || !secrets_page)
return -ENODEV;

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

diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c
index dbc04229f7ac..4cef4e108130 100644
--- a/drivers/virt/coco/sev-guest/sev-guest.c
+++ b/drivers/virt/coco/sev-guest/sev-guest.c
@@ -820,12 +820,10 @@ static void unregister_sev_tsm(void *data)

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))
@@ -835,28 +833,24 @@ static int __init sev_guest_probe(struct platform_device *pdev)
return -ENODEV;

data = (struct sev_guest_platform_data *)dev->platform_data;
- mapping = ioremap_encrypted(data->secrets_gpa, PAGE_SIZE);
- if (!mapping)
+ if (!data->secrets_page)
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;
+ snp_dev->layout = (__force struct snp_secrets_page_layout *)data->secrets_page;
if (!snp_assign_vmpck(snp_dev, vmpck_id)) {
dev_err(dev, "invalid vmpck id %u\n", vmpck_id);
- goto e_unmap;
+ return ret;
}

/* Verify that VMPCK is not zero. */
if (snp_is_vmpck_empty(snp_dev)) {
dev_err(dev, "vmpck id %u is null\n", vmpck_id);
- goto e_unmap;
+ return ret;
}

platform_set_drvdata(pdev, snp_dev);
@@ -865,7 +859,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
/* Allocate secret request and response message for double buffering */
snp_dev->secret_request = kzalloc(SNP_GUEST_MSG_SIZE, GFP_KERNEL);
if (!snp_dev->secret_request)
- goto e_unmap;
+ return ret;

snp_dev->secret_response = kzalloc(SNP_GUEST_MSG_SIZE, GFP_KERNEL);
if (!snp_dev->secret_response)
@@ -925,8 +919,6 @@ static int __init sev_guest_probe(struct platform_device *pdev)
kfree(snp_dev->secret_response);
e_free_secret_req:
kfree(snp_dev->secret_request);
-e_unmap:
- iounmap(mapping);
return ret;
}


Regards
Nikunj

2024-04-17 08:00:43

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 05/16] x86/sev: Cache the secrets page address

On 4/17/2024 10:57 AM, Nikunj A. Dadhania wrote:
> On 4/16/2024 8:15 PM, Borislav Petkov wrote:
>> On Thu, Feb 15, 2024 at 05:01:17PM +0530, Nikunj A Dadhania wrote:
>>> +/* Secrets page physical address from the CC blob */
>>> +static u64 secrets_pa __ro_after_init;
>>
>> Since you're going to use this during runtime (are you?),
>
> Yes, this is used during runtime, during initial boot will be used by Secure TSC and later by sev-guest driver.
>
>> why don't you put in here the result of:
>>
>> ioremap_encrypted(secrets_pa, PAGE_SIZE);
>>
>> so that you can have it ready and not even have to ioremap each time?
>

> @@ -2118,6 +2083,14 @@ bool __init snp_init(struct boot_params *bp)
> if (!cc_info)
> return false;
>
> + if (cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE) {
> + secrets_page = ioremap_encrypted(cc_info->secrets_phys, PAGE_SIZE);

ioremap_encrypted() does not work this early, snp guest boot fails, will debug further.

Regards,
Nikunj


2024-04-22 13:01:15

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

On Thu, Feb 15, 2024 at 05:01:18PM +0530, Nikunj A Dadhania wrote:
> SNP command mutex is used to serialize the shared buffer access, command
> handling and message sequence number races. Move the SNP guest command
> mutex out of the sev guest driver and provide accessors to sev-guest

And why in the hell are we doing this?

Always, *ALWAYS* make sure a patch's commit message answers *why*
a change is done. This doesn't explain why so I'm reading "just because"
and "just because" doesn't fly.

> driver. Remove multiple lockdep check in sev-guest driver, next patch adds
> a single lockdep check in snp_send_guest_request().

The concept of "next patch" is meaningless once the patch is in git.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-22 13:22:55

by Borislav Petkov

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

On Thu, Feb 15, 2024 at 05:01:19PM +0530, Nikunj A Dadhania wrote:
> Subject: Re: [PATCH v8 07/16] x86/sev: Move and reorganize sev guest request api

s/api/API/g

Please check your whole patchset for proper naming/abbreviations
spelling, etc.

Another one: s/sev/SEV/g and so on. You should know the drill by now.

> 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
> API to the sev guest driver for sending guest message and vmpck
> routines.

Patches which move and reorganize must always be split: first patch(es):
you do *purely* *mechanical* move without any code changes. Then you do
the code changes ontop so that a reviewer can have a chance of seeing
what you're doing.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-22 13:25:46

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 09/16] x86/cpufeatures: Add synthetic Secure TSC bit

On Thu, Feb 15, 2024 at 05:01:21PM +0530, Nikunj A Dadhania wrote:
> Add support for the synthetic CPUID flag which indicates that the SNP
> guest is running with secure tsc enabled (MSR_AMD64_SEV Bit 11 -

"TSC"

> SecureTsc_Enabled) . This flag is there so that this capability in the
> guests can be detected easily without reading MSRs every time accessors.

Why?

What's wrong with cc_platform_has(CC_ATTR_GUEST_SECURE_TSC) or so?

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-22 13:29:07

by Borislav Petkov

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

On Thu, Feb 15, 2024 at 05:01:20PM +0530, Nikunj A Dadhania wrote:
> diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
> index d035bce3a2b0..68aa06852466 100644
> --- a/arch/x86/mm/mem_encrypt.c
> +++ b/arch/x86/mm/mem_encrypt.c
> @@ -89,6 +89,8 @@ void __init mem_encrypt_init(void)
> /* Call into SWIOTLB to update the SWIOTLB DMA buffers */
> swiotlb_update_mem_attributes();
>
> + x86_platform.guest.enc_init();
> +
> print_mem_encrypt_feature_info();

Why all this hoopla if all you need is to call it once in mem_encrypt.c?

IOW, you can simply call snp_secure_tsc_prepare() there, no?

Those function pointers are to be used in generic code in order to hide
all the platform-specific hackery but mem_encrypt.c is not really
generic code, I'd say...

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-22 13:51:57

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 10/16] x86/sev: Add Secure TSC support for SNP guests

On Thu, Feb 15, 2024 at 05:01:22PM +0530, 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

"CPUs"

> 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.
>
> Use the guest enc_init hook to fetch SNP TSC info from the AMD Security
> Processor and initialize the snp_tsc_scale and snp_tsc_offset. During
> secondary CPU initialization set VMSA fields GUEST_TSC_SCALE (offset 2F0h)
> and GUEST_TSC_OFFSET(offset 2F8h) with snp_tsc_scale and snp_tsc_offset
> respectively.
>
> Signed-off-by: Nikunj A Dadhania <[email protected]>
> Tested-by: Peter Gonda <[email protected]>
> ---
> arch/x86/include/asm/sev-common.h | 1 +
> arch/x86/include/asm/sev.h | 23 +++++++
> arch/x86/include/asm/svm.h | 6 +-
> arch/x86/kernel/sev.c | 107 ++++++++++++++++++++++++++++--
> arch/x86/mm/mem_encrypt_amd.c | 6 ++
> 5 files changed, 134 insertions(+), 9 deletions(-)
>
> diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
> index b463fcbd4b90..6adc8e27feeb 100644
> --- a/arch/x86/include/asm/sev-common.h
> +++ b/arch/x86/include/asm/sev-common.h
> @@ -159,6 +159,7 @@ struct snp_psc_desc {
> #define GHCB_TERM_NOT_VMPL0 3 /* SNP guest is not running at VMPL-0 */
> #define GHCB_TERM_CPUID 4 /* CPUID-validation failure */
> #define GHCB_TERM_CPUID_HV 5 /* CPUID failure during hypervisor fallback */
> +#define GHCB_TERM_SECURE_TSC 6 /* Secure TSC initialization failed */
>
> #define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
>
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index d950a3ac5694..16bf5afa7731 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -170,6 +170,8 @@ enum msg_type {
> SNP_MSG_ABSORB_RSP,
> SNP_MSG_VMRK_REQ,
> SNP_MSG_VMRK_RSP,

<-- Pls leave an empty newline here to denote that there's a hole in the
define numbers. Alternatively, you can add the missing ones too.

> + SNP_MSG_TSC_INFO_REQ = 17,
> + SNP_MSG_TSC_INFO_RSP,
>
> SNP_MSG_TYPE_MAX
> };
> @@ -214,6 +216,23 @@ struct sev_guest_platform_data {
> struct snp_req_data input;
> };
>
> +#define SNP_TSC_INFO_REQ_SZ 128
> +
> +struct snp_tsc_info_req {
> + /* Must be zero filled */

Instead of adding a comment which people might very likely miss, add
a check for that array to warn when it is not zeroed.

> + u8 rsvd[SNP_TSC_INFO_REQ_SZ];
> +} __packed;
> +
> +struct snp_tsc_info_resp {
> + /* Status of TSC_INFO message */

The other struct members don't need a comment?

> + u32 status;
> + u32 rsvd1;
> + u64 tsc_scale;
> + u64 tsc_offset;
> + u32 tsc_factor;
> + u8 rsvd2[100];
> +} __packed;
> +
> struct snp_guest_dev {
> struct device *dev;
> struct miscdevice misc;
> @@ -233,6 +252,7 @@ struct snp_guest_dev {
> struct snp_report_req report;
> struct snp_derived_key_req derived_key;
> struct snp_ext_report_req ext_report;
> + struct snp_tsc_info_req tsc_info;
> } req;
> unsigned int vmpck_id;
> };
> @@ -370,6 +390,8 @@ static inline void *alloc_shared_pages(size_t sz)
>
> return page_address(page);
> }
> +
> +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) { }
> @@ -404,6 +426,7 @@ static inline int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_g
> struct snp_guest_request_ioctl *rio) { return 0; }
> static inline void free_shared_pages(void *buf, size_t sz) { }
> static inline void *alloc_shared_pages(size_t sz) { return NULL; }
> +static inline void __init snp_secure_tsc_prepare(void) { }
> #endif
>
> #ifdef CONFIG_KVM_AMD_SEV
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index 87a7b917d30e..3a8294bbd109 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -410,7 +410,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;
> @@ -542,7 +544,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 a9c1efd6d4e3..20a1e50b7638 100644
> --- a/arch/x86/kernel/sev.c
> +++ b/arch/x86/kernel/sev.c
> @@ -75,6 +75,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 snp_tsc_scale __ro_after_init;
> +static u64 snp_tsc_offset __ro_after_init;
> +
> /* #VC handler runtime per-CPU data */
> struct sev_es_runtime_data {
> struct ghcb ghcb_page;
> @@ -956,6 +960,83 @@ void snp_guest_cmd_unlock(void)
> }
> EXPORT_SYMBOL_GPL(snp_guest_cmd_unlock);
>
> +static struct snp_guest_dev tsc_snp_dev __initdata;
> +
> +static int __snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
> + struct snp_guest_request_ioctl *rio);
> +

Pls design your code without the need for a forward declaration.

> +static int __init snp_get_tsc_info(void)
> +{
> + struct snp_tsc_info_req *tsc_req = &tsc_snp_dev.req.tsc_info;
> + 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_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;

Huh, those both are static buffers. Such checks are done with
BUILD_BUG_ON.

> + 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;

Do that before the memsetting.

> +
> + /* Initialize the PSP channel to send snp messages */
> + 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);

The changes to *snp_send_guest_request are unrelated to the secure TSC
enablement. Pls do them in a pre-patch.

Ok, I'm going to stop here and give you a chance to work in all the
review feedback and send a new revision.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-23 04:23:33

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

On 4/22/2024 6:30 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:18PM +0530, Nikunj A Dadhania wrote:
>> SNP command mutex is used to serialize the shared buffer access, command
>> handling and message sequence number races. Move the SNP guest command
>> mutex out of the sev guest driver and provide accessors to sev-guest
>
> And why in the hell are we doing this?

SNP guest messaging will be moving as part of sev.c, and Secure TSC code
will use this mutex.

> Always, *ALWAYS* make sure a patch's commit message answers *why*
> a change is done. This doesn't explain why so I'm reading "just because"
> and "just because" doesn't fly.

Sure, will change.

>
>> driver. Remove multiple lockdep check in sev-guest driver, next patch adds
>> a single lockdep check in snp_send_guest_request().
>
> The concept of "next patch" is meaningless once the patch is in git.

Sure. As direct access to the mutex was not available now, I had removed lockdep
check here and documented that lockdep gets added at later point.

Regards
Nikunj



2024-04-23 04:28:37

by Nikunj A. Dadhania

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

On 4/22/2024 6:44 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:19PM +0530, Nikunj A Dadhania wrote:
>> Subject: Re: [PATCH v8 07/16] x86/sev: Move and reorganize sev guest request api
>
> s/api/API/g
>
> Please check your whole patchset for proper naming/abbreviations
> spelling, etc.
>
> Another one: s/sev/SEV/g and so on. You should know the drill by now.

Sure.

>> 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
>> API to the sev guest driver for sending guest message and vmpck
>> routines.
>
> Patches which move and reorganize must always be split: first patch(es):
> you do *purely* *mechanical* move without any code changes.

Yes, I had tried that compilation/guest boot does not break at this stage.
That was the reason for intermixing movement and code change.

Let me give a second stab at this and I will try just to make sure compilation does not break.

> Then you do the code changes ontop so that a reviewer can have a chance of seeing
> what you're doing.

Sure

Regards,
Nikunj


2024-04-23 04:35:09

by Nikunj A. Dadhania

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

On 4/22/2024 6:50 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:20PM +0530, Nikunj A Dadhania wrote:
>> diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
>> index d035bce3a2b0..68aa06852466 100644
>> --- a/arch/x86/mm/mem_encrypt.c
>> +++ b/arch/x86/mm/mem_encrypt.c
>> @@ -89,6 +89,8 @@ void __init mem_encrypt_init(void)
>> /* Call into SWIOTLB to update the SWIOTLB DMA buffers */
>> swiotlb_update_mem_attributes();
>>
>> + x86_platform.guest.enc_init();
>> +
>> print_mem_encrypt_feature_info();
>
> Why all this hoopla if all you need is to call it once in mem_encrypt.c?

This was added thinking in mind that any SNP/TDX init can happen in this hook.

> IOW, you can simply call snp_secure_tsc_prepare() there, no?

Yes, that is very simple, will drop this change.

> Those function pointers are to be used in generic code in order to hide
> all the platform-specific hackery but mem_encrypt.c is not really
> generic code, I'd say...
>

Sure.

Regards
Nikunj

2024-04-23 04:40:43

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 09/16] x86/cpufeatures: Add synthetic Secure TSC bit

On 4/22/2024 6:55 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:21PM +0530, Nikunj A Dadhania wrote:
>> Add support for the synthetic CPUID flag which indicates that the SNP
>> guest is running with secure tsc enabled (MSR_AMD64_SEV Bit 11 -
>
> "TSC"

Sure

>
>> SecureTsc_Enabled) . This flag is there so that this capability in the
>> guests can be detected easily without reading MSRs every time accessors.
>
> Why?
>
> What's wrong with cc_platform_has(CC_ATTR_GUEST_SECURE_TSC) or so?
>

That was the initial implementation, and there were review comments[1] to
use synthetic flag.

Regards
Nikunj

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

2024-04-23 04:44:48

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 10/16] x86/sev: Add Secure TSC support for SNP guests

On 4/22/2024 7:20 PM, Borislav Petkov wrote:
> On Thu, Feb 15, 2024 at 05:01:22PM +0530, 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
>
> "CPUs"

Sure

>> 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.
>>
>> Use the guest enc_init hook to fetch SNP TSC info from the AMD Security
>> Processor and initialize the snp_tsc_scale and snp_tsc_offset. During
>> secondary CPU initialization set VMSA fields GUEST_TSC_SCALE (offset 2F0h)
>> and GUEST_TSC_OFFSET(offset 2F8h) with snp_tsc_scale and snp_tsc_offset
>> respectively.
>>
>> Signed-off-by: Nikunj A Dadhania <[email protected]>
>> Tested-by: Peter Gonda <[email protected]>
>> ---
>> arch/x86/include/asm/sev-common.h | 1 +
>> arch/x86/include/asm/sev.h | 23 +++++++
>> arch/x86/include/asm/svm.h | 6 +-
>> arch/x86/kernel/sev.c | 107 ++++++++++++++++++++++++++++--
>> arch/x86/mm/mem_encrypt_amd.c | 6 ++
>> 5 files changed, 134 insertions(+), 9 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
>> index b463fcbd4b90..6adc8e27feeb 100644
>> --- a/arch/x86/include/asm/sev-common.h
>> +++ b/arch/x86/include/asm/sev-common.h
>> @@ -159,6 +159,7 @@ struct snp_psc_desc {
>> #define GHCB_TERM_NOT_VMPL0 3 /* SNP guest is not running at VMPL-0 */
>> #define GHCB_TERM_CPUID 4 /* CPUID-validation failure */
>> #define GHCB_TERM_CPUID_HV 5 /* CPUID failure during hypervisor fallback */
>> +#define GHCB_TERM_SECURE_TSC 6 /* Secure TSC initialization failed */
>>
>> #define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
>>
>> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
>> index d950a3ac5694..16bf5afa7731 100644
>> --- a/arch/x86/include/asm/sev.h
>> +++ b/arch/x86/include/asm/sev.h
>> @@ -170,6 +170,8 @@ enum msg_type {
>> SNP_MSG_ABSORB_RSP,
>> SNP_MSG_VMRK_REQ,
>> SNP_MSG_VMRK_RSP,
>
> <-- Pls leave an empty newline here to denote that there's a hole in the
> define numbers. Alternatively, you can add the missing ones too.

I will add empty line.

>> + SNP_MSG_TSC_INFO_REQ = 17,
>> + SNP_MSG_TSC_INFO_RSP,
>>
>> SNP_MSG_TYPE_MAX
>> };
>> @@ -214,6 +216,23 @@ struct sev_guest_platform_data {
>> struct snp_req_data input;
>> };
>>
>> +#define SNP_TSC_INFO_REQ_SZ 128
>> +
>> +struct snp_tsc_info_req {
>> + /* Must be zero filled */
>
> Instead of adding a comment which people might very likely miss, add
> a check for that array to warn when it is not zeroed.

Sure.

>
>> + u8 rsvd[SNP_TSC_INFO_REQ_SZ];
>> +} __packed;
>> +
>> +struct snp_tsc_info_resp {
>> + /* Status of TSC_INFO message */
>
> The other struct members don't need a comment?

Sure.

>> + u32 status;
>> + u32 rsvd1;
>> + u64 tsc_scale;
>> + u64 tsc_offset;
>> + u32 tsc_factor;
>> + u8 rsvd2[100];
>> +} __packed;
>> +
>> struct snp_guest_dev {
>> struct device *dev;
>> struct miscdevice misc;
>> @@ -233,6 +252,7 @@ struct snp_guest_dev {
>> struct snp_report_req report;
>> struct snp_derived_key_req derived_key;
>> struct snp_ext_report_req ext_report;
>> + struct snp_tsc_info_req tsc_info;
>> } req;
>> unsigned int vmpck_id;
>> };
>> @@ -370,6 +390,8 @@ static inline void *alloc_shared_pages(size_t sz)
>>
>> return page_address(page);
>> }
>> +
>> +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) { }
>> @@ -404,6 +426,7 @@ static inline int snp_send_guest_request(struct snp_guest_dev *dev, struct snp_g
>> struct snp_guest_request_ioctl *rio) { return 0; }
>> static inline void free_shared_pages(void *buf, size_t sz) { }
>> static inline void *alloc_shared_pages(size_t sz) { return NULL; }
>> +static inline void __init snp_secure_tsc_prepare(void) { }
>> #endif
>>
>> #ifdef CONFIG_KVM_AMD_SEV
>> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
>> index 87a7b917d30e..3a8294bbd109 100644
>> --- a/arch/x86/include/asm/svm.h
>> +++ b/arch/x86/include/asm/svm.h
>> @@ -410,7 +410,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;
>> @@ -542,7 +544,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 a9c1efd6d4e3..20a1e50b7638 100644
>> --- a/arch/x86/kernel/sev.c
>> +++ b/arch/x86/kernel/sev.c
>> @@ -75,6 +75,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 snp_tsc_scale __ro_after_init;
>> +static u64 snp_tsc_offset __ro_after_init;
>> +
>> /* #VC handler runtime per-CPU data */
>> struct sev_es_runtime_data {
>> struct ghcb ghcb_page;
>> @@ -956,6 +960,83 @@ void snp_guest_cmd_unlock(void)
>> }
>> EXPORT_SYMBOL_GPL(snp_guest_cmd_unlock);
>>
>> +static struct snp_guest_dev tsc_snp_dev __initdata;
>> +
>> +static int __snp_send_guest_request(struct snp_guest_dev *snp_dev, struct snp_guest_req *req,
>> + struct snp_guest_request_ioctl *rio);
>> +
>
> Pls design your code without the need for a forward declaration.

Sure

>
>> +static int __init snp_get_tsc_info(void)
>> +{
>> + struct snp_tsc_info_req *tsc_req = &tsc_snp_dev.req.tsc_info;
>> + 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_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;
>
> Huh, those both are static buffers. Such checks are done with
> BUILD_BUG_ON.

Ok

>
>> + 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;
>
> Do that before the memsetting.
>

Sure

>> +
>> + /* Initialize the PSP channel to send snp messages */
>> + 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);
>
> The changes to *snp_send_guest_request are unrelated to the secure TSC
> enablement. Pls do them in a pre-patch.

Sure

>
> Ok, I'm going to stop here and give you a chance to work in all the
> review feedback and send a new revision.

Thank you for detailed review/feedback, will address them and send new revision.

Regards
Nikunj


2024-04-23 10:29:28

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

On Tue, Apr 23, 2024 at 09:52:41AM +0530, Nikunj A. Dadhania wrote:
> SNP guest messaging will be moving as part of sev.c, and Secure TSC code
> will use this mutex.

No, this is all backwards.

You have a *static* function in sev-guest - snp_guest_ioctl- which takes
an exported lock - snp_guest_cmd_lock - in order to synchronize with
other callers which are only in that same sev-guest driver.

Why do you even need the guest messaging in sev.c?

I guess this: "Many of the required functions are implemented in the
sev-guest driver and therefore not available at early boot."

But then your API is misdesigned: the lock should be private to sev.c
and none of the callers should pay attention to grabbing it - the
callers simply call the functions and underneath the locking works
automatically for them - they don't care. Just like any other shared
resource, users see only the API they call and the actual
synchronization is done behind the scenes.

Sounds like you need to go back to the drawing board and think how this
thing should look like.

And when you have it, make sure to explain the commit messages *why* it
is done this way.

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-23 10:42:34

by Nikunj A. Dadhania

[permalink] [raw]
Subject: Re: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

On 4/23/2024 3:58 PM, Borislav Petkov wrote:
> On Tue, Apr 23, 2024 at 09:52:41AM +0530, Nikunj A. Dadhania wrote:
>> SNP guest messaging will be moving as part of sev.c, and Secure TSC code
>> will use this mutex.
>
> No, this is all backwards.
>
> You have a *static* function in sev-guest - snp_guest_ioctl- which takes
> an exported lock - snp_guest_cmd_lock - in order to synchronize with
> other callers which are only in that same sev-guest driver.
>
> Why do you even need the guest messaging in sev.c?
>
> I guess this: "Many of the required functions are implemented in the
> sev-guest driver and therefore not available at early boot."

Yes.

>
> But then your API is misdesigned: the lock should be private to sev.c
> and none of the callers should pay attention to grabbing it - the
> callers simply call the functions and underneath the locking works
> automatically for them - they don't care. Just like any other shared
> resource, users see only the API they call and the actual
> synchronization is done behind the scenes.
>
> Sounds like you need to go back to the drawing board and think how this
> thing should look like.

Something like below ?

snp_guest_ioctl()
-> get_report()/get_derived_key()/get_ext_report()
-> snp_send_guest_request()
snp_guest_cmd_lock();
...
snp_guest_cmd_lock();

With this the cmd_lock will be private to sev.c and lock/unlock function
doesn't need to be exported.

Regards
Nikunj

2024-04-23 11:22:44

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 06/16] virt: sev-guest: Move SNP Guest command mutex

On Tue, Apr 23, 2024 at 04:12:00PM +0530, Nikunj A. Dadhania wrote:
> Something like below ?
>
> snp_guest_ioctl()
> -> get_report()/get_derived_key()/get_ext_report()
> -> snp_send_guest_request()
> snp_guest_cmd_lock();
> ...
> snp_guest_cmd_lock();
>
> With this the cmd_lock will be private to sev.c and lock/unlock function
> doesn't need to be exported.

Yes, something like that.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-23 13:29:55

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v8 09/16] x86/cpufeatures: Add synthetic Secure TSC bit

On Tue, Apr 23, 2024 at 10:10:14AM +0530, Nikunj A. Dadhania wrote:
> 1. https://lore.kernel.org/lkml/[email protected]/

I have no clue what "We have better instrumentation around features."
means and am not convinced.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette

2024-04-23 13:54:58

by Borislav Petkov

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

On Tue, Apr 23, 2024 at 09:56:38AM +0530, Nikunj A. Dadhania wrote:
> Yes, I had tried that compilation/guest boot does not break at this
> stage. That was the reason for intermixing movement and code change.
>
> Let me give a second stab at this and I will try just to make sure
> compilation does not break.

Yes, you can also do preparatory changes which get removed later, if
that helps.

It is perfectly fine to have a couple more patches preparing and doing
the move which are trivial to review and verify vs one combo patch which
makes you stare at it a long time and you're still not sure it doesn't
change something...

Thx.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette