Path: blob/master/tools/testing/selftests/kvm/x86/hyperv_ipi.c
38236 views
// SPDX-License-Identifier: GPL-2.01/*2* Hyper-V HvCallSendSyntheticClusterIpi{,Ex} tests3*4* Copyright (C) 2022, Red Hat, Inc.5*6*/7#include <pthread.h>8#include <inttypes.h>910#include "kvm_util.h"11#include "hyperv.h"12#include "test_util.h"13#include "vmx.h"1415#define RECEIVER_VCPU_ID_1 216#define RECEIVER_VCPU_ID_2 651718#define IPI_VECTOR 0xfe1920static volatile uint64_t ipis_rcvd[RECEIVER_VCPU_ID_2 + 1];2122struct hv_vpset {23u64 format;24u64 valid_bank_mask;25u64 bank_contents[2];26};2728enum HV_GENERIC_SET_FORMAT {29HV_GENERIC_SET_SPARSE_4K,30HV_GENERIC_SET_ALL,31};3233/* HvCallSendSyntheticClusterIpi hypercall */34struct hv_send_ipi {35u32 vector;36u32 reserved;37u64 cpu_mask;38};3940/* HvCallSendSyntheticClusterIpiEx hypercall */41struct hv_send_ipi_ex {42u32 vector;43u32 reserved;44struct hv_vpset vp_set;45};4647static inline void hv_init(vm_vaddr_t pgs_gpa)48{49wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);50wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);51}5253static void receiver_code(void *hcall_page, vm_vaddr_t pgs_gpa)54{55u32 vcpu_id;5657x2apic_enable();58hv_init(pgs_gpa);5960vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX);6162/* Signal sender vCPU we're ready */63ipis_rcvd[vcpu_id] = (u64)-1;6465for (;;) {66safe_halt();67cli();68}69}7071static void guest_ipi_handler(struct ex_regs *regs)72{73u32 vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX);7475ipis_rcvd[vcpu_id]++;76wrmsr(HV_X64_MSR_EOI, 1);77}7879static inline void nop_loop(void)80{81int i;8283for (i = 0; i < 100000000; i++)84asm volatile("nop");85}8687static void sender_guest_code(void *hcall_page, vm_vaddr_t pgs_gpa)88{89struct hv_send_ipi *ipi = (struct hv_send_ipi *)hcall_page;90struct hv_send_ipi_ex *ipi_ex = (struct hv_send_ipi_ex *)hcall_page;91int stage = 1, ipis_expected[2] = {0};9293hv_init(pgs_gpa);94GUEST_SYNC(stage++);9596/* Wait for receiver vCPUs to come up */97while (!ipis_rcvd[RECEIVER_VCPU_ID_1] || !ipis_rcvd[RECEIVER_VCPU_ID_2])98nop_loop();99ipis_rcvd[RECEIVER_VCPU_ID_1] = ipis_rcvd[RECEIVER_VCPU_ID_2] = 0;100101/* 'Slow' HvCallSendSyntheticClusterIpi to RECEIVER_VCPU_ID_1 */102ipi->vector = IPI_VECTOR;103ipi->cpu_mask = 1 << RECEIVER_VCPU_ID_1;104hyperv_hypercall(HVCALL_SEND_IPI, pgs_gpa, pgs_gpa + PAGE_SIZE);105nop_loop();106GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);107GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);108GUEST_SYNC(stage++);109/* 'Fast' HvCallSendSyntheticClusterIpi to RECEIVER_VCPU_ID_1 */110hyperv_hypercall(HVCALL_SEND_IPI | HV_HYPERCALL_FAST_BIT,111IPI_VECTOR, 1 << RECEIVER_VCPU_ID_1);112nop_loop();113GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);114GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);115GUEST_SYNC(stage++);116117/* 'Slow' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_1 */118memset(hcall_page, 0, PAGE_SIZE);119ipi_ex->vector = IPI_VECTOR;120ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;121ipi_ex->vp_set.valid_bank_mask = 1 << 0;122ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_1);123hyperv_hypercall(HVCALL_SEND_IPI_EX | (1 << HV_HYPERCALL_VARHEAD_OFFSET),124pgs_gpa, pgs_gpa + PAGE_SIZE);125nop_loop();126GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);127GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);128GUEST_SYNC(stage++);129/* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_1 */130hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 1);131hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |132(1 << HV_HYPERCALL_VARHEAD_OFFSET),133IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);134nop_loop();135GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);136GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);137GUEST_SYNC(stage++);138139/* 'Slow' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_2 */140memset(hcall_page, 0, PAGE_SIZE);141ipi_ex->vector = IPI_VECTOR;142ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;143ipi_ex->vp_set.valid_bank_mask = 1 << 1;144ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_2 - 64);145hyperv_hypercall(HVCALL_SEND_IPI_EX | (1 << HV_HYPERCALL_VARHEAD_OFFSET),146pgs_gpa, pgs_gpa + PAGE_SIZE);147nop_loop();148GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ipis_expected[0]);149GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);150GUEST_SYNC(stage++);151/* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_2 */152hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 1);153hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |154(1 << HV_HYPERCALL_VARHEAD_OFFSET),155IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);156nop_loop();157GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ipis_expected[0]);158GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);159GUEST_SYNC(stage++);160161/* 'Slow' HvCallSendSyntheticClusterIpiEx to both RECEIVER_VCPU_ID_{1,2} */162memset(hcall_page, 0, PAGE_SIZE);163ipi_ex->vector = IPI_VECTOR;164ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;165ipi_ex->vp_set.valid_bank_mask = 1 << 1 | 1;166ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_1);167ipi_ex->vp_set.bank_contents[1] = BIT(RECEIVER_VCPU_ID_2 - 64);168hyperv_hypercall(HVCALL_SEND_IPI_EX | (2 << HV_HYPERCALL_VARHEAD_OFFSET),169pgs_gpa, pgs_gpa + PAGE_SIZE);170nop_loop();171GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);172GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);173GUEST_SYNC(stage++);174/* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to both RECEIVER_VCPU_ID_{1, 2} */175hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 2);176hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |177(2 << HV_HYPERCALL_VARHEAD_OFFSET),178IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);179nop_loop();180GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);181GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);182GUEST_SYNC(stage++);183184/* 'Slow' HvCallSendSyntheticClusterIpiEx to HV_GENERIC_SET_ALL */185memset(hcall_page, 0, PAGE_SIZE);186ipi_ex->vector = IPI_VECTOR;187ipi_ex->vp_set.format = HV_GENERIC_SET_ALL;188hyperv_hypercall(HVCALL_SEND_IPI_EX, pgs_gpa, pgs_gpa + PAGE_SIZE);189nop_loop();190GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);191GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);192GUEST_SYNC(stage++);193/*194* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to HV_GENERIC_SET_ALL.195*/196ipi_ex->vp_set.valid_bank_mask = 0;197hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 2);198hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT,199IPI_VECTOR, HV_GENERIC_SET_ALL);200nop_loop();201GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);202GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);203GUEST_SYNC(stage++);204205GUEST_DONE();206}207208static void *vcpu_thread(void *arg)209{210struct kvm_vcpu *vcpu = (struct kvm_vcpu *)arg;211int old, r;212213r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);214TEST_ASSERT(!r, "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",215vcpu->id, r);216217vcpu_run(vcpu);218219TEST_FAIL("vCPU %u exited unexpectedly", vcpu->id);220221return NULL;222}223224static void cancel_join_vcpu_thread(pthread_t thread, struct kvm_vcpu *vcpu)225{226void *retval;227int r;228229r = pthread_cancel(thread);230TEST_ASSERT(!r, "pthread_cancel on vcpu_id=%d failed with errno=%d",231vcpu->id, r);232233r = pthread_join(thread, &retval);234TEST_ASSERT(!r, "pthread_join on vcpu_id=%d failed with errno=%d",235vcpu->id, r);236TEST_ASSERT(retval == PTHREAD_CANCELED,237"expected retval=%p, got %p", PTHREAD_CANCELED,238retval);239}240241int main(int argc, char *argv[])242{243struct kvm_vm *vm;244struct kvm_vcpu *vcpu[3];245vm_vaddr_t hcall_page;246pthread_t threads[2];247int stage = 1, r;248struct ucall uc;249250TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_SEND_IPI));251252vm = vm_create_with_one_vcpu(&vcpu[0], sender_guest_code);253254/* Hypercall input/output */255hcall_page = vm_vaddr_alloc_pages(vm, 2);256memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize());257258259vcpu[1] = vm_vcpu_add(vm, RECEIVER_VCPU_ID_1, receiver_code);260vcpu_args_set(vcpu[1], 2, hcall_page, addr_gva2gpa(vm, hcall_page));261vcpu_set_msr(vcpu[1], HV_X64_MSR_VP_INDEX, RECEIVER_VCPU_ID_1);262vcpu_set_hv_cpuid(vcpu[1]);263264vcpu[2] = vm_vcpu_add(vm, RECEIVER_VCPU_ID_2, receiver_code);265vcpu_args_set(vcpu[2], 2, hcall_page, addr_gva2gpa(vm, hcall_page));266vcpu_set_msr(vcpu[2], HV_X64_MSR_VP_INDEX, RECEIVER_VCPU_ID_2);267vcpu_set_hv_cpuid(vcpu[2]);268269vm_install_exception_handler(vm, IPI_VECTOR, guest_ipi_handler);270271vcpu_args_set(vcpu[0], 2, hcall_page, addr_gva2gpa(vm, hcall_page));272vcpu_set_hv_cpuid(vcpu[0]);273274r = pthread_create(&threads[0], NULL, vcpu_thread, vcpu[1]);275TEST_ASSERT(!r, "pthread_create failed errno=%d", r);276277r = pthread_create(&threads[1], NULL, vcpu_thread, vcpu[2]);278TEST_ASSERT(!r, "pthread_create failed errno=%d", errno);279280while (true) {281vcpu_run(vcpu[0]);282283TEST_ASSERT_KVM_EXIT_REASON(vcpu[0], KVM_EXIT_IO);284285switch (get_ucall(vcpu[0], &uc)) {286case UCALL_SYNC:287TEST_ASSERT(uc.args[1] == stage,288"Unexpected stage: %ld (%d expected)",289uc.args[1], stage);290break;291case UCALL_DONE:292goto done;293case UCALL_ABORT:294REPORT_GUEST_ASSERT(uc);295/* NOT REACHED */296default:297TEST_FAIL("Unknown ucall %lu", uc.cmd);298}299300stage++;301}302303done:304cancel_join_vcpu_thread(threads[0], vcpu[1]);305cancel_join_vcpu_thread(threads[1], vcpu[2]);306kvm_vm_free(vm);307308return r;309}310311312