xref: /openbmc/linux/arch/x86/coco/core.c (revision c15b5c6f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Confidential Computing Platform Capability checks
4  *
5  * Copyright (C) 2021 Advanced Micro Devices, Inc.
6  * Copyright (C) 2024 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
7  *
8  * Author: Tom Lendacky <thomas.lendacky@amd.com>
9  */
10 
11 #include <linux/export.h>
12 #include <linux/cc_platform.h>
13 #include <linux/string.h>
14 #include <linux/random.h>
15 
16 #include <asm/archrandom.h>
17 #include <asm/coco.h>
18 #include <asm/processor.h>
19 
20 enum cc_vendor cc_vendor __ro_after_init = CC_VENDOR_NONE;
21 u64 cc_mask __ro_after_init;
22 
23 static bool noinstr intel_cc_platform_has(enum cc_attr attr)
24 {
25 	switch (attr) {
26 	case CC_ATTR_GUEST_UNROLL_STRING_IO:
27 	case CC_ATTR_HOTPLUG_DISABLED:
28 	case CC_ATTR_GUEST_MEM_ENCRYPT:
29 	case CC_ATTR_MEM_ENCRYPT:
30 		return true;
31 	default:
32 		return false;
33 	}
34 }
35 
36 /*
37  * Handle the SEV-SNP vTOM case where sme_me_mask is zero, and
38  * the other levels of SME/SEV functionality, including C-bit
39  * based SEV-SNP, are not enabled.
40  */
41 static __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr)
42 {
43 	switch (attr) {
44 	case CC_ATTR_GUEST_MEM_ENCRYPT:
45 	case CC_ATTR_MEM_ENCRYPT:
46 		return true;
47 	default:
48 		return false;
49 	}
50 }
51 
52 /*
53  * SME and SEV are very similar but they are not the same, so there are
54  * times that the kernel will need to distinguish between SME and SEV. The
55  * cc_platform_has() function is used for this.  When a distinction isn't
56  * needed, the CC_ATTR_MEM_ENCRYPT attribute can be used.
57  *
58  * The trampoline code is a good example for this requirement.  Before
59  * paging is activated, SME will access all memory as decrypted, but SEV
60  * will access all memory as encrypted.  So, when APs are being brought
61  * up under SME the trampoline area cannot be encrypted, whereas under SEV
62  * the trampoline area must be encrypted.
63  */
64 
65 static bool noinstr amd_cc_platform_has(enum cc_attr attr)
66 {
67 #ifdef CONFIG_AMD_MEM_ENCRYPT
68 
69 	if (sev_status & MSR_AMD64_SNP_VTOM)
70 		return amd_cc_platform_vtom(attr);
71 
72 	switch (attr) {
73 	case CC_ATTR_MEM_ENCRYPT:
74 		return sme_me_mask;
75 
76 	case CC_ATTR_HOST_MEM_ENCRYPT:
77 		return sme_me_mask && !(sev_status & MSR_AMD64_SEV_ENABLED);
78 
79 	case CC_ATTR_GUEST_MEM_ENCRYPT:
80 		return sev_status & MSR_AMD64_SEV_ENABLED;
81 
82 	case CC_ATTR_GUEST_STATE_ENCRYPT:
83 		return sev_status & MSR_AMD64_SEV_ES_ENABLED;
84 
85 	/*
86 	 * With SEV, the rep string I/O instructions need to be unrolled
87 	 * but SEV-ES supports them through the #VC handler.
88 	 */
89 	case CC_ATTR_GUEST_UNROLL_STRING_IO:
90 		return (sev_status & MSR_AMD64_SEV_ENABLED) &&
91 			!(sev_status & MSR_AMD64_SEV_ES_ENABLED);
92 
93 	case CC_ATTR_GUEST_SEV_SNP:
94 		return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
95 
96 	default:
97 		return false;
98 	}
99 #else
100 	return false;
101 #endif
102 }
103 
104 bool noinstr cc_platform_has(enum cc_attr attr)
105 {
106 	switch (cc_vendor) {
107 	case CC_VENDOR_AMD:
108 		return amd_cc_platform_has(attr);
109 	case CC_VENDOR_INTEL:
110 		return intel_cc_platform_has(attr);
111 	default:
112 		return false;
113 	}
114 }
115 EXPORT_SYMBOL_GPL(cc_platform_has);
116 
117 u64 cc_mkenc(u64 val)
118 {
119 	/*
120 	 * Both AMD and Intel use a bit in the page table to indicate
121 	 * encryption status of the page.
122 	 *
123 	 * - for AMD, bit *set* means the page is encrypted
124 	 * - for AMD with vTOM and for Intel, *clear* means encrypted
125 	 */
126 	switch (cc_vendor) {
127 	case CC_VENDOR_AMD:
128 		if (sev_status & MSR_AMD64_SNP_VTOM)
129 			return val & ~cc_mask;
130 		else
131 			return val | cc_mask;
132 	case CC_VENDOR_INTEL:
133 		return val & ~cc_mask;
134 	default:
135 		return val;
136 	}
137 }
138 
139 u64 cc_mkdec(u64 val)
140 {
141 	/* See comment in cc_mkenc() */
142 	switch (cc_vendor) {
143 	case CC_VENDOR_AMD:
144 		if (sev_status & MSR_AMD64_SNP_VTOM)
145 			return val | cc_mask;
146 		else
147 			return val & ~cc_mask;
148 	case CC_VENDOR_INTEL:
149 		return val | cc_mask;
150 	default:
151 		return val;
152 	}
153 }
154 EXPORT_SYMBOL_GPL(cc_mkdec);
155 
156 __init void cc_random_init(void)
157 {
158 	/*
159 	 * The seed is 32 bytes (in units of longs), which is 256 bits, which
160 	 * is the security level that the RNG is targeting.
161 	 */
162 	unsigned long rng_seed[32 / sizeof(long)];
163 	size_t i, longs;
164 
165 	if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
166 		return;
167 
168 	/*
169 	 * Since the CoCo threat model includes the host, the only reliable
170 	 * source of entropy that can be neither observed nor manipulated is
171 	 * RDRAND. Usually, RDRAND failure is considered tolerable, but since
172 	 * CoCo guests have no other unobservable source of entropy, it's
173 	 * important to at least ensure the RNG gets some initial random seeds.
174 	 */
175 	for (i = 0; i < ARRAY_SIZE(rng_seed); i += longs) {
176 		longs = arch_get_random_longs(&rng_seed[i], ARRAY_SIZE(rng_seed) - i);
177 
178 		/*
179 		 * A zero return value means that the guest doesn't have RDRAND
180 		 * or the CPU is physically broken, and in both cases that
181 		 * means most crypto inside of the CoCo instance will be
182 		 * broken, defeating the purpose of CoCo in the first place. So
183 		 * just panic here because it's absolutely unsafe to continue
184 		 * executing.
185 		 */
186 		if (longs == 0)
187 			panic("RDRAND is defective.");
188 	}
189 	add_device_randomness(rng_seed, sizeof(rng_seed));
190 	memzero_explicit(rng_seed, sizeof(rng_seed));
191 }
192