1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hyper-V HvFlushVirtualAddress{List,Space}{,Ex} tests 4 * 5 * Copyright (C) 2022, Red Hat, Inc. 6 * 7 */ 8 #include <asm/barrier.h> 9 #include <pthread.h> 10 #include <inttypes.h> 11 12 #include "kvm_util.h" 13 #include "processor.h" 14 #include "hyperv.h" 15 #include "test_util.h" 16 #include "vmx.h" 17 18 #define WORKER_VCPU_ID_1 2 19 #define WORKER_VCPU_ID_2 65 20 21 #define NTRY 100 22 #define NTEST_PAGES 2 23 24 struct hv_vpset { 25 u64 format; 26 u64 valid_bank_mask; 27 u64 bank_contents[]; 28 }; 29 30 enum HV_GENERIC_SET_FORMAT { 31 HV_GENERIC_SET_SPARSE_4K, 32 HV_GENERIC_SET_ALL, 33 }; 34 35 #define HV_FLUSH_ALL_PROCESSORS BIT(0) 36 #define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1) 37 #define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2) 38 #define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3) 39 40 /* HvFlushVirtualAddressSpace, HvFlushVirtualAddressList hypercalls */ 41 struct hv_tlb_flush { 42 u64 address_space; 43 u64 flags; 44 u64 processor_mask; 45 u64 gva_list[]; 46 } __packed; 47 48 /* HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressListEx hypercalls */ 49 struct hv_tlb_flush_ex { 50 u64 address_space; 51 u64 flags; 52 struct hv_vpset hv_vp_set; 53 u64 gva_list[]; 54 } __packed; 55 56 /* 57 * Pass the following info to 'workers' and 'sender' 58 * - Hypercall page's GVA 59 * - Hypercall page's GPA 60 * - Test pages GVA 61 * - GVAs of the test pages' PTEs 62 */ 63 struct test_data { 64 gva_t hcall_gva; 65 gpa_t hcall_gpa; 66 gva_t test_pages; 67 gva_t test_pages_pte[NTEST_PAGES]; 68 }; 69 70 /* 'Worker' vCPU code checking the contents of the test page */ 71 static void worker_guest_code(gva_t test_data) 72 { 73 struct test_data *data = (struct test_data *)test_data; 74 u32 vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX); 75 void *exp_page = (void *)data->test_pages + PAGE_SIZE * NTEST_PAGES; 76 u64 *this_cpu = (u64 *)(exp_page + vcpu_id * sizeof(u64)); 77 u64 expected, val; 78 79 x2apic_enable(); 80 wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); 81 82 for (;;) { 83 cpu_relax(); 84 85 expected = READ_ONCE(*this_cpu); 86 87 /* 88 * Make sure the value in the test page is read after reading 89 * the expectation for the first time. Pairs with wmb() in 90 * prepare_to_test(). 91 */ 92 rmb(); 93 94 val = READ_ONCE(*(u64 *)data->test_pages); 95 96 /* 97 * Make sure the value in the test page is read after before 98 * reading the expectation for the second time. Pairs with wmb() 99 * post_test(). 100 */ 101 rmb(); 102 103 /* 104 * '0' indicates the sender is between iterations, wait until 105 * the sender is ready for this vCPU to start checking again. 106 */ 107 if (!expected) 108 continue; 109 110 /* 111 * Re-read the per-vCPU byte to ensure the sender didn't move 112 * onto a new iteration. 113 */ 114 if (expected != READ_ONCE(*this_cpu)) 115 continue; 116 117 GUEST_ASSERT(val == expected); 118 } 119 } 120 121 /* 122 * Write per-CPU info indicating what each 'worker' CPU is supposed to see in 123 * test page. '0' means don't check. 124 */ 125 static void set_expected_val(void *addr, u64 val, int vcpu_id) 126 { 127 void *exp_page = addr + PAGE_SIZE * NTEST_PAGES; 128 129 *(u64 *)(exp_page + vcpu_id * sizeof(u64)) = val; 130 } 131 132 /* 133 * Update PTEs swapping two test pages. 134 * TODO: use swap()/xchg() when these are provided. 135 */ 136 static void swap_two_test_pages(gpa_t pte_gva1, gpa_t pte_gva2) 137 { 138 u64 tmp = *(u64 *)pte_gva1; 139 140 *(u64 *)pte_gva1 = *(u64 *)pte_gva2; 141 *(u64 *)pte_gva2 = tmp; 142 } 143 144 /* 145 * Prepare to test: 'disable' workers by setting the expectation to '0', 146 * clear hypercall input page and then swap two test pages. 147 */ 148 static inline void prepare_to_test(struct test_data *data) 149 { 150 /* Clear hypercall input page */ 151 memset((void *)data->hcall_gva, 0, PAGE_SIZE); 152 153 /* 'Disable' workers */ 154 set_expected_val((void *)data->test_pages, 0x0, WORKER_VCPU_ID_1); 155 set_expected_val((void *)data->test_pages, 0x0, WORKER_VCPU_ID_2); 156 157 /* Make sure workers are 'disabled' before we swap PTEs. */ 158 wmb(); 159 160 /* Make sure workers have enough time to notice */ 161 udelay(100); 162 163 /* Swap test page mappings */ 164 swap_two_test_pages(data->test_pages_pte[0], data->test_pages_pte[1]); 165 } 166 167 /* 168 * Finalize the test: check hypercall resule set the expected val for 169 * 'worker' CPUs and give them some time to test. 170 */ 171 static inline void post_test(struct test_data *data, u64 exp1, u64 exp2) 172 { 173 /* Make sure we change the expectation after swapping PTEs */ 174 wmb(); 175 176 /* Set the expectation for workers, '0' means don't test */ 177 set_expected_val((void *)data->test_pages, exp1, WORKER_VCPU_ID_1); 178 set_expected_val((void *)data->test_pages, exp2, WORKER_VCPU_ID_2); 179 180 /* Make sure workers have enough time to test */ 181 udelay(100); 182 } 183 184 #define TESTVAL1 0x0101010101010101 185 #define TESTVAL2 0x0202020202020202 186 187 /* Main vCPU doing the test */ 188 static void sender_guest_code(gva_t test_data) 189 { 190 struct test_data *data = (struct test_data *)test_data; 191 struct hv_tlb_flush *flush = (struct hv_tlb_flush *)data->hcall_gva; 192 struct hv_tlb_flush_ex *flush_ex = (struct hv_tlb_flush_ex *)data->hcall_gva; 193 gpa_t hcall_gpa = data->hcall_gpa; 194 int i, stage = 1; 195 196 wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); 197 wrmsr(HV_X64_MSR_HYPERCALL, data->hcall_gpa); 198 199 /* "Slow" hypercalls */ 200 201 GUEST_SYNC(stage++); 202 203 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE for WORKER_VCPU_ID_1 */ 204 for (i = 0; i < NTRY; i++) { 205 prepare_to_test(data); 206 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 207 flush->processor_mask = BIT(WORKER_VCPU_ID_1); 208 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE, hcall_gpa, 209 hcall_gpa + PAGE_SIZE); 210 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 0x0); 211 } 212 213 GUEST_SYNC(stage++); 214 215 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST for WORKER_VCPU_ID_1 */ 216 for (i = 0; i < NTRY; i++) { 217 prepare_to_test(data); 218 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 219 flush->processor_mask = BIT(WORKER_VCPU_ID_1); 220 flush->gva_list[0] = (u64)data->test_pages; 221 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST | 222 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 223 hcall_gpa, hcall_gpa + PAGE_SIZE); 224 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 0x0); 225 } 226 227 GUEST_SYNC(stage++); 228 229 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE for HV_FLUSH_ALL_PROCESSORS */ 230 for (i = 0; i < NTRY; i++) { 231 prepare_to_test(data); 232 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES | 233 HV_FLUSH_ALL_PROCESSORS; 234 flush->processor_mask = 0; 235 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE, hcall_gpa, 236 hcall_gpa + PAGE_SIZE); 237 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, i % 2 ? TESTVAL1 : TESTVAL2); 238 } 239 240 GUEST_SYNC(stage++); 241 242 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST for HV_FLUSH_ALL_PROCESSORS */ 243 for (i = 0; i < NTRY; i++) { 244 prepare_to_test(data); 245 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES | 246 HV_FLUSH_ALL_PROCESSORS; 247 flush->gva_list[0] = (u64)data->test_pages; 248 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST | 249 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 250 hcall_gpa, hcall_gpa + PAGE_SIZE); 251 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 252 i % 2 ? TESTVAL1 : TESTVAL2); 253 } 254 255 GUEST_SYNC(stage++); 256 257 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for WORKER_VCPU_ID_2 */ 258 for (i = 0; i < NTRY; i++) { 259 prepare_to_test(data); 260 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 261 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 262 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64); 263 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 264 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX | 265 (1 << HV_HYPERCALL_VARHEAD_OFFSET), 266 hcall_gpa, hcall_gpa + PAGE_SIZE); 267 post_test(data, 0x0, i % 2 ? TESTVAL1 : TESTVAL2); 268 } 269 270 GUEST_SYNC(stage++); 271 272 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for WORKER_VCPU_ID_2 */ 273 for (i = 0; i < NTRY; i++) { 274 prepare_to_test(data); 275 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 276 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 277 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64); 278 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 279 /* bank_contents and gva_list occupy the same space, thus [1] */ 280 flush_ex->gva_list[1] = (u64)data->test_pages; 281 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 282 (1 << HV_HYPERCALL_VARHEAD_OFFSET) | 283 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 284 hcall_gpa, hcall_gpa + PAGE_SIZE); 285 post_test(data, 0x0, i % 2 ? TESTVAL1 : TESTVAL2); 286 } 287 288 GUEST_SYNC(stage++); 289 290 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for both vCPUs */ 291 for (i = 0; i < NTRY; i++) { 292 prepare_to_test(data); 293 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 294 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 295 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64) | 296 BIT_ULL(WORKER_VCPU_ID_1 / 64); 297 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_1 % 64); 298 flush_ex->hv_vp_set.bank_contents[1] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 299 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX | 300 (2 << HV_HYPERCALL_VARHEAD_OFFSET), 301 hcall_gpa, hcall_gpa + PAGE_SIZE); 302 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 303 i % 2 ? TESTVAL1 : TESTVAL2); 304 } 305 306 GUEST_SYNC(stage++); 307 308 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for both vCPUs */ 309 for (i = 0; i < NTRY; i++) { 310 prepare_to_test(data); 311 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 312 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 313 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_1 / 64) | 314 BIT_ULL(WORKER_VCPU_ID_2 / 64); 315 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_1 % 64); 316 flush_ex->hv_vp_set.bank_contents[1] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 317 /* bank_contents and gva_list occupy the same space, thus [2] */ 318 flush_ex->gva_list[2] = (u64)data->test_pages; 319 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 320 (2 << HV_HYPERCALL_VARHEAD_OFFSET) | 321 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 322 hcall_gpa, hcall_gpa + PAGE_SIZE); 323 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 324 i % 2 ? TESTVAL1 : TESTVAL2); 325 } 326 327 GUEST_SYNC(stage++); 328 329 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for HV_GENERIC_SET_ALL */ 330 for (i = 0; i < NTRY; i++) { 331 prepare_to_test(data); 332 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 333 flush_ex->hv_vp_set.format = HV_GENERIC_SET_ALL; 334 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX, 335 hcall_gpa, hcall_gpa + PAGE_SIZE); 336 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 337 i % 2 ? TESTVAL1 : TESTVAL2); 338 } 339 340 GUEST_SYNC(stage++); 341 342 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for HV_GENERIC_SET_ALL */ 343 for (i = 0; i < NTRY; i++) { 344 prepare_to_test(data); 345 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 346 flush_ex->hv_vp_set.format = HV_GENERIC_SET_ALL; 347 flush_ex->gva_list[0] = (u64)data->test_pages; 348 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 349 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 350 hcall_gpa, hcall_gpa + PAGE_SIZE); 351 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 352 i % 2 ? TESTVAL1 : TESTVAL2); 353 } 354 355 /* "Fast" hypercalls */ 356 357 GUEST_SYNC(stage++); 358 359 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE for WORKER_VCPU_ID_1 */ 360 for (i = 0; i < NTRY; i++) { 361 prepare_to_test(data); 362 flush->processor_mask = BIT(WORKER_VCPU_ID_1); 363 hyperv_write_xmm_input(&flush->processor_mask, 1); 364 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | 365 HV_HYPERCALL_FAST_BIT, 0x0, 366 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 367 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 0x0); 368 } 369 370 GUEST_SYNC(stage++); 371 372 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST for WORKER_VCPU_ID_1 */ 373 for (i = 0; i < NTRY; i++) { 374 prepare_to_test(data); 375 flush->processor_mask = BIT(WORKER_VCPU_ID_1); 376 flush->gva_list[0] = (u64)data->test_pages; 377 hyperv_write_xmm_input(&flush->processor_mask, 1); 378 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST | 379 HV_HYPERCALL_FAST_BIT | 380 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 381 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 382 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 0x0); 383 } 384 385 GUEST_SYNC(stage++); 386 387 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE for HV_FLUSH_ALL_PROCESSORS */ 388 for (i = 0; i < NTRY; i++) { 389 prepare_to_test(data); 390 hyperv_write_xmm_input(&flush->processor_mask, 1); 391 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | 392 HV_HYPERCALL_FAST_BIT, 0x0, 393 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES | 394 HV_FLUSH_ALL_PROCESSORS); 395 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 396 i % 2 ? TESTVAL1 : TESTVAL2); 397 } 398 399 GUEST_SYNC(stage++); 400 401 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST for HV_FLUSH_ALL_PROCESSORS */ 402 for (i = 0; i < NTRY; i++) { 403 prepare_to_test(data); 404 flush->gva_list[0] = (u64)data->test_pages; 405 hyperv_write_xmm_input(&flush->processor_mask, 1); 406 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST | 407 HV_HYPERCALL_FAST_BIT | 408 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 0x0, 409 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES | 410 HV_FLUSH_ALL_PROCESSORS); 411 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 412 i % 2 ? TESTVAL1 : TESTVAL2); 413 } 414 415 GUEST_SYNC(stage++); 416 417 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for WORKER_VCPU_ID_2 */ 418 for (i = 0; i < NTRY; i++) { 419 prepare_to_test(data); 420 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 421 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64); 422 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 423 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 2); 424 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX | 425 HV_HYPERCALL_FAST_BIT | 426 (1 << HV_HYPERCALL_VARHEAD_OFFSET), 427 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 428 post_test(data, 0x0, i % 2 ? TESTVAL1 : TESTVAL2); 429 } 430 431 GUEST_SYNC(stage++); 432 433 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for WORKER_VCPU_ID_2 */ 434 for (i = 0; i < NTRY; i++) { 435 prepare_to_test(data); 436 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 437 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64); 438 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 439 /* bank_contents and gva_list occupy the same space, thus [1] */ 440 flush_ex->gva_list[1] = (u64)data->test_pages; 441 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 2); 442 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 443 HV_HYPERCALL_FAST_BIT | 444 (1 << HV_HYPERCALL_VARHEAD_OFFSET) | 445 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 446 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 447 post_test(data, 0x0, i % 2 ? TESTVAL1 : TESTVAL2); 448 } 449 450 GUEST_SYNC(stage++); 451 452 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for both vCPUs */ 453 for (i = 0; i < NTRY; i++) { 454 prepare_to_test(data); 455 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 456 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_2 / 64) | 457 BIT_ULL(WORKER_VCPU_ID_1 / 64); 458 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_1 % 64); 459 flush_ex->hv_vp_set.bank_contents[1] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 460 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 2); 461 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX | 462 HV_HYPERCALL_FAST_BIT | 463 (2 << HV_HYPERCALL_VARHEAD_OFFSET), 464 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 465 post_test(data, i % 2 ? TESTVAL1 : 466 TESTVAL2, i % 2 ? TESTVAL1 : TESTVAL2); 467 } 468 469 GUEST_SYNC(stage++); 470 471 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for both vCPUs */ 472 for (i = 0; i < NTRY; i++) { 473 prepare_to_test(data); 474 flush_ex->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K; 475 flush_ex->hv_vp_set.valid_bank_mask = BIT_ULL(WORKER_VCPU_ID_1 / 64) | 476 BIT_ULL(WORKER_VCPU_ID_2 / 64); 477 flush_ex->hv_vp_set.bank_contents[0] = BIT_ULL(WORKER_VCPU_ID_1 % 64); 478 flush_ex->hv_vp_set.bank_contents[1] = BIT_ULL(WORKER_VCPU_ID_2 % 64); 479 /* bank_contents and gva_list occupy the same space, thus [2] */ 480 flush_ex->gva_list[2] = (u64)data->test_pages; 481 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 3); 482 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 483 HV_HYPERCALL_FAST_BIT | 484 (2 << HV_HYPERCALL_VARHEAD_OFFSET) | 485 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 486 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 487 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 488 i % 2 ? TESTVAL1 : TESTVAL2); 489 } 490 491 GUEST_SYNC(stage++); 492 493 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX for HV_GENERIC_SET_ALL */ 494 for (i = 0; i < NTRY; i++) { 495 prepare_to_test(data); 496 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 497 flush_ex->hv_vp_set.format = HV_GENERIC_SET_ALL; 498 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 2); 499 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX | 500 HV_HYPERCALL_FAST_BIT, 501 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 502 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 503 i % 2 ? TESTVAL1 : TESTVAL2); 504 } 505 506 GUEST_SYNC(stage++); 507 508 /* HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX for HV_GENERIC_SET_ALL */ 509 for (i = 0; i < NTRY; i++) { 510 prepare_to_test(data); 511 flush_ex->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; 512 flush_ex->hv_vp_set.format = HV_GENERIC_SET_ALL; 513 flush_ex->gva_list[0] = (u64)data->test_pages; 514 hyperv_write_xmm_input(&flush_ex->hv_vp_set, 2); 515 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX | 516 HV_HYPERCALL_FAST_BIT | 517 (1UL << HV_HYPERCALL_REP_COMP_OFFSET), 518 0x0, HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES); 519 post_test(data, i % 2 ? TESTVAL1 : TESTVAL2, 520 i % 2 ? TESTVAL1 : TESTVAL2); 521 } 522 523 GUEST_DONE(); 524 } 525 526 static void *vcpu_thread(void *arg) 527 { 528 struct kvm_vcpu *vcpu = (struct kvm_vcpu *)arg; 529 struct ucall uc; 530 int old; 531 int r; 532 533 r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); 534 TEST_ASSERT(!r, "pthread_setcanceltype failed on vcpu_id=%u with errno=%d", 535 vcpu->id, r); 536 537 vcpu_run(vcpu); 538 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 539 540 switch (get_ucall(vcpu, &uc)) { 541 case UCALL_ABORT: 542 REPORT_GUEST_ASSERT(uc); 543 /* NOT REACHED */ 544 default: 545 TEST_FAIL("Unexpected ucall %lu, vCPU %d", uc.cmd, vcpu->id); 546 } 547 548 return NULL; 549 } 550 551 static void cancel_join_vcpu_thread(pthread_t thread, struct kvm_vcpu *vcpu) 552 { 553 void *retval; 554 int r; 555 556 r = pthread_cancel(thread); 557 TEST_ASSERT(!r, "pthread_cancel on vcpu_id=%d failed with errno=%d", 558 vcpu->id, r); 559 560 r = pthread_join(thread, &retval); 561 TEST_ASSERT(!r, "pthread_join on vcpu_id=%d failed with errno=%d", 562 vcpu->id, r); 563 TEST_ASSERT(retval == PTHREAD_CANCELED, 564 "expected retval=%p, got %p", PTHREAD_CANCELED, 565 retval); 566 } 567 568 int main(int argc, char *argv[]) 569 { 570 struct kvm_vm *vm; 571 struct kvm_vcpu *vcpu[3]; 572 pthread_t threads[2]; 573 gva_t test_data_page, gva; 574 gpa_t gpa; 575 u64 *pte; 576 struct test_data *data; 577 struct ucall uc; 578 int stage = 1, r, i; 579 580 TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_TLBFLUSH)); 581 582 vm = vm_create_with_one_vcpu(&vcpu[0], sender_guest_code); 583 584 /* Test data page */ 585 test_data_page = vm_alloc_page(vm); 586 data = (struct test_data *)addr_gva2hva(vm, test_data_page); 587 588 /* Hypercall input/output */ 589 data->hcall_gva = vm_alloc_pages(vm, 2); 590 data->hcall_gpa = addr_gva2gpa(vm, data->hcall_gva); 591 memset(addr_gva2hva(vm, data->hcall_gva), 0x0, 2 * PAGE_SIZE); 592 593 /* 594 * Test pages: the first one is filled with '0x01's, the second with '0x02's 595 * and the test will swap their mappings. The third page keeps the indication 596 * about the current state of mappings. 597 */ 598 data->test_pages = vm_alloc_pages(vm, NTEST_PAGES + 1); 599 for (i = 0; i < NTEST_PAGES; i++) 600 memset(addr_gva2hva(vm, data->test_pages + PAGE_SIZE * i), 601 (u8)(i + 1), PAGE_SIZE); 602 set_expected_val(addr_gva2hva(vm, data->test_pages), 0x0, WORKER_VCPU_ID_1); 603 set_expected_val(addr_gva2hva(vm, data->test_pages), 0x0, WORKER_VCPU_ID_2); 604 605 /* 606 * Get PTE pointers for test pages and map them inside the guest. 607 * Use separate page for each PTE for simplicity. 608 */ 609 gva = vm_unused_gva_gap(vm, NTEST_PAGES * PAGE_SIZE, KVM_UTIL_MIN_VADDR); 610 for (i = 0; i < NTEST_PAGES; i++) { 611 pte = vm_get_pte(vm, data->test_pages + i * PAGE_SIZE); 612 gpa = addr_hva2gpa(vm, pte); 613 virt_pg_map(vm, gva + PAGE_SIZE * i, gpa & PAGE_MASK); 614 data->test_pages_pte[i] = gva + (gpa & ~PAGE_MASK); 615 } 616 617 /* 618 * Sender vCPU which performs the test: swaps test pages, sets expectation 619 * for 'workers' and issues TLB flush hypercalls. 620 */ 621 vcpu_args_set(vcpu[0], 1, test_data_page); 622 vcpu_set_hv_cpuid(vcpu[0]); 623 624 /* Create worker vCPUs which check the contents of the test pages */ 625 vcpu[1] = vm_vcpu_add(vm, WORKER_VCPU_ID_1, worker_guest_code); 626 vcpu_args_set(vcpu[1], 1, test_data_page); 627 vcpu_set_msr(vcpu[1], HV_X64_MSR_VP_INDEX, WORKER_VCPU_ID_1); 628 vcpu_set_hv_cpuid(vcpu[1]); 629 630 vcpu[2] = vm_vcpu_add(vm, WORKER_VCPU_ID_2, worker_guest_code); 631 vcpu_args_set(vcpu[2], 1, test_data_page); 632 vcpu_set_msr(vcpu[2], HV_X64_MSR_VP_INDEX, WORKER_VCPU_ID_2); 633 vcpu_set_hv_cpuid(vcpu[2]); 634 635 r = pthread_create(&threads[0], NULL, vcpu_thread, vcpu[1]); 636 TEST_ASSERT(!r, "pthread_create() failed"); 637 638 r = pthread_create(&threads[1], NULL, vcpu_thread, vcpu[2]); 639 TEST_ASSERT(!r, "pthread_create() failed"); 640 641 while (true) { 642 vcpu_run(vcpu[0]); 643 TEST_ASSERT_KVM_EXIT_REASON(vcpu[0], KVM_EXIT_IO); 644 645 switch (get_ucall(vcpu[0], &uc)) { 646 case UCALL_SYNC: 647 TEST_ASSERT(uc.args[1] == stage, 648 "Unexpected stage: %ld (%d expected)", 649 uc.args[1], stage); 650 break; 651 case UCALL_ABORT: 652 REPORT_GUEST_ASSERT(uc); 653 /* NOT REACHED */ 654 case UCALL_DONE: 655 goto done; 656 default: 657 TEST_FAIL("Unknown ucall %lu", uc.cmd); 658 } 659 660 stage++; 661 } 662 663 done: 664 cancel_join_vcpu_thread(threads[0], vcpu[1]); 665 cancel_join_vcpu_thread(threads[1], vcpu[2]); 666 kvm_vm_free(vm); 667 668 return 0; 669 } 670