blob: 9d61d8362ebd09aca788b38879ade285c31423b7 [file] [log] [blame]
#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)
{
if (synic_supported()) {
int ncpus, i;
bool ok;
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");
} else {
printf("Hyper-V SynIC is not supported");
}
return report_summary();
}