From patchwork Wed Jul 31 15:08:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nikunj A. Dadhania" X-Patchwork-Id: 13748889 Received: from NAM04-MW2-obe.outbound.protection.outlook.com (mail-mw2nam04on2054.outbound.protection.outlook.com [40.107.101.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C8F261C0DC6; Wed, 31 Jul 2024 15:09:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.101.54 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722438561; cv=fail; b=F9GYb0p4Z2TOxTOjicnAnsqz1kM6T/nr88d64LS7Sjbvrryywf5SYz7x3nuMvA+SRjNwiG9KHyQRtXIaGrCXpRTPRq0dTrFxQ0EZnqVliy92hwgw8nk2x4ltMd7OBBrKGBaFIuILpx+frBwl3+wm6f/kEyGPV4/zzS4xUezlyuI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722438561; c=relaxed/simple; bh=APJb6aRVvzsrUk/klZpQIYKBKlkdnu2FYzpfCfDHVNo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=B2IaG0vYU3EwYjf1pd3MjZEzzqIV6qm8sP6uy5N/m+zO7bkHMX7bL0kAqeDOJ7gXDXbU5DuOYfJKwHtr9MD/W+UpmGnkKHN/6N9AB+9JvOAbYNQC5KLxwiGP0e98Up/EdqIJDBG/1fcHzbCnC9epcidtBzeqC61xE9s7QbBuDn4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com; spf=fail smtp.mailfrom=amd.com; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b=g50nkpPX; arc=fail smtp.client-ip=40.107.101.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=amd.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=amd.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=amd.com header.i=@amd.com header.b="g50nkpPX" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=EYPMDLflokyz7LQoXPuwVDPBEfuFiMTYt7pNwE5xxamDN4PCCphrpIAdnVZQv9u3X/0eQVR/y77BsZGpYmuX4NAmbpjGqmAi9qtf5GCr4mMcwr1BTOpPob7/FyEQqRP18yR3zv/nqLfaRcSwx/v9uziGRrEMP6r28EFoAfvR0lKCEeyIDDOsA2zivXLh2ZqVqj+B0GT+UvNu5CsNDH1e0Xjtb5sF6RyTpW6E5EfBK5TxvsWi2+B8h0lfW2Z+PwAywL1CIhM355PlUIopwHgXD1SUe01W1WKO3FKhyz8T1bnbFMkv0M/+VVpilWB2aFvd+PdOyzd5aFWUY3tn1Xrzig== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=+XZk/lUnDYxhTXYX4tGAqZb3sUBgHXEjwvhBs4mJL8M=; b=Ef18Uy//VgG5Gb8HpNqWl2lSN4gBtmBRBCFwmKmcoHHix8Qk2so3skbCONyLraXcjytkw0K/2Bby//6tvHpuSrUNs+aTrRpGwshaMY/Sjn5sVlmh0IIp0KOnKZleW9Wt96eBCeUFDMACIb65WKtky0+KXvEjLJFChD+4LHkyBemuqjyKvBv5d8Fp3+Lta/EV5TibJekcALzUOyHRUiu7xourEjc+w7fEhaoiT/Ug0wyPxNgOWMM2jWZDnpaGf3nR6F3ptHQ/R5z2kDuivqBHhd+wJDVjP6GlWXiYH1SmbS2pEELOSiT2/2hvx3L2bre9KRLbI43fii4C0n6WtadUrA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=vger.kernel.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=+XZk/lUnDYxhTXYX4tGAqZb3sUBgHXEjwvhBs4mJL8M=; b=g50nkpPXrYeStUrUjLTDud16jq5DXzShtbePRUQhdPjwZHzPjboQgFtxzEDhiiaAJZlE6bm8ZoCS7MjYcGmTzqOyWU1/7okOWdQgvk/vYcw8nU8Uzvusq+S1eqys+gDy32g814w37R1GOfjUyxOeOKUFklDF0EX4zXl/BscnXxQ= Received: from CH0PR03CA0309.namprd03.prod.outlook.com (2603:10b6:610:118::30) by MN0PR12MB5811.namprd12.prod.outlook.com (2603:10b6:208:377::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7807.28; Wed, 31 Jul 2024 15:09:15 +0000 Received: from DS3PEPF0000C381.namprd04.prod.outlook.com (2603:10b6:610:118:cafe::cf) by CH0PR03CA0309.outlook.office365.com (2603:10b6:610:118::30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7784.34 via Frontend Transport; Wed, 31 Jul 2024 15:09:15 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=SATLEXMB04.amd.com; pr=C Received: from SATLEXMB04.amd.com (165.204.84.17) by DS3PEPF0000C381.mail.protection.outlook.com (10.167.23.11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.7828.19 via Frontend Transport; Wed, 31 Jul 2024 15:09:14 +0000 Received: from gomati.amd.com (10.180.168.240) by SATLEXMB04.amd.com (10.181.40.145) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Wed, 31 Jul 2024 10:09:10 -0500 From: Nikunj A Dadhania To: , , , , CC: , , , , , , Subject: [PATCH v11 12/20] x86/sev: Relocate SNP guest messaging routines to common code Date: Wed, 31 Jul 2024 20:38:03 +0530 Message-ID: <20240731150811.156771-13-nikunj@amd.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240731150811.156771-1-nikunj@amd.com> References: <20240731150811.156771-1-nikunj@amd.com> Precedence: bulk X-Mailing-List: kvm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: SATLEXMB04.amd.com (10.181.40.145) To SATLEXMB04.amd.com (10.181.40.145) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DS3PEPF0000C381:EE_|MN0PR12MB5811:EE_ X-MS-Office365-Filtering-Correlation-Id: ba522584-a26d-4395-90bf-08dcb172bdc7 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|82310400026|36860700013|7416014|376014|1800799024; X-Microsoft-Antispam-Message-Info: gPUUmVdnWyqnFHfF7/Ub0xsfPLi/DUazrFCmyFeZ3Sp5cCGSxfcBGVT1t+4Pn4FUXUM1j57QtrQIhslo5l0OGkpsQQvdZuCIQfjzi7Thnh3LKG9NiI1AwR7aWrqVull/NPI1z6bM99Tb07c103JNrZltNBD6pC6m15kZHzFsCMYQQWG1L6Mq0pV2RHV8AXSbW/xnfFKu8qZ75MDtgvQUmbzjJIEwO9S3PonP2EcyUVsVCGbF7PD5mpzD1rSbXgyylFXw4sElhX8w6X7srqPuWuzQM9043WJvu8OXmPv3/gAeU/qujpRABcX6tImJQAJKuSFL+Taz0VyOLG+2gAqwI44zZisLeFn2Pg15QeAgF2rHqJo+HLNFvKyyDvdKlw9kK7AsXYx6SzlPbG3zbjv2KeDgVen+0a040PE/vNNWIU/G7YhV2x8eSHAZUu55XmPCtfcevMEDwpXrk2pM8Z5Xgavn9etZi+kytO9DbP7Et31KznXiMkhKOP+OoLh+s2ZxGqcTQ5Dk7s3kbGSz668XN8+VeXX5NUQiSKvj042oy9kz5DN/fLkHD1Da/qSBf0IrMOtjBMpRX48xMjEw5XPGM+pk85t5t3DSUjuGgjhx38tETvI1KMwb7bDrXVVf1g8r3aVeyarh4gwCKkl5dtlF5zJZbrU4sAWMt87V/fUu7nOyNnDWuTkm4wj9acNdW4hnznXAeYzFYEGAmdkcz2NjQqtOfVqWEYlxbWljnanSm2RcW3HJZKuHBXqmwCPXrwdOYcGXwmZ4Rd4mDuXhq5JgIXRzgp3bJX3Dg5Fuwz8slHV85xeGBWNb6skY7DkGYwjMZHeN2dgvyk7aVxXSqo6DRyV8+v/ZR2vR/3JJZAfyS2TLhQkFOb2vIuI1PP/EJj+0uk0domE8+bM0QpX3h1S2NI2+o1ZnrURLZjjP6o4831DnPD8pIFCjwK54C6an+LLZvbr+1qj4xzvCtrf/gtdEq2+CObDZfIuiBcidy5iw0QeZR4SJwVTGzA4+Un7DEQDBBWSYCgnZMyIfasesqV8IfNEuHSN8OJ/qJAi2qaDY1vv9bN6rOxU1m7DpBCrtJWpO84wuOI+6sII+k0HldCTMSC6akK7oJKyRd9nujrJRFLsqWjzdZ1C12cbHzkHE15IbnHJnMytv6Y0Hufr+gtroHb5NEXO5l23jeKpq4+KTNgRoUJWfTd1jk8TIgj28f47/IDtolXocePzQH2hPqZrbPr7R5+DdVMc6JBt957bb+AWSjFl4h0KueMSwdD5whn8IDKxsHi+lyA01q0cbGrGM0970BB/S05YzEWoiuWnGsHhsj7A2u8nNfughJMS/4UV++V0TPgzf7bvw75wOhEPpwOjV7O5OZn9BZaXA540fLapyO0g7mZDiJevHZOW54X1C27yx57zT+4f/OsaPE8qfRsQT0OVNFpzlklfY2B33ABIK+fQamSfcvzfO9wAr0fYb X-Forefront-Antispam-Report: CIP:165.204.84.17;CTRY:US;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:SATLEXMB04.amd.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230040)(82310400026)(36860700013)(7416014)(376014)(1800799024);DIR:OUT;SFP:1101; X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 31 Jul 2024 15:09:14.7034 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: ba522584-a26d-4395-90bf-08dcb172bdc7 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.17];Helo=[SATLEXMB04.amd.com] X-MS-Exchange-CrossTenant-AuthSource: DS3PEPF0000C381.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN0PR12MB5811 At present, the SEV guest driver exclusively handles SNP guest messaging. All routines for sending guest messages are embedded within the guest driver. To support Secure TSC, SEV-SNP guests must communicate with the AMD Security Processor during early boot. However, these guest messaging functions are not accessible during early boot since they are currently part of the guest driver. Hence, relocate the core SNP guest messaging functions to SEV common code and provide an API for sending SNP guest messages. No functional change, but just an export symbol. Signed-off-by: Nikunj A Dadhania Reviewed-by: Tom Lendacky --- arch/x86/include/asm/sev.h | 8 + arch/x86/coco/sev/core.c | 284 +++++++++++++++++++++++ drivers/virt/coco/sev-guest/sev-guest.c | 286 ------------------------ arch/x86/Kconfig | 1 + drivers/virt/coco/sev-guest/Kconfig | 1 - 5 files changed, 293 insertions(+), 287 deletions(-) diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h index 3812692ba3fe..eda435eba53e 100644 --- a/arch/x86/include/asm/sev.h +++ b/arch/x86/include/asm/sev.h @@ -125,6 +125,9 @@ struct snp_req_data { #define AAD_LEN 48 #define MSG_HDR_VER 1 +#define SNP_REQ_MAX_RETRY_DURATION (60*HZ) +#define SNP_REQ_RETRY_DELAY (2*HZ) + /* See SNP spec SNP_GUEST_REQUEST section for the structure */ enum msg_type { SNP_MSG_TYPE_INVALID = 0, @@ -493,6 +496,9 @@ static inline void snp_msg_cleanup(struct snp_msg_desc *mdesc) kfree(mdesc->ctx); } +int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, + struct snp_guest_request_ioctl *rio); + #else /* !CONFIG_AMD_MEM_ENCRYPT */ #define snp_vmpl 0 @@ -537,6 +543,8 @@ static inline int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id) { retur static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; } static inline void snp_msg_cleanup(struct snp_msg_desc *mdesc) { } +static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, + struct snp_guest_request_ioctl *rio) { return -ENODEV; } #endif /* CONFIG_AMD_MEM_ENCRYPT */ diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index 6787d0972a45..aaeeca938265 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -2697,3 +2697,287 @@ struct snp_msg_desc *snp_msg_alloc(void) return ERR_PTR(-ENOMEM); } EXPORT_SYMBOL_GPL(snp_msg_alloc); + +/* Mutex to serialize the shared buffer access and command handling. */ +static DEFINE_MUTEX(snp_cmd_mutex); + +/* + * 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_msg_desc *mdesc) +{ + pr_alert("Disabling VMPCK%d communication key to prevent IV reuse.\n", + mdesc->vmpck_id); + memzero_explicit(mdesc->vmpck, VMPCK_KEY_LEN); + mdesc->vmpck = NULL; +} + +static inline u64 __snp_get_msg_seqno(struct snp_msg_desc *mdesc) +{ + u64 count; + + lockdep_assert_held(&snp_cmd_mutex); + + /* Read the current message sequence counter from secrets pages */ + count = *mdesc->os_area_msg_seqno; + + return count + 1; +} + +/* Return a non-zero on success */ +static u64 snp_get_msg_seqno(struct snp_msg_desc *mdesc) +{ + u64 count = __snp_get_msg_seqno(mdesc); + + /* + * 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("request message sequence counter overflow\n"); + return 0; + } + + return count; +} + +static void snp_inc_msg_seqno(struct snp_msg_desc *mdesc) +{ + /* + * The counter is also incremented by the PSP, so increment it by 2 + * and save in secrets page. + */ + *mdesc->os_area_msg_seqno += 2; +} + +static int verify_and_dec_payload(struct snp_msg_desc *mdesc, struct snp_guest_req *req) +{ + struct snp_guest_msg *resp_msg = &mdesc->secret_response; + struct snp_guest_msg *req_msg = &mdesc->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 = mdesc->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, mdesc->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_msg_desc *mdesc, u64 seqno, struct snp_guest_req *req) +{ + struct snp_guest_msg *msg = &mdesc->secret_request; + struct snp_guest_msg_hdr *hdr = &msg->hdr; + struct aesgcm_ctx *ctx = mdesc->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_msg_desc *mdesc, 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, &mdesc->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 = mdesc->input.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(mdesc); + + 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) + mdesc->input.data_npages = override_npages; + + return rc; +} + +int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, + struct snp_guest_request_ioctl *rio) +{ + u64 seqno; + int rc; + + guard(mutex)(&snp_cmd_mutex); + + /* Get message sequence and verify that its a non-zero */ + seqno = snp_get_msg_seqno(mdesc); + if (!seqno) + return -EIO; + + /* Clear shared memory's response for the host to populate. */ + memset(mdesc->response, 0, sizeof(struct snp_guest_msg)); + + /* Encrypt the userspace provided payload in mdesc->secret_request. */ + rc = enc_payload(mdesc, seqno, req); + if (rc) + return rc; + + /* + * Write the fully encrypted request to the shared unencrypted + * request page. + */ + memcpy(mdesc->request, &mdesc->secret_request, + sizeof(mdesc->secret_request)); + + rc = __handle_guest_request(mdesc, req, rio); + 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(mdesc); + return rc; + } + + rc = verify_and_dec_payload(mdesc, req); + if (rc) { + pr_alert("Detected unexpected decode failure from ASP. rc: %d\n", rc); + snp_disable_vmpck(mdesc); + return rc; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snp_send_guest_request); diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c index 40509fe18658..019eca753f85 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -31,9 +31,6 @@ #define DEVICE_NAME "sev-guest" -#define SNP_REQ_MAX_RETRY_DURATION (60*HZ) -#define SNP_REQ_RETRY_DELAY (2*HZ) - #define SVSM_MAX_RETRIES 3 struct snp_guest_dev { @@ -60,76 +57,6 @@ static int vmpck_id = -1; module_param(vmpck_id, int, 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); - -/* - * 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_msg_desc *mdesc) -{ - pr_alert("Disabling VMPCK%d communication key to prevent IV reuse.\n", - mdesc->vmpck_id); - memzero_explicit(mdesc->vmpck, VMPCK_KEY_LEN); - mdesc->vmpck = NULL; -} - -static inline u64 __snp_get_msg_seqno(struct snp_msg_desc *mdesc) -{ - u64 count; - - lockdep_assert_held(&snp_cmd_mutex); - - /* Read the current message sequence counter from secrets pages */ - count = *mdesc->os_area_msg_seqno; - - return count + 1; -} - -/* Return a non-zero on success */ -static u64 snp_get_msg_seqno(struct snp_msg_desc *mdesc) -{ - u64 count = __snp_get_msg_seqno(mdesc); - - /* - * 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("request message sequence counter overflow\n"); - return 0; - } - - return count; -} - -static void snp_inc_msg_seqno(struct snp_msg_desc *mdesc) -{ - /* - * The counter is also incremented by the PSP, so increment it by 2 - * and save in secrets page. - */ - *mdesc->os_area_msg_seqno += 2; -} - static inline struct snp_guest_dev *to_snp_dev(struct file *file) { struct miscdevice *dev = file->private_data; @@ -137,219 +64,6 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file) return container_of(dev, struct snp_guest_dev, misc); } -static int verify_and_dec_payload(struct snp_msg_desc *mdesc, struct snp_guest_req *req) -{ - struct snp_guest_msg *resp_msg = &mdesc->secret_response; - struct snp_guest_msg *req_msg = &mdesc->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 = mdesc->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, mdesc->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_msg_desc *mdesc, u64 seqno, struct snp_guest_req *req) -{ - struct snp_guest_msg *msg = &mdesc->secret_request; - struct snp_guest_msg_hdr *hdr = &msg->hdr; - struct aesgcm_ctx *ctx = mdesc->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_msg_desc *mdesc, 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, &mdesc->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 = mdesc->input.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(mdesc); - - 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) - mdesc->input.data_npages = override_npages; - - return rc; -} - -static int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req, - struct snp_guest_request_ioctl *rio) -{ - u64 seqno; - int rc; - - guard(mutex)(&snp_cmd_mutex); - - /* Get message sequence and verify that its a non-zero */ - seqno = snp_get_msg_seqno(mdesc); - if (!seqno) - return -EIO; - - /* Clear shared memory's response for the host to populate. */ - memset(mdesc->response, 0, sizeof(struct snp_guest_msg)); - - /* Encrypt the userspace provided payload in mdesc->secret_request. */ - rc = enc_payload(mdesc, seqno, req); - if (rc) - return rc; - - /* - * Write the fully encrypted request to the shared unencrypted - * request page. - */ - memcpy(mdesc->request, &mdesc->secret_request, - sizeof(mdesc->secret_request)); - - rc = __handle_guest_request(mdesc, req, rio); - 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(mdesc); - return rc; - } - - rc = verify_and_dec_payload(mdesc, req); - if (rc) { - pr_alert("Detected unexpected decode failure from ASP. rc: %d\n", rc); - snp_disable_vmpck(mdesc); - return rc; - } - - return 0; -} - static int handle_guest_request(struct snp_msg_desc *mdesc, u64 exit_code, struct snp_guest_request_ioctl *rio, u8 type, void *req_buf, size_t req_sz, void *resp_buf, diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 007bab9f2a0e..45060e7cea48 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1552,6 +1552,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/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