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

Merge branch kvm-arm64/pkvm-selftest-6.16 into kvm-arm64/pkvm-np-thp-6.16

* kvm-arm64/pkvm-selftest-6.16:
: .
: pKVM selftests covering the memory ownership transitions by
: Quentin Perret. From the initial cover letter:
:
: "We have recently found a bug [1] in the pKVM memory ownership
: transitions by code inspection, but it could have been caught with a
: test.
:
: Introduce a boot-time selftest exercising all the known pKVM memory
: transitions and importantly checks the rejection of illegal transitions.
:
: The new test is hidden behind a new Kconfig option separate from
: CONFIG_EL2_NVHE_DEBUG on purpose as that has side effects on the
: transition checks ([1] doesn't reproduce with EL2 debug enabled).
:
: [1] https://lore.kernel.org/kvmarm/20241128154406.602875-1-qperret@google.com/"
: .
KVM: arm64: Extend pKVM selftest for np-guests
KVM: arm64: Selftest for pKVM transitions
KVM: arm64: Don't WARN from __pkvm_host_share_guest()
KVM: arm64: Add .hyp.data section

Signed-off-by: Marc Zyngier <maz@kernel.org>

+250 -4
+6
arch/arm64/include/asm/kvm_pkvm.h
··· 135 135 return res; 136 136 } 137 137 138 + #ifdef CONFIG_NVHE_EL2_DEBUG 139 + static inline unsigned long pkvm_selftest_pages(void) { return 32; } 140 + #else 141 + static inline unsigned long pkvm_selftest_pages(void) { return 0; } 142 + #endif 143 + 138 144 #define KVM_FFA_MBOX_NR_PAGES 1 139 145 140 146 static inline unsigned long hyp_ffa_proxy_pages(void)
+1
arch/arm64/include/asm/sections.h
··· 11 11 extern char __hibernate_exit_text_start[], __hibernate_exit_text_end[]; 12 12 extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[]; 13 13 extern char __hyp_text_start[], __hyp_text_end[]; 14 + extern char __hyp_data_start[], __hyp_data_end[]; 14 15 extern char __hyp_rodata_start[], __hyp_rodata_end[]; 15 16 extern char __hyp_reloc_begin[], __hyp_reloc_end[]; 16 17 extern char __hyp_bss_start[], __hyp_bss_end[];
+2
arch/arm64/kernel/image-vars.h
··· 127 127 KVM_NVHE_ALIAS(__hyp_text_end); 128 128 KVM_NVHE_ALIAS(__hyp_bss_start); 129 129 KVM_NVHE_ALIAS(__hyp_bss_end); 130 + KVM_NVHE_ALIAS(__hyp_data_start); 131 + KVM_NVHE_ALIAS(__hyp_data_end); 130 132 KVM_NVHE_ALIAS(__hyp_rodata_start); 131 133 KVM_NVHE_ALIAS(__hyp_rodata_end); 132 134
+15 -3
arch/arm64/kernel/vmlinux.lds.S
··· 13 13 *(__kvm_ex_table) \ 14 14 __stop___kvm_ex_table = .; 15 15 16 - #define HYPERVISOR_DATA_SECTIONS \ 16 + #define HYPERVISOR_RODATA_SECTIONS \ 17 17 HYP_SECTION_NAME(.rodata) : { \ 18 18 . = ALIGN(PAGE_SIZE); \ 19 19 __hyp_rodata_start = .; \ ··· 21 21 *(HYP_SECTION_NAME(.rodata)) \ 22 22 . = ALIGN(PAGE_SIZE); \ 23 23 __hyp_rodata_end = .; \ 24 + } 25 + 26 + #define HYPERVISOR_DATA_SECTION \ 27 + HYP_SECTION_NAME(.data) : { \ 28 + . = ALIGN(PAGE_SIZE); \ 29 + __hyp_data_start = .; \ 30 + *(HYP_SECTION_NAME(.data)) \ 31 + . = ALIGN(PAGE_SIZE); \ 32 + __hyp_data_end = .; \ 24 33 } 25 34 26 35 #define HYPERVISOR_PERCPU_SECTION \ ··· 60 51 #define SBSS_ALIGN PAGE_SIZE 61 52 #else /* CONFIG_KVM */ 62 53 #define HYPERVISOR_EXTABLE 63 - #define HYPERVISOR_DATA_SECTIONS 54 + #define HYPERVISOR_RODATA_SECTIONS 55 + #define HYPERVISOR_DATA_SECTION 64 56 #define HYPERVISOR_PERCPU_SECTION 65 57 #define HYPERVISOR_RELOC_SECTION 66 58 #define SBSS_ALIGN 0 ··· 200 190 /* everything from this point to __init_begin will be marked RO NX */ 201 191 RO_DATA(PAGE_SIZE) 202 192 203 - HYPERVISOR_DATA_SECTIONS 193 + HYPERVISOR_RODATA_SECTIONS 204 194 205 195 .got : { *(.got) } 206 196 /* ··· 304 294 _data = .; 305 295 _sdata = .; 306 296 RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN) 297 + 298 + HYPERVISOR_DATA_SECTION 307 299 308 300 /* 309 301 * Data written with the MMU off but read with the MMU on requires
+7
arch/arm64/kvm/arm.c
··· 2604 2604 goto out_err; 2605 2605 } 2606 2606 2607 + err = create_hyp_mappings(kvm_ksym_ref(__hyp_data_start), 2608 + kvm_ksym_ref(__hyp_data_end), PAGE_HYP); 2609 + if (err) { 2610 + kvm_err("Cannot map .hyp.data section\n"); 2611 + goto out_err; 2612 + } 2613 + 2607 2614 err = create_hyp_mappings(kvm_ksym_ref(__hyp_rodata_start), 2608 2615 kvm_ksym_ref(__hyp_rodata_end), PAGE_HYP_RO); 2609 2616 if (err) {
+6
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
··· 67 67 else 68 68 write_sysreg(0, vttbr_el2); 69 69 } 70 + 71 + #ifdef CONFIG_NVHE_EL2_DEBUG 72 + void pkvm_ownership_selftest(void *base); 73 + #else 74 + static inline void pkvm_ownership_selftest(void *base) { } 75 + #endif 70 76 #endif /* __KVM_NVHE_MEM_PROTECT__ */
+2
arch/arm64/kvm/hyp/nvhe/hyp.lds.S
··· 25 25 BEGIN_HYP_SECTION(.data..percpu) 26 26 PERCPU_INPUT(L1_CACHE_BYTES) 27 27 END_HYP_SECTION 28 + 28 29 HYP_SECTION(.bss) 30 + HYP_SECTION(.data) 29 31 }
+197 -1
arch/arm64/kvm/hyp/nvhe/mem_protect.c
··· 921 921 if (page->host_share_guest_count) 922 922 break; 923 923 /* Only host to np-guest multi-sharing is tolerated */ 924 - WARN_ON(1); 925 924 fallthrough; 926 925 default: 927 926 ret = -EPERM; ··· 1092 1093 1093 1094 return 0; 1094 1095 } 1096 + 1097 + #ifdef CONFIG_NVHE_EL2_DEBUG 1098 + struct pkvm_expected_state { 1099 + enum pkvm_page_state host; 1100 + enum pkvm_page_state hyp; 1101 + enum pkvm_page_state guest[2]; /* [ gfn, gfn + 1 ] */ 1102 + }; 1103 + 1104 + static struct pkvm_expected_state selftest_state; 1105 + static struct hyp_page *selftest_page; 1106 + 1107 + static struct pkvm_hyp_vm selftest_vm = { 1108 + .kvm = { 1109 + .arch = { 1110 + .mmu = { 1111 + .arch = &selftest_vm.kvm.arch, 1112 + .pgt = &selftest_vm.pgt, 1113 + }, 1114 + }, 1115 + }, 1116 + }; 1117 + 1118 + static struct pkvm_hyp_vcpu selftest_vcpu = { 1119 + .vcpu = { 1120 + .arch = { 1121 + .hw_mmu = &selftest_vm.kvm.arch.mmu, 1122 + }, 1123 + .kvm = &selftest_vm.kvm, 1124 + }, 1125 + }; 1126 + 1127 + static void init_selftest_vm(void *virt) 1128 + { 1129 + struct hyp_page *p = hyp_virt_to_page(virt); 1130 + int i; 1131 + 1132 + selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr; 1133 + WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt)); 1134 + 1135 + for (i = 0; i < pkvm_selftest_pages(); i++) { 1136 + if (p[i].refcount) 1137 + continue; 1138 + p[i].refcount = 1; 1139 + hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i])); 1140 + } 1141 + } 1142 + 1143 + static u64 selftest_ipa(void) 1144 + { 1145 + return BIT(selftest_vm.pgt.ia_bits - 1); 1146 + } 1147 + 1148 + static void assert_page_state(void) 1149 + { 1150 + void *virt = hyp_page_to_virt(selftest_page); 1151 + u64 size = PAGE_SIZE << selftest_page->order; 1152 + struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu; 1153 + u64 phys = hyp_virt_to_phys(virt); 1154 + u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE }; 1155 + 1156 + host_lock_component(); 1157 + WARN_ON(__host_check_page_state_range(phys, size, selftest_state.host)); 1158 + host_unlock_component(); 1159 + 1160 + hyp_lock_component(); 1161 + WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp)); 1162 + hyp_unlock_component(); 1163 + 1164 + guest_lock_component(&selftest_vm); 1165 + WARN_ON(__guest_check_page_state_range(vcpu, ipa[0], size, selftest_state.guest[0])); 1166 + WARN_ON(__guest_check_page_state_range(vcpu, ipa[1], size, selftest_state.guest[1])); 1167 + guest_unlock_component(&selftest_vm); 1168 + } 1169 + 1170 + #define assert_transition_res(res, fn, ...) \ 1171 + do { \ 1172 + WARN_ON(fn(__VA_ARGS__) != res); \ 1173 + assert_page_state(); \ 1174 + } while (0) 1175 + 1176 + void pkvm_ownership_selftest(void *base) 1177 + { 1178 + enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX; 1179 + void *virt = hyp_alloc_pages(&host_s2_pool, 0); 1180 + struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu; 1181 + struct pkvm_hyp_vm *vm = &selftest_vm; 1182 + u64 phys, size, pfn, gfn; 1183 + 1184 + WARN_ON(!virt); 1185 + selftest_page = hyp_virt_to_page(virt); 1186 + selftest_page->refcount = 0; 1187 + init_selftest_vm(base); 1188 + 1189 + size = PAGE_SIZE << selftest_page->order; 1190 + phys = hyp_virt_to_phys(virt); 1191 + pfn = hyp_phys_to_pfn(phys); 1192 + gfn = hyp_phys_to_pfn(selftest_ipa()); 1193 + 1194 + selftest_state.host = PKVM_NOPAGE; 1195 + selftest_state.hyp = PKVM_PAGE_OWNED; 1196 + selftest_state.guest[0] = selftest_state.guest[1] = PKVM_NOPAGE; 1197 + assert_page_state(); 1198 + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); 1199 + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); 1200 + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); 1201 + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); 1202 + assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1); 1203 + assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); 1204 + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1205 + assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm); 1206 + 1207 + selftest_state.host = PKVM_PAGE_OWNED; 1208 + selftest_state.hyp = PKVM_NOPAGE; 1209 + assert_transition_res(0, __pkvm_hyp_donate_host, pfn, 1); 1210 + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); 1211 + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); 1212 + assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1); 1213 + assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm); 1214 + assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); 1215 + 1216 + selftest_state.host = PKVM_PAGE_SHARED_OWNED; 1217 + selftest_state.hyp = PKVM_PAGE_SHARED_BORROWED; 1218 + assert_transition_res(0, __pkvm_host_share_hyp, pfn); 1219 + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); 1220 + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); 1221 + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); 1222 + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); 1223 + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1224 + assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm); 1225 + 1226 + assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size); 1227 + assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size); 1228 + hyp_unpin_shared_mem(virt, virt + size); 1229 + WARN_ON(hyp_page_count(virt) != 1); 1230 + assert_transition_res(-EBUSY, __pkvm_host_unshare_hyp, pfn); 1231 + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); 1232 + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); 1233 + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); 1234 + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); 1235 + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1236 + assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm); 1237 + 1238 + hyp_unpin_shared_mem(virt, virt + size); 1239 + assert_page_state(); 1240 + WARN_ON(hyp_page_count(virt)); 1241 + 1242 + selftest_state.host = PKVM_PAGE_OWNED; 1243 + selftest_state.hyp = PKVM_NOPAGE; 1244 + assert_transition_res(0, __pkvm_host_unshare_hyp, pfn); 1245 + 1246 + selftest_state.host = PKVM_PAGE_SHARED_OWNED; 1247 + selftest_state.hyp = PKVM_NOPAGE; 1248 + assert_transition_res(0, __pkvm_host_share_ffa, pfn, 1); 1249 + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); 1250 + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); 1251 + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); 1252 + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); 1253 + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); 1254 + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1255 + assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm); 1256 + assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); 1257 + 1258 + selftest_state.host = PKVM_PAGE_OWNED; 1259 + selftest_state.hyp = PKVM_NOPAGE; 1260 + assert_transition_res(0, __pkvm_host_unshare_ffa, pfn, 1); 1261 + assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1); 1262 + 1263 + selftest_state.host = PKVM_PAGE_SHARED_OWNED; 1264 + selftest_state.guest[0] = PKVM_PAGE_SHARED_BORROWED; 1265 + assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1266 + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot); 1267 + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); 1268 + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); 1269 + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); 1270 + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); 1271 + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); 1272 + assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); 1273 + 1274 + selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED; 1275 + assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn + 1, vcpu, prot); 1276 + WARN_ON(hyp_virt_to_page(virt)->host_share_guest_count != 2); 1277 + 1278 + selftest_state.guest[0] = PKVM_NOPAGE; 1279 + assert_transition_res(0, __pkvm_host_unshare_guest, gfn, vm); 1280 + 1281 + selftest_state.guest[1] = PKVM_NOPAGE; 1282 + selftest_state.host = PKVM_PAGE_OWNED; 1283 + assert_transition_res(0, __pkvm_host_unshare_guest, gfn + 1, vm); 1284 + 1285 + selftest_state.host = PKVM_NOPAGE; 1286 + selftest_state.hyp = PKVM_PAGE_OWNED; 1287 + assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1); 1288 + 1289 + selftest_page->refcount = 1; 1290 + hyp_put_page(&host_s2_pool, virt); 1291 + } 1292 + #endif
+12
arch/arm64/kvm/hyp/nvhe/setup.c
··· 28 28 static void *vm_table_base; 29 29 static void *hyp_pgt_base; 30 30 static void *host_s2_pgt_base; 31 + static void *selftest_base; 31 32 static void *ffa_proxy_pages; 32 33 static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops; 33 34 static struct hyp_pool hpool; ··· 38 37 unsigned long nr_pages; 39 38 40 39 hyp_early_alloc_init(virt, size); 40 + 41 + nr_pages = pkvm_selftest_pages(); 42 + selftest_base = hyp_early_alloc_contig(nr_pages); 43 + if (nr_pages && !selftest_base) 44 + return -ENOMEM; 41 45 42 46 nr_pages = hyp_vmemmap_pages(sizeof(struct hyp_page)); 43 47 vmemmap_base = hyp_early_alloc_contig(nr_pages); ··· 122 116 return ret; 123 117 124 118 ret = pkvm_create_mappings(__hyp_text_start, __hyp_text_end, PAGE_HYP_EXEC); 119 + if (ret) 120 + return ret; 121 + 122 + ret = pkvm_create_mappings(__hyp_data_start, __hyp_data_end, PAGE_HYP); 125 123 if (ret) 126 124 return ret; 127 125 ··· 318 308 goto out; 319 309 320 310 pkvm_hyp_vm_table_init(vm_table_base); 311 + 312 + pkvm_ownership_selftest(selftest_base); 321 313 out: 322 314 /* 323 315 * We tail-called to here from handle___pkvm_init() and will not return,
+2
arch/arm64/kvm/pkvm.c
··· 79 79 hyp_mem_pages += host_s2_pgtable_pages(); 80 80 hyp_mem_pages += hyp_vm_table_pages(); 81 81 hyp_mem_pages += hyp_vmemmap_pages(STRUCT_HYP_PAGE_SIZE); 82 + hyp_mem_pages += pkvm_selftest_pages(); 82 83 hyp_mem_pages += hyp_ffa_proxy_pages(); 83 84 84 85 /* ··· 263 262 * at, which would end badly once inaccessible. 264 263 */ 265 264 kmemleak_free_part(__hyp_bss_start, __hyp_bss_end - __hyp_bss_start); 265 + kmemleak_free_part(__hyp_data_start, __hyp_data_end - __hyp_data_start); 266 266 kmemleak_free_part(__hyp_rodata_start, __hyp_rodata_end - __hyp_rodata_start); 267 267 kmemleak_free_part_phys(hyp_mem_base, hyp_mem_size); 268 268