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