Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: ccp - Add SEV_INIT_EX support

Add new module parameter to allow users to use SEV_INIT_EX instead of
SEV_INIT. This helps users who lock their SPI bus to use the PSP for SEV
functionality. The 'init_ex_path' parameter defaults to NULL which means
the kernel will use SEV_INIT, if a path is specified SEV_INIT_EX will be
used with the data found at the path. On certain PSP commands this
file is written to as the PSP updates the NV memory region. Depending on
file system initialization this file open may fail during module init
but the CCP driver for SEV already has sufficient retries for platform
initialization. During normal operation of PSP system and SEV commands
if the PSP has not been initialized it is at run time. If the file at
'init_ex_path' does not exist the PSP will not be initialized. The user
must create the file prior to use with 32Kb of 0xFFs per spec.

Signed-off-by: David Rientjes <rientjes@google.com>
Co-developed-by: Peter Gonda <pgonda@google.com>
Signed-off-by: Peter Gonda <pgonda@google.com>
Reviewed-by: Marc Orr <marcorr@google.com>
Reported-by: kernel test robot <lkp@intel.com>
Acked-by: Brijesh Singh <brijesh.singh@amd.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Marc Orr <marcorr@google.com>
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: David Rientjes <rientjes@google.com>
Cc: John Allen <john.allen@amd.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: linux-crypto@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

David Rientjes and committed by
Herbert Xu
3d725965 b64fa5fc

