| #include "libcflat.h" |
| #include "processor.h" |
| #include "msr.h" |
| #include "isr.h" |
| #include "vm.h" |
| #include "apic.h" |
| #include "desc.h" |
| #include "smp.h" |
| #include "atomic.h" |
| #include "hyperv.h" |
| #include "alloc_page.h" |
| |
| #define MAX_CPUS 4 |
| |
| static atomic_t isr_enter_count[MAX_CPUS]; |
| |
| static void synic_sint_auto_eoi_isr(isr_regs_t *regs) |
| { |
| atomic_inc(&isr_enter_count[smp_id()]); |
| } |
| |
| static void synic_sint_isr(isr_regs_t *regs) |
| { |
| atomic_inc(&isr_enter_count[smp_id()]); |
| eoi(); |
| } |
| |
| struct sint_vec_entry { |
| int vec; |
| bool auto_eoi; |
| }; |
| |
| struct sint_vec_entry sint_vecs[HV_SYNIC_SINT_COUNT] = { |
| {0xB0, false}, |
| {0xB1, false}, |
| {0xB2, false}, |
| {0xB3, true}, |
| {0xB4, false}, |
| {0xB5, false}, |
| {0xB6, false}, |
| {0xB7, false}, |
| {0xB8, true}, |
| {0xB9, false}, |
| {0xBA, true}, |
| {0xBB, false}, |
| {0xBC, false}, |
| {0xBD, false}, |
| {0xBE, true}, |
| {0xBF, false}, |
| }; |
| |
| static void synic_prepare_sint_vecs(void) |
| { |
| bool auto_eoi; |
| int i, vec; |
| |
| for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { |
| vec = sint_vecs[i].vec; |
| auto_eoi = sint_vecs[i].auto_eoi; |
| handle_irq(vec, (auto_eoi) ? synic_sint_auto_eoi_isr : synic_sint_isr); |
| } |
| } |
| |
| static void synic_sints_prepare(int vcpu) |
| { |
| bool auto_eoi; |
| int i, vec; |
| |
| for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { |
| vec = sint_vecs[i].vec; |
| auto_eoi = sint_vecs[i].auto_eoi; |
| synic_sint_create(i, vec, auto_eoi); |
| } |
| } |
| |
| static void synic_test_prepare(void *ctx) |
| { |
| u64 r; |
| int i = 0; |
| |
| write_cr3((ulong)ctx); |
| sti(); |
| |
| rdmsr(HV_X64_MSR_SVERSION); |
| rdmsr(HV_X64_MSR_SIMP); |
| rdmsr(HV_X64_MSR_SIEFP); |
| rdmsr(HV_X64_MSR_SCONTROL); |
| for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { |
| rdmsr(HV_X64_MSR_SINT0 + i); |
| } |
| r = rdmsr(HV_X64_MSR_EOM); |
| if (r != 0) { |
| report_fail("Hyper-V SynIC test, EOM read %#" PRIx64, r); |
| return; |
| } |
| |
| wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) | |
| HV_SYNIC_SIMP_ENABLE); |
| wrmsr(HV_X64_MSR_SIEFP, (u64)virt_to_phys(alloc_page())| |
| HV_SYNIC_SIEFP_ENABLE); |
| wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); |
| |
| synic_sints_prepare(smp_id()); |
| } |
| |
| static void synic_sints_test(int dst_vcpu) |
| { |
| int i; |
| |
| atomic_set(&isr_enter_count[dst_vcpu], 0); |
| for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { |
| synic_sint_set(dst_vcpu, i); |
| } |
| |
| while (atomic_read(&isr_enter_count[dst_vcpu]) != HV_SYNIC_SINT_COUNT) { |
| pause(); |
| } |
| } |
| |
| static void synic_test(void *ctx) |
| { |
| int dst_vcpu = (ulong)ctx; |
| |
| sti(); |
| synic_sints_test(dst_vcpu); |
| } |
| |
| static void synic_test_cleanup(void *ctx) |
| { |
| int i; |
| |
| sti(); |
| for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) { |
| synic_sint_destroy(i); |
| } |
| |
| wrmsr(HV_X64_MSR_SCONTROL, 0); |
| wrmsr(HV_X64_MSR_SIMP, 0); |
| wrmsr(HV_X64_MSR_SIEFP, 0); |
| } |
| |
| int main(int ac, char **av) |
| { |
| int ncpus, i; |
| bool ok; |
| |
| if (!hv_synic_supported()) { |
| report_skip("Hyper-V SynIC is not supported"); |
| goto done; |
| } |
| |
| setup_vm(); |
| enable_apic(); |
| |
| ncpus = cpu_count(); |
| if (ncpus > MAX_CPUS) |
| report_abort("number cpus exceeds %d", MAX_CPUS); |
| printf("ncpus = %d\n", ncpus); |
| |
| synic_prepare_sint_vecs(); |
| |
| printf("prepare\n"); |
| on_cpus(synic_test_prepare, (void *)read_cr3()); |
| |
| for (i = 0; i < ncpus; i++) { |
| printf("test %d -> %d\n", i, ncpus - 1 - i); |
| on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i)); |
| } |
| while (cpus_active() > 1) |
| pause(); |
| |
| printf("cleanup\n"); |
| on_cpus(synic_test_cleanup, NULL); |
| |
| ok = true; |
| for (i = 0; i < ncpus; ++i) { |
| printf("isr_enter_count[%d] = %d\n", |
| i, atomic_read(&isr_enter_count[i])); |
| ok &= atomic_read(&isr_enter_count[i]) == 16; |
| } |
| |
| report(ok, "Hyper-V SynIC test"); |
| |
| done: |
| return report_summary(); |
| } |