|
23 | 23 | #define sev_printk_rtl(fmt, ...) |
24 | 24 | #endif |
25 | 25 |
|
| 26 | +/* |
| 27 | + * SVSM related information: |
| 28 | + * When running under an SVSM, the VMPL that Linux is executing at must be |
| 29 | + * non-zero. The VMPL is therefore used to indicate the presence of an SVSM. |
| 30 | + * |
| 31 | + * During boot, the page tables are set up as identity mapped and later |
| 32 | + * changed to use kernel virtual addresses. Maintain separate virtual and |
| 33 | + * physical addresses for the CAA to allow SVSM functions to be used during |
| 34 | + * early boot, both with identity mapped virtual addresses and proper kernel |
| 35 | + * virtual addresses. |
| 36 | + */ |
| 37 | +static u8 snp_vmpl __ro_after_init; |
| 38 | +static struct svsm_ca *boot_svsm_caa __ro_after_init; |
| 39 | +static u64 boot_svsm_caa_pa __ro_after_init; |
| 40 | + |
26 | 41 | /* I/O parameters for CPUID-related helpers */ |
27 | 42 | struct cpuid_leaf { |
28 | 43 | u32 fn; |
@@ -1269,3 +1284,64 @@ static enum es_result vc_check_opcode_bytes(struct es_em_ctxt *ctxt, |
1269 | 1284 |
|
1270 | 1285 | return ES_UNSUPPORTED; |
1271 | 1286 | } |
| 1287 | + |
| 1288 | +/* |
| 1289 | + * Maintain the GPA of the SVSM Calling Area (CA) in order to utilize the SVSM |
| 1290 | + * services needed when not running in VMPL0. |
| 1291 | + */ |
| 1292 | +static void __head svsm_setup_ca(const struct cc_blob_sev_info *cc_info) |
| 1293 | +{ |
| 1294 | + struct snp_secrets_page *secrets_page; |
| 1295 | + u64 caa; |
| 1296 | + |
| 1297 | + BUILD_BUG_ON(sizeof(*secrets_page) != PAGE_SIZE); |
| 1298 | + |
| 1299 | + /* |
| 1300 | + * Check if running at VMPL0. |
| 1301 | + * |
| 1302 | + * Use RMPADJUST (see the rmpadjust() function for a description of what |
| 1303 | + * the instruction does) to update the VMPL1 permissions of a page. If |
| 1304 | + * the guest is running at VMPL0, this will succeed and implies there is |
| 1305 | + * no SVSM. If the guest is running at any other VMPL, this will fail. |
| 1306 | + * Linux SNP guests only ever run at a single VMPL level so permission mask |
| 1307 | + * changes of a lesser-privileged VMPL are a don't-care. |
| 1308 | + * |
| 1309 | + * Use a rip-relative reference to obtain the proper address, since this |
| 1310 | + * routine is running identity mapped when called, both by the decompressor |
| 1311 | + * code and the early kernel code. |
| 1312 | + */ |
| 1313 | + if (!rmpadjust((unsigned long)&RIP_REL_REF(boot_ghcb_page), RMP_PG_SIZE_4K, 1)) |
| 1314 | + return; |
| 1315 | + |
| 1316 | + /* |
| 1317 | + * Not running at VMPL0, ensure everything has been properly supplied |
| 1318 | + * for running under an SVSM. |
| 1319 | + */ |
| 1320 | + if (!cc_info || !cc_info->secrets_phys || cc_info->secrets_len != PAGE_SIZE) |
| 1321 | + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECRETS_PAGE); |
| 1322 | + |
| 1323 | + secrets_page = (struct snp_secrets_page *)cc_info->secrets_phys; |
| 1324 | + if (!secrets_page->svsm_size) |
| 1325 | + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NO_SVSM); |
| 1326 | + |
| 1327 | + if (!secrets_page->svsm_guest_vmpl) |
| 1328 | + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_VMPL0); |
| 1329 | + |
| 1330 | + RIP_REL_REF(snp_vmpl) = secrets_page->svsm_guest_vmpl; |
| 1331 | + |
| 1332 | + caa = secrets_page->svsm_caa; |
| 1333 | + |
| 1334 | + /* |
| 1335 | + * An open-coded PAGE_ALIGNED() in order to avoid including |
| 1336 | + * kernel-proper headers into the decompressor. |
| 1337 | + */ |
| 1338 | + if (caa & (PAGE_SIZE - 1)) |
| 1339 | + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_CAA); |
| 1340 | + |
| 1341 | + /* |
| 1342 | + * The CA is identity mapped when this routine is called, both by the |
| 1343 | + * decompressor code and the early kernel code. |
| 1344 | + */ |
| 1345 | + RIP_REL_REF(boot_svsm_caa) = (struct svsm_ca *)caa; |
| 1346 | + RIP_REL_REF(boot_svsm_caa_pa) = caa; |
| 1347 | +} |
0 commit comments