+209 -19
+6
Documentation/virt/kvm/amd-memory-encryption.rst
··· 85 85 The KVM_SEV_INIT command is used by the hypervisor to initialize the SEV platform 86 86 context. In a typical workflow, this command should be the first command issued. 87 87 88 + The firmware can be initialized either by using its own non-volatile storage or 89 + the OS can manage the NV storage for the firmware using the module parameter 90 + ``init_ex_path``. The file specified by ``init_ex_path`` must exist. To create 91 + a new NV storage file allocate the file with 32KB bytes of 0xFF as required by 92 + the SEV spec. 93 + 88 94 Returns: 0 on success, -negative on error 89 95 90 96 2. KVM_SEV_LAUNCH_START
+182 -19
drivers/crypto/ccp/sev-dev.c
··· 22 22 #include <linux/firmware.h> 23 23 #include <linux/gfp.h> 24 24 #include <linux/cpufeature.h> 25 + #include <linux/fs.h> 25 26 26 27 #include <asm/smp.h> 27 28 ··· 44 43 module_param(psp_probe_timeout, int, 0644); 45 44 MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); 46 45 46 + static char *init_ex_path; 47 + module_param(init_ex_path, charp, 0444); 48 + MODULE_PARM_DESC(init_ex_path, " Path for INIT_EX data; if set try INIT_EX"); 49 + 47 50 static bool psp_init_on_probe = true; 48 51 module_param(psp_init_on_probe, bool, 0444); 49 52 MODULE_PARM_DESC(psp_init_on_probe, " if true, the PSP will be initialized on module init. Else the PSP will be initialized on the first command requiring it"); ··· 66 61 */ 67 62 #define SEV_ES_TMR_SIZE (1024 * 1024) 68 63 static void *sev_es_tmr; 64 + 65 + /* INIT_EX NV Storage: 66 + * The NV Storage is a 32Kb area and must be 4Kb page aligned. Use the page 67 + * allocator to allocate the memory, which will return aligned memory for the 68 + * specified allocation order. 69 + */ 70 + #define NV_LENGTH (32 * 1024) 71 + static void *sev_init_ex_buffer; 69 72 70 73 static inline bool sev_version_greater_or_equal(u8 maj, u8 min) 71 74 { ··· 124 111 { 125 112 switch (cmd) { 126 113 case SEV_CMD_INIT: return sizeof(struct sev_data_init); 114 + case SEV_CMD_INIT_EX: return sizeof(struct sev_data_init_ex); 127 115 case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status); 128 116 case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr); 129 117 case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import); ··· 168 154 return NULL; 169 155 170 156 return page_address(page); 157 + } 158 + 159 + static int sev_read_init_ex_file(void) 160 + { 161 + struct sev_device *sev = psp_master->sev_data; 162 + struct file *fp; 163 + ssize_t nread; 164 + 165 + lockdep_assert_held(&sev_cmd_mutex); 166 + 167 + if (!sev_init_ex_buffer) 168 + return -EOPNOTSUPP; 169 + 170 + fp = filp_open(init_ex_path, O_RDONLY, 0); 171 + if (IS_ERR(fp)) { 172 + int ret = PTR_ERR(fp); 173 + 174 + dev_err(sev->dev, 175 + "SEV: could not open %s for read, error %d\n", 176 + init_ex_path, ret); 177 + return ret; 178 + } 179 + 180 + nread = kernel_read(fp, sev_init_ex_buffer, NV_LENGTH, NULL); 181 + if (nread != NV_LENGTH) { 182 + dev_err(sev->dev, 183 + "SEV: failed to read %u bytes to non volatile memory area, ret %ld\n", 184 + NV_LENGTH, nread); 185 + return -EIO; 186 + } 187 + 188 + dev_dbg(sev->dev, "SEV: read %ld bytes from NV file\n", nread); 189 + filp_close(fp, NULL); 190 + 191 + return 0; 192 + } 193 + 194 + static void sev_write_init_ex_file(void) 195 + { 196 + struct sev_device *sev = psp_master->sev_data; 197 + struct file *fp; 198 + loff_t offset = 0; 199 + ssize_t nwrite; 200 + 201 + lockdep_assert_held(&sev_cmd_mutex); 202 + 203 + if (!sev_init_ex_buffer) 204 + return; 205 + 206 + fp = filp_open(init_ex_path, O_CREAT | O_WRONLY, 0600); 207 + if (IS_ERR(fp)) { 208 + dev_err(sev->dev, 209 + "SEV: could not open file for write, error %ld\n", 210 + PTR_ERR(fp)); 211 + return; 212 + } 213 + 214 + nwrite = kernel_write(fp, sev_init_ex_buffer, NV_LENGTH, &offset); 215 + vfs_fsync(fp, 0); 216 + filp_close(fp, NULL); 217 + 218 + if (nwrite != NV_LENGTH) { 219 + dev_err(sev->dev, 220 + "SEV: failed to write %u bytes to non volatile memory area, ret %ld\n", 221 + NV_LENGTH, nwrite); 222 + return; 223 + } 224 + 225 + dev_dbg(sev->dev, "SEV: write successful to NV file\n"); 226 + } 227 + 228 + static void sev_write_init_ex_file_if_required(int cmd_id) 229 + { 230 + lockdep_assert_held(&sev_cmd_mutex); 231 + 232 + if (!sev_init_ex_buffer) 233 + return; 234 + 235 + /* 236 + * Only a few platform commands modify the SPI/NV area, but none of the 237 + * non-platform commands do. Only INIT(_EX), PLATFORM_RESET, PEK_GEN, 238 + * PEK_CERT_IMPORT, and PDH_GEN do. 239 + */ 240 + switch (cmd_id) { 241 + case SEV_CMD_FACTORY_RESET: 242 + case SEV_CMD_INIT_EX: 243 + case SEV_CMD_PDH_GEN: 244 + case SEV_CMD_PEK_CERT_IMPORT: 245 + case SEV_CMD_PEK_GEN: 246 + break; 247 + default: 248 + return; 249 + }; 250 + 251 + sev_write_init_ex_file(); 171 252 } 172 253 173 254 static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret) ··· 334 225 dev_dbg(sev->dev, "sev command %#x failed (%#010x)\n", 335 226 cmd, reg & PSP_CMDRESP_ERR_MASK); 336 227 ret = -EIO; 228 + } else { 229 + sev_write_init_ex_file_if_required(cmd); 337 230 } 338 231 339 232 print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, ··· 362 251 return rc; 363 252 } 364 253 254 + static int __sev_init_locked(int *error) 255 + { 256 + struct sev_data_init data; 257 + 258 + memset(&data, 0, sizeof(data)); 259 + if (sev_es_tmr) { 260 + /* 261 + * Do not include the encryption mask on the physical 262 + * address of the TMR (firmware should clear it anyway). 263 + */ 264 + data.tmr_address = __pa(sev_es_tmr); 265 + 266 + data.flags |= SEV_INIT_FLAGS_SEV_ES; 267 + data.tmr_len = SEV_ES_TMR_SIZE; 268 + } 269 + 270 + return __sev_do_cmd_locked(SEV_CMD_INIT, &data, error); 271 + } 272 + 273 + static int __sev_init_ex_locked(int *error) 274 + { 275 + struct sev_data_init_ex data; 276 + int ret; 277 + 278 + memset(&data, 0, sizeof(data)); 279 + data.length = sizeof(data); 280 + data.nv_address = __psp_pa(sev_init_ex_buffer); 281 + data.nv_len = NV_LENGTH; 282 + 283 + ret = sev_read_init_ex_file(); 284 + if (ret) 285 + return ret; 286 + 287 + if (sev_es_tmr) { 288 + /* 289 + * Do not include the encryption mask on the physical 290 + * address of the TMR (firmware should clear it anyway). 291 + */ 292 + data.tmr_address = __pa(sev_es_tmr); 293 + 294 + data.flags |= SEV_INIT_FLAGS_SEV_ES; 295 + data.tmr_len = SEV_ES_TMR_SIZE; 296 + } 297 + 298 + return __sev_do_cmd_locked(SEV_CMD_INIT_EX, &data, error); 299 + } 300 + 365 301 static int __sev_platform_init_locked(int *error) 366 302 { 367 303 struct psp_device *psp = psp_master; 368 - struct sev_data_init data; 369 304 struct sev_device *sev; 370 - int psp_ret, rc = 0; 305 + int rc, psp_ret; 306 + int (*init_function)(int *error); 371 307 372 308 if (!psp || !psp->sev_data) 373 309 return -ENODEV; ··· 424 266 if (sev->state == SEV_STATE_INIT) 425 267 return 0; 426 268 427 - memset(&data, 0, sizeof(data)); 428 - if (sev_es_tmr) { 429 - u64 tmr_pa; 430 - 431 - /* 432 - * Do not include the encryption mask on the physical 433 - * address of the TMR (firmware should clear it anyway). 434 - */ 435 - tmr_pa = __pa(sev_es_tmr); 436 - 437 - data.flags |= SEV_INIT_FLAGS_SEV_ES; 438 - data.tmr_address = tmr_pa; 439 - data.tmr_len = SEV_ES_TMR_SIZE; 440 - } 441 - 442 - rc = __sev_do_cmd_locked(SEV_CMD_INIT, &data, &psp_ret); 269 + init_function = sev_init_ex_buffer ? __sev_init_ex_locked : 270 + __sev_init_locked; 271 + rc = init_function(&psp_ret); 443 272 if (rc && psp_ret == SEV_RET_SECURE_DATA_INVALID) { 444 273 /* 445 274 * Initialization command returned an integrity check failure ··· 436 291 * with a reset state. 437 292 */ 438 293 dev_dbg(sev->dev, "SEV: retrying INIT command"); 439 - rc = __sev_do_cmd_locked(SEV_CMD_INIT, &data, &psp_ret); 294 + rc = init_function(&psp_ret); 440 295 } 441 296 if (error) 442 297 *error = psp_ret; ··· 1211 1066 get_order(SEV_ES_TMR_SIZE)); 1212 1067 sev_es_tmr = NULL; 1213 1068 } 1069 + 1070 + if (sev_init_ex_buffer) { 1071 + free_pages((unsigned long)sev_init_ex_buffer, 1072 + get_order(NV_LENGTH)); 1073 + sev_init_ex_buffer = NULL; 1074 + } 1214 1075 } 1215 1076 1216 1077 void sev_dev_destroy(struct psp_device *psp) ··· 1260 1109 if (sev_version_greater_or_equal(0, 15) && 1261 1110 sev_update_firmware(sev->dev) == 0) 1262 1111 sev_get_api_version(); 1112 + 1113 + /* If an init_ex_path is provided rely on INIT_EX for PSP initialization 1114 + * instead of INIT. 1115 + */ 1116 + if (init_ex_path) { 1117 + sev_init_ex_buffer = sev_fw_alloc(NV_LENGTH); 1118 + if (!sev_init_ex_buffer) { 1119 + dev_err(sev->dev, 1120 + "SEV: INIT_EX NV memory allocation failed\n"); 1121 + goto err; 1122 + } 1123 + } 1263 1124 1264 1125 /* Obtain the TMR memory area for SEV-ES use */ 1265 1126 sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE);
+21
include/linux/psp-sev.h
··· 52 52 SEV_CMD_DF_FLUSH = 0x00A, 53 53 SEV_CMD_DOWNLOAD_FIRMWARE = 0x00B, 54 54 SEV_CMD_GET_ID = 0x00C, 55 + SEV_CMD_INIT_EX = 0x00D, 55 56 56 57 /* Guest commands */ 57 58 SEV_CMD_DECOMMISSION = 0x020, ··· 101 100 u32 reserved; /* In */ 102 101 u64 tmr_address; /* In */ 103 102 u32 tmr_len; /* In */ 103 + } __packed; 104 + 105 + /** 106 + * struct sev_data_init_ex - INIT_EX command parameters 107 + * 108 + * @length: len of the command buffer read by the PSP 109 + * @flags: processing flags 110 + * @tmr_address: system physical address used for SEV-ES 111 + * @tmr_len: len of tmr_address 112 + * @nv_address: system physical address used for PSP NV storage 113 + * @nv_len: len of nv_address 114 + */ 115 + struct sev_data_init_ex { 116 + u32 length; /* In */ 117 + u32 flags; /* In */ 118 + u64 tmr_address; /* In */ 119 + u32 tmr_len; /* In */ 120 + u32 reserved; /* In */ 121 + u64 nv_address; /* In/Out */ 122 + u32 nv_len; /* In */ 104 123 } __packed; 105 124 106 125 #define SEV_INIT_FLAGS_SEV_ES 0x01