162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Confidential Computing Platform Capability checks
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Tom Lendacky <thomas.lendacky@amd.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/cc_platform.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/coco.h>
1462306a36Sopenharmony_ci#include <asm/processor.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cienum cc_vendor cc_vendor __ro_after_init = CC_VENDOR_NONE;
1762306a36Sopenharmony_cistatic u64 cc_mask __ro_after_init;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic bool noinstr intel_cc_platform_has(enum cc_attr attr)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	switch (attr) {
2262306a36Sopenharmony_ci	case CC_ATTR_GUEST_UNROLL_STRING_IO:
2362306a36Sopenharmony_ci	case CC_ATTR_HOTPLUG_DISABLED:
2462306a36Sopenharmony_ci	case CC_ATTR_GUEST_MEM_ENCRYPT:
2562306a36Sopenharmony_ci	case CC_ATTR_MEM_ENCRYPT:
2662306a36Sopenharmony_ci		return true;
2762306a36Sopenharmony_ci	default:
2862306a36Sopenharmony_ci		return false;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Handle the SEV-SNP vTOM case where sme_me_mask is zero, and
3462306a36Sopenharmony_ci * the other levels of SME/SEV functionality, including C-bit
3562306a36Sopenharmony_ci * based SEV-SNP, are not enabled.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	switch (attr) {
4062306a36Sopenharmony_ci	case CC_ATTR_GUEST_MEM_ENCRYPT:
4162306a36Sopenharmony_ci	case CC_ATTR_MEM_ENCRYPT:
4262306a36Sopenharmony_ci		return true;
4362306a36Sopenharmony_ci	default:
4462306a36Sopenharmony_ci		return false;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * SME and SEV are very similar but they are not the same, so there are
5062306a36Sopenharmony_ci * times that the kernel will need to distinguish between SME and SEV. The
5162306a36Sopenharmony_ci * cc_platform_has() function is used for this.  When a distinction isn't
5262306a36Sopenharmony_ci * needed, the CC_ATTR_MEM_ENCRYPT attribute can be used.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * The trampoline code is a good example for this requirement.  Before
5562306a36Sopenharmony_ci * paging is activated, SME will access all memory as decrypted, but SEV
5662306a36Sopenharmony_ci * will access all memory as encrypted.  So, when APs are being brought
5762306a36Sopenharmony_ci * up under SME the trampoline area cannot be encrypted, whereas under SEV
5862306a36Sopenharmony_ci * the trampoline area must be encrypted.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic bool noinstr amd_cc_platform_has(enum cc_attr attr)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci#ifdef CONFIG_AMD_MEM_ENCRYPT
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (sev_status & MSR_AMD64_SNP_VTOM)
6662306a36Sopenharmony_ci		return amd_cc_platform_vtom(attr);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	switch (attr) {
6962306a36Sopenharmony_ci	case CC_ATTR_MEM_ENCRYPT:
7062306a36Sopenharmony_ci		return sme_me_mask;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	case CC_ATTR_HOST_MEM_ENCRYPT:
7362306a36Sopenharmony_ci		return sme_me_mask && !(sev_status & MSR_AMD64_SEV_ENABLED);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	case CC_ATTR_GUEST_MEM_ENCRYPT:
7662306a36Sopenharmony_ci		return sev_status & MSR_AMD64_SEV_ENABLED;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	case CC_ATTR_GUEST_STATE_ENCRYPT:
7962306a36Sopenharmony_ci		return sev_status & MSR_AMD64_SEV_ES_ENABLED;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * With SEV, the rep string I/O instructions need to be unrolled
8362306a36Sopenharmony_ci	 * but SEV-ES supports them through the #VC handler.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	case CC_ATTR_GUEST_UNROLL_STRING_IO:
8662306a36Sopenharmony_ci		return (sev_status & MSR_AMD64_SEV_ENABLED) &&
8762306a36Sopenharmony_ci			!(sev_status & MSR_AMD64_SEV_ES_ENABLED);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	case CC_ATTR_GUEST_SEV_SNP:
9062306a36Sopenharmony_ci		return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	default:
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci#else
9662306a36Sopenharmony_ci	return false;
9762306a36Sopenharmony_ci#endif
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cibool noinstr cc_platform_has(enum cc_attr attr)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	switch (cc_vendor) {
10362306a36Sopenharmony_ci	case CC_VENDOR_AMD:
10462306a36Sopenharmony_ci		return amd_cc_platform_has(attr);
10562306a36Sopenharmony_ci	case CC_VENDOR_INTEL:
10662306a36Sopenharmony_ci		return intel_cc_platform_has(attr);
10762306a36Sopenharmony_ci	default:
10862306a36Sopenharmony_ci		return false;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cc_platform_has);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciu64 cc_mkenc(u64 val)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	/*
11662306a36Sopenharmony_ci	 * Both AMD and Intel use a bit in the page table to indicate
11762306a36Sopenharmony_ci	 * encryption status of the page.
11862306a36Sopenharmony_ci	 *
11962306a36Sopenharmony_ci	 * - for AMD, bit *set* means the page is encrypted
12062306a36Sopenharmony_ci	 * - for AMD with vTOM and for Intel, *clear* means encrypted
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	switch (cc_vendor) {
12362306a36Sopenharmony_ci	case CC_VENDOR_AMD:
12462306a36Sopenharmony_ci		if (sev_status & MSR_AMD64_SNP_VTOM)
12562306a36Sopenharmony_ci			return val & ~cc_mask;
12662306a36Sopenharmony_ci		else
12762306a36Sopenharmony_ci			return val | cc_mask;
12862306a36Sopenharmony_ci	case CC_VENDOR_INTEL:
12962306a36Sopenharmony_ci		return val & ~cc_mask;
13062306a36Sopenharmony_ci	default:
13162306a36Sopenharmony_ci		return val;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciu64 cc_mkdec(u64 val)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	/* See comment in cc_mkenc() */
13862306a36Sopenharmony_ci	switch (cc_vendor) {
13962306a36Sopenharmony_ci	case CC_VENDOR_AMD:
14062306a36Sopenharmony_ci		if (sev_status & MSR_AMD64_SNP_VTOM)
14162306a36Sopenharmony_ci			return val | cc_mask;
14262306a36Sopenharmony_ci		else
14362306a36Sopenharmony_ci			return val & ~cc_mask;
14462306a36Sopenharmony_ci	case CC_VENDOR_INTEL:
14562306a36Sopenharmony_ci		return val | cc_mask;
14662306a36Sopenharmony_ci	default:
14762306a36Sopenharmony_ci		return val;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cc_mkdec);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci__init void cc_set_mask(u64 mask)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	cc_mask = mask;
15562306a36Sopenharmony_ci}
156