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 struct cc_attr_flags { |
24 | __u64 host_sev_snp : 1, |
25 | __resv : 63; |
26 | } cc_flags; |
27 | |
28 | static bool noinstr intel_cc_platform_has(enum cc_attr attr) |
29 | { |
30 | switch (attr) { |
31 | case CC_ATTR_GUEST_UNROLL_STRING_IO: |
32 | case CC_ATTR_HOTPLUG_DISABLED: |
33 | case CC_ATTR_GUEST_MEM_ENCRYPT: |
34 | case CC_ATTR_MEM_ENCRYPT: |
35 | return true; |
36 | default: |
37 | return false; |
38 | } |
39 | } |
40 | |
41 | /* |
42 | * Handle the SEV-SNP vTOM case where sme_me_mask is zero, and |
43 | * the other levels of SME/SEV functionality, including C-bit |
44 | * based SEV-SNP, are not enabled. |
45 | */ |
46 | static __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr) |
47 | { |
48 | switch (attr) { |
49 | case CC_ATTR_GUEST_MEM_ENCRYPT: |
50 | case CC_ATTR_MEM_ENCRYPT: |
51 | return true; |
52 | default: |
53 | return false; |
54 | } |
55 | } |
56 | |
57 | /* |
58 | * SME and SEV are very similar but they are not the same, so there are |
59 | * times that the kernel will need to distinguish between SME and SEV. The |
60 | * cc_platform_has() function is used for this. When a distinction isn't |
61 | * needed, the CC_ATTR_MEM_ENCRYPT attribute can be used. |
62 | * |
63 | * The trampoline code is a good example for this requirement. Before |
64 | * paging is activated, SME will access all memory as decrypted, but SEV |
65 | * will access all memory as encrypted. So, when APs are being brought |
66 | * up under SME the trampoline area cannot be encrypted, whereas under SEV |
67 | * the trampoline area must be encrypted. |
68 | */ |
69 | |
70 | static bool noinstr amd_cc_platform_has(enum cc_attr attr) |
71 | { |
72 | #ifdef CONFIG_AMD_MEM_ENCRYPT |
73 | |
74 | if (sev_status & MSR_AMD64_SNP_VTOM) |
75 | return amd_cc_platform_vtom(attr); |
76 | |
77 | switch (attr) { |
78 | case CC_ATTR_MEM_ENCRYPT: |
79 | return sme_me_mask; |
80 | |
81 | case CC_ATTR_HOST_MEM_ENCRYPT: |
82 | return sme_me_mask && !(sev_status & MSR_AMD64_SEV_ENABLED); |
83 | |
84 | case CC_ATTR_GUEST_MEM_ENCRYPT: |
85 | return sev_status & MSR_AMD64_SEV_ENABLED; |
86 | |
87 | case CC_ATTR_GUEST_STATE_ENCRYPT: |
88 | return sev_status & MSR_AMD64_SEV_ES_ENABLED; |
89 | |
90 | /* |
91 | * With SEV, the rep string I/O instructions need to be unrolled |
92 | * but SEV-ES supports them through the #VC handler. |
93 | */ |
94 | case CC_ATTR_GUEST_UNROLL_STRING_IO: |
95 | return (sev_status & MSR_AMD64_SEV_ENABLED) && |
96 | !(sev_status & MSR_AMD64_SEV_ES_ENABLED); |
97 | |
98 | case CC_ATTR_GUEST_SEV_SNP: |
99 | return sev_status & MSR_AMD64_SEV_SNP_ENABLED; |
100 | |
101 | case CC_ATTR_HOST_SEV_SNP: |
102 | return cc_flags.host_sev_snp; |
103 | |
104 | default: |
105 | return false; |
106 | } |
107 | #else |
108 | return false; |
109 | #endif |
110 | } |
111 | |
112 | bool noinstr cc_platform_has(enum cc_attr attr) |
113 | { |
114 | switch (cc_vendor) { |
115 | case CC_VENDOR_AMD: |
116 | return amd_cc_platform_has(attr); |
117 | case CC_VENDOR_INTEL: |
118 | return intel_cc_platform_has(attr); |
119 | default: |
120 | return false; |
121 | } |
122 | } |
123 | EXPORT_SYMBOL_GPL(cc_platform_has); |
124 | |
125 | u64 cc_mkenc(u64 val) |
126 | { |
127 | /* |
128 | * Both AMD and Intel use a bit in the page table to indicate |
129 | * encryption status of the page. |
130 | * |
131 | * - for AMD, bit *set* means the page is encrypted |
132 | * - for AMD with vTOM and for Intel, *clear* means encrypted |
133 | */ |
134 | switch (cc_vendor) { |
135 | case CC_VENDOR_AMD: |
136 | if (sev_status & MSR_AMD64_SNP_VTOM) |
137 | return val & ~cc_mask; |
138 | else |
139 | return val | cc_mask; |
140 | case CC_VENDOR_INTEL: |
141 | return val & ~cc_mask; |
142 | default: |
143 | return val; |
144 | } |
145 | } |
146 | |
147 | u64 cc_mkdec(u64 val) |
148 | { |
149 | /* See comment in cc_mkenc() */ |
150 | switch (cc_vendor) { |
151 | case CC_VENDOR_AMD: |
152 | if (sev_status & MSR_AMD64_SNP_VTOM) |
153 | return val | cc_mask; |
154 | else |
155 | return val & ~cc_mask; |
156 | case CC_VENDOR_INTEL: |
157 | return val | cc_mask; |
158 | default: |
159 | return val; |
160 | } |
161 | } |
162 | EXPORT_SYMBOL_GPL(cc_mkdec); |
163 | |
164 | static void amd_cc_platform_clear(enum cc_attr attr) |
165 | { |
166 | switch (attr) { |
167 | case CC_ATTR_HOST_SEV_SNP: |
168 | cc_flags.host_sev_snp = 0; |
169 | break; |
170 | default: |
171 | break; |
172 | } |
173 | } |
174 | |
175 | void cc_platform_clear(enum cc_attr attr) |
176 | { |
177 | switch (cc_vendor) { |
178 | case CC_VENDOR_AMD: |
179 | amd_cc_platform_clear(attr); |
180 | break; |
181 | default: |
182 | break; |
183 | } |
184 | } |
185 | |
186 | static void amd_cc_platform_set(enum cc_attr attr) |
187 | { |
188 | switch (attr) { |
189 | case CC_ATTR_HOST_SEV_SNP: |
190 | cc_flags.host_sev_snp = 1; |
191 | break; |
192 | default: |
193 | break; |
194 | } |
195 | } |
196 | |
197 | void cc_platform_set(enum cc_attr attr) |
198 | { |
199 | switch (cc_vendor) { |
200 | case CC_VENDOR_AMD: |
201 | amd_cc_platform_set(attr); |
202 | break; |
203 | default: |
204 | break; |
205 | } |
206 | } |
207 | |
208 | __init void cc_random_init(void) |
209 | { |
210 | /* |
211 | * The seed is 32 bytes (in units of longs), which is 256 bits, which |
212 | * is the security level that the RNG is targeting. |
213 | */ |
214 | unsigned long rng_seed[32 / sizeof(long)]; |
215 | size_t i, longs; |
216 | |
217 | if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) |
218 | return; |
219 | |
220 | /* |
221 | * Since the CoCo threat model includes the host, the only reliable |
222 | * source of entropy that can be neither observed nor manipulated is |
223 | * RDRAND. Usually, RDRAND failure is considered tolerable, but since |
224 | * CoCo guests have no other unobservable source of entropy, it's |
225 | * important to at least ensure the RNG gets some initial random seeds. |
226 | */ |
227 | for (i = 0; i < ARRAY_SIZE(rng_seed); i += longs) { |
228 | longs = arch_get_random_longs(v: &rng_seed[i], ARRAY_SIZE(rng_seed) - i); |
229 | |
230 | /* |
231 | * A zero return value means that the guest doesn't have RDRAND |
232 | * or the CPU is physically broken, and in both cases that |
233 | * means most crypto inside of the CoCo instance will be |
234 | * broken, defeating the purpose of CoCo in the first place. So |
235 | * just panic here because it's absolutely unsafe to continue |
236 | * executing. |
237 | */ |
238 | if (longs == 0) |
239 | panic(fmt: "RDRAND is defective." ); |
240 | } |
241 | add_device_randomness(buf: rng_seed, len: sizeof(rng_seed)); |
242 | memzero_explicit(s: rng_seed, count: sizeof(rng_seed)); |
243 | } |
244 | |