|
12 | 12 | #ifndef __BOOT_COMPRESSED |
13 | 13 | #define error(v) pr_err(v) |
14 | 14 | #define has_cpuflag(f) boot_cpu_has(f) |
| 15 | +#else |
| 16 | +#undef WARN |
| 17 | +#define WARN(condition, format...) (!!(condition)) |
15 | 18 | #endif |
16 | 19 |
|
17 | 20 | /* I/O parameters for CPUID-related helpers */ |
@@ -991,3 +994,103 @@ static void __init setup_cpuid_table(const struct cc_blob_sev_info *cc_info) |
991 | 994 | cpuid_ext_range_max = fn->eax; |
992 | 995 | } |
993 | 996 | } |
| 997 | + |
| 998 | +static void pvalidate_pages(struct snp_psc_desc *desc) |
| 999 | +{ |
| 1000 | + struct psc_entry *e; |
| 1001 | + unsigned long vaddr; |
| 1002 | + unsigned int size; |
| 1003 | + unsigned int i; |
| 1004 | + bool validate; |
| 1005 | + int rc; |
| 1006 | + |
| 1007 | + for (i = 0; i <= desc->hdr.end_entry; i++) { |
| 1008 | + e = &desc->entries[i]; |
| 1009 | + |
| 1010 | + vaddr = (unsigned long)pfn_to_kaddr(e->gfn); |
| 1011 | + size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K; |
| 1012 | + validate = e->operation == SNP_PAGE_STATE_PRIVATE; |
| 1013 | + |
| 1014 | + rc = pvalidate(vaddr, size, validate); |
| 1015 | + if (rc == PVALIDATE_FAIL_SIZEMISMATCH && size == RMP_PG_SIZE_2M) { |
| 1016 | + unsigned long vaddr_end = vaddr + PMD_SIZE; |
| 1017 | + |
| 1018 | + for (; vaddr < vaddr_end; vaddr += PAGE_SIZE) { |
| 1019 | + rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate); |
| 1020 | + if (rc) |
| 1021 | + break; |
| 1022 | + } |
| 1023 | + } |
| 1024 | + |
| 1025 | + if (rc) { |
| 1026 | + WARN(1, "Failed to validate address 0x%lx ret %d", vaddr, rc); |
| 1027 | + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE); |
| 1028 | + } |
| 1029 | + } |
| 1030 | +} |
| 1031 | + |
| 1032 | +static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc) |
| 1033 | +{ |
| 1034 | + int cur_entry, end_entry, ret = 0; |
| 1035 | + struct snp_psc_desc *data; |
| 1036 | + struct es_em_ctxt ctxt; |
| 1037 | + |
| 1038 | + vc_ghcb_invalidate(ghcb); |
| 1039 | + |
| 1040 | + /* Copy the input desc into GHCB shared buffer */ |
| 1041 | + data = (struct snp_psc_desc *)ghcb->shared_buffer; |
| 1042 | + memcpy(ghcb->shared_buffer, desc, min_t(int, GHCB_SHARED_BUF_SIZE, sizeof(*desc))); |
| 1043 | + |
| 1044 | + /* |
| 1045 | + * As per the GHCB specification, the hypervisor can resume the guest |
| 1046 | + * before processing all the entries. Check whether all the entries |
| 1047 | + * are processed. If not, then keep retrying. Note, the hypervisor |
| 1048 | + * will update the data memory directly to indicate the status, so |
| 1049 | + * reference the data->hdr everywhere. |
| 1050 | + * |
| 1051 | + * The strategy here is to wait for the hypervisor to change the page |
| 1052 | + * state in the RMP table before guest accesses the memory pages. If the |
| 1053 | + * page state change was not successful, then later memory access will |
| 1054 | + * result in a crash. |
| 1055 | + */ |
| 1056 | + cur_entry = data->hdr.cur_entry; |
| 1057 | + end_entry = data->hdr.end_entry; |
| 1058 | + |
| 1059 | + while (data->hdr.cur_entry <= data->hdr.end_entry) { |
| 1060 | + ghcb_set_sw_scratch(ghcb, (u64)__pa(data)); |
| 1061 | + |
| 1062 | + /* This will advance the shared buffer data points to. */ |
| 1063 | + ret = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_PSC, 0, 0); |
| 1064 | + |
| 1065 | + /* |
| 1066 | + * Page State Change VMGEXIT can pass error code through |
| 1067 | + * exit_info_2. |
| 1068 | + */ |
| 1069 | + if (WARN(ret || ghcb->save.sw_exit_info_2, |
| 1070 | + "SNP: PSC failed ret=%d exit_info_2=%llx\n", |
| 1071 | + ret, ghcb->save.sw_exit_info_2)) { |
| 1072 | + ret = 1; |
| 1073 | + goto out; |
| 1074 | + } |
| 1075 | + |
| 1076 | + /* Verify that reserved bit is not set */ |
| 1077 | + if (WARN(data->hdr.reserved, "Reserved bit is set in the PSC header\n")) { |
| 1078 | + ret = 1; |
| 1079 | + goto out; |
| 1080 | + } |
| 1081 | + |
| 1082 | + /* |
| 1083 | + * Sanity check that entry processing is not going backwards. |
| 1084 | + * This will happen only if hypervisor is tricking us. |
| 1085 | + */ |
| 1086 | + if (WARN(data->hdr.end_entry > end_entry || cur_entry > data->hdr.cur_entry, |
| 1087 | +"SNP: PSC processing going backward, end_entry %d (got %d) cur_entry %d (got %d)\n", |
| 1088 | + end_entry, data->hdr.end_entry, cur_entry, data->hdr.cur_entry)) { |
| 1089 | + ret = 1; |
| 1090 | + goto out; |
| 1091 | + } |
| 1092 | + } |
| 1093 | + |
| 1094 | +out: |
| 1095 | + return ret; |
| 1096 | +} |
0 commit comments