xref: /kvm-unit-tests/x86/hyperv_stimer.c (revision 9b910f27c616038f736e720d54ccabf453be8256)
169d4bf75SAndrey Smetanin #include "libcflat.h"
269d4bf75SAndrey Smetanin #include "processor.h"
369d4bf75SAndrey Smetanin #include "msr.h"
469d4bf75SAndrey Smetanin #include "isr.h"
569d4bf75SAndrey Smetanin #include "vm.h"
669d4bf75SAndrey Smetanin #include "apic.h"
769d4bf75SAndrey Smetanin #include "desc.h"
869d4bf75SAndrey Smetanin #include "smp.h"
969d4bf75SAndrey Smetanin #include "atomic.h"
1069d4bf75SAndrey Smetanin #include "hyperv.h"
118226c540SAlexander Gordeev #include "asm/barrier.h"
1269d4bf75SAndrey Smetanin 
1369d4bf75SAndrey Smetanin #define MAX_CPUS 4
1469d4bf75SAndrey Smetanin 
1569d4bf75SAndrey Smetanin #define SINT1_VEC 0xF1
1669d4bf75SAndrey Smetanin #define SINT2_VEC 0xF2
1769d4bf75SAndrey Smetanin 
1869d4bf75SAndrey Smetanin #define SINT1_NUM 2
1969d4bf75SAndrey Smetanin #define SINT2_NUM 3
2069d4bf75SAndrey Smetanin #define ONE_MS_IN_100NS 10000
2169d4bf75SAndrey Smetanin 
2269d4bf75SAndrey Smetanin static struct spinlock g_synic_alloc_lock;
2369d4bf75SAndrey Smetanin 
2469d4bf75SAndrey Smetanin struct stimer {
2569d4bf75SAndrey Smetanin     int sint;
2669d4bf75SAndrey Smetanin     int index;
2769d4bf75SAndrey Smetanin     atomic_t fire_count;
2869d4bf75SAndrey Smetanin };
2969d4bf75SAndrey Smetanin 
3069d4bf75SAndrey Smetanin struct svcpu {
3169d4bf75SAndrey Smetanin     int vcpu;
3269d4bf75SAndrey Smetanin     void *msg_page;
3369d4bf75SAndrey Smetanin     void *evt_page;
3469d4bf75SAndrey Smetanin     struct stimer timer[HV_SYNIC_STIMER_COUNT];
3569d4bf75SAndrey Smetanin };
3669d4bf75SAndrey Smetanin 
3769d4bf75SAndrey Smetanin static struct svcpu g_synic_vcpu[MAX_CPUS];
3869d4bf75SAndrey Smetanin 
3969d4bf75SAndrey Smetanin static void *synic_alloc_page(void)
4069d4bf75SAndrey Smetanin {
4169d4bf75SAndrey Smetanin     void *page;
4269d4bf75SAndrey Smetanin 
4369d4bf75SAndrey Smetanin     spin_lock(&g_synic_alloc_lock);
4469d4bf75SAndrey Smetanin     page = alloc_page();
4569d4bf75SAndrey Smetanin     spin_unlock(&g_synic_alloc_lock);
4669d4bf75SAndrey Smetanin     return page;
4769d4bf75SAndrey Smetanin }
4869d4bf75SAndrey Smetanin 
4969d4bf75SAndrey Smetanin static void synic_free_page(void *page)
5069d4bf75SAndrey Smetanin {
5169d4bf75SAndrey Smetanin     spin_lock(&g_synic_alloc_lock);
5269d4bf75SAndrey Smetanin     free_page(page);
5369d4bf75SAndrey Smetanin     spin_unlock(&g_synic_alloc_lock);
5469d4bf75SAndrey Smetanin }
5569d4bf75SAndrey Smetanin 
5669d4bf75SAndrey Smetanin static void stimer_init(struct stimer *timer, int index)
5769d4bf75SAndrey Smetanin {
5869d4bf75SAndrey Smetanin     memset(timer, 0, sizeof(*timer));
5969d4bf75SAndrey Smetanin     timer->index = index;
6069d4bf75SAndrey Smetanin }
6169d4bf75SAndrey Smetanin 
6269d4bf75SAndrey Smetanin static void synic_enable(void)
6369d4bf75SAndrey Smetanin {
6469d4bf75SAndrey Smetanin     int vcpu = smp_id(), i;
6569d4bf75SAndrey Smetanin     struct svcpu *svcpu = &g_synic_vcpu[vcpu];
6669d4bf75SAndrey Smetanin 
6769d4bf75SAndrey Smetanin     memset(svcpu, 0, sizeof(*svcpu));
6869d4bf75SAndrey Smetanin     svcpu->vcpu = vcpu;
6969d4bf75SAndrey Smetanin     svcpu->msg_page = synic_alloc_page();
7069d4bf75SAndrey Smetanin     for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
7169d4bf75SAndrey Smetanin         stimer_init(&svcpu->timer[i], i);
7269d4bf75SAndrey Smetanin     }
7369d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(svcpu->msg_page) |
7469d4bf75SAndrey Smetanin             HV_SYNIC_SIMP_ENABLE);
7569d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
7669d4bf75SAndrey Smetanin }
7769d4bf75SAndrey Smetanin 
7869d4bf75SAndrey Smetanin static void stimer_shutdown(struct stimer *timer)
7969d4bf75SAndrey Smetanin {
8069d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
8169d4bf75SAndrey Smetanin }
8269d4bf75SAndrey Smetanin 
8369d4bf75SAndrey Smetanin static void process_stimer_expired(struct svcpu *svcpu, struct stimer *timer,
8469d4bf75SAndrey Smetanin                                    u64 expiration_time, u64 delivery_time)
8569d4bf75SAndrey Smetanin {
8669d4bf75SAndrey Smetanin     atomic_inc(&timer->fire_count);
8769d4bf75SAndrey Smetanin }
8869d4bf75SAndrey Smetanin 
8969d4bf75SAndrey Smetanin static void process_stimer_msg(struct svcpu *svcpu,
9069d4bf75SAndrey Smetanin                               struct hv_message *msg, int sint)
9169d4bf75SAndrey Smetanin {
9269d4bf75SAndrey Smetanin     struct hv_timer_message_payload *payload =
9369d4bf75SAndrey Smetanin                         (struct hv_timer_message_payload *)msg->u.payload;
9469d4bf75SAndrey Smetanin     struct stimer *timer;
9569d4bf75SAndrey Smetanin 
9669d4bf75SAndrey Smetanin     if (msg->header.message_type != HVMSG_TIMER_EXPIRED &&
9769d4bf75SAndrey Smetanin         msg->header.message_type != HVMSG_NONE) {
9869d4bf75SAndrey Smetanin         report("invalid Hyper-V SynIC msg type", false);
9969d4bf75SAndrey Smetanin         report_summary();
100a2b7c499SAndrew Jones         abort();
10169d4bf75SAndrey Smetanin     }
10269d4bf75SAndrey Smetanin 
10369d4bf75SAndrey Smetanin     if (msg->header.message_type == HVMSG_NONE) {
10469d4bf75SAndrey Smetanin         return;
10569d4bf75SAndrey Smetanin     }
10669d4bf75SAndrey Smetanin 
10769d4bf75SAndrey Smetanin     if (msg->header.payload_size < sizeof(*payload)) {
10869d4bf75SAndrey Smetanin         report("invalid Hyper-V SynIC msg payload size", false);
10969d4bf75SAndrey Smetanin         report_summary();
110a2b7c499SAndrew Jones         abort();
11169d4bf75SAndrey Smetanin     }
11269d4bf75SAndrey Smetanin 
11369d4bf75SAndrey Smetanin     /* Now process timer expiration message */
11469d4bf75SAndrey Smetanin 
11569d4bf75SAndrey Smetanin     if (payload->timer_index >= ARRAY_SIZE(svcpu->timer)) {
11669d4bf75SAndrey Smetanin         report("invalid Hyper-V SynIC timer index", false);
11769d4bf75SAndrey Smetanin         report_summary();
118a2b7c499SAndrew Jones         abort();
11969d4bf75SAndrey Smetanin     }
12069d4bf75SAndrey Smetanin     timer = &svcpu->timer[payload->timer_index];
12169d4bf75SAndrey Smetanin     process_stimer_expired(svcpu, timer, payload->expiration_time,
12269d4bf75SAndrey Smetanin                           payload->delivery_time);
12369d4bf75SAndrey Smetanin 
12469d4bf75SAndrey Smetanin     msg->header.message_type = HVMSG_NONE;
12569d4bf75SAndrey Smetanin     mb();
12669d4bf75SAndrey Smetanin     if (msg->header.message_flags.msg_pending) {
12769d4bf75SAndrey Smetanin         wrmsr(HV_X64_MSR_EOM, 0);
12869d4bf75SAndrey Smetanin     }
12969d4bf75SAndrey Smetanin }
13069d4bf75SAndrey Smetanin 
13169d4bf75SAndrey Smetanin static void __stimer_isr(int vcpu)
13269d4bf75SAndrey Smetanin {
13369d4bf75SAndrey Smetanin     struct svcpu *svcpu = &g_synic_vcpu[vcpu];
13469d4bf75SAndrey Smetanin     struct hv_message_page *msg_page;
13569d4bf75SAndrey Smetanin     struct hv_message *msg;
13669d4bf75SAndrey Smetanin     int i;
13769d4bf75SAndrey Smetanin 
13869d4bf75SAndrey Smetanin 
13969d4bf75SAndrey Smetanin     msg_page = (struct hv_message_page *)svcpu->msg_page;
14069d4bf75SAndrey Smetanin     for (i = 0; i < ARRAY_SIZE(msg_page->sint_message); i++) {
14169d4bf75SAndrey Smetanin         msg = &msg_page->sint_message[i];
14269d4bf75SAndrey Smetanin         process_stimer_msg(svcpu, msg, i);
14369d4bf75SAndrey Smetanin     }
14469d4bf75SAndrey Smetanin }
14569d4bf75SAndrey Smetanin 
14669d4bf75SAndrey Smetanin static void stimer_isr(isr_regs_t *regs)
14769d4bf75SAndrey Smetanin {
14869d4bf75SAndrey Smetanin     int vcpu = smp_id();
14969d4bf75SAndrey Smetanin 
15069d4bf75SAndrey Smetanin     __stimer_isr(vcpu);
15169d4bf75SAndrey Smetanin     eoi();
15269d4bf75SAndrey Smetanin }
15369d4bf75SAndrey Smetanin 
15469d4bf75SAndrey Smetanin static void stimer_isr_auto_eoi(isr_regs_t *regs)
15569d4bf75SAndrey Smetanin {
15669d4bf75SAndrey Smetanin     int vcpu = smp_id();
15769d4bf75SAndrey Smetanin 
15869d4bf75SAndrey Smetanin     __stimer_isr(vcpu);
15969d4bf75SAndrey Smetanin }
16069d4bf75SAndrey Smetanin 
16169d4bf75SAndrey Smetanin static void stimer_start(struct stimer *timer,
16269d4bf75SAndrey Smetanin                          bool auto_enable, bool periodic,
16369d4bf75SAndrey Smetanin                          u64 tick_100ns, int sint)
16469d4bf75SAndrey Smetanin {
16569d4bf75SAndrey Smetanin     u64 config, count;
16669d4bf75SAndrey Smetanin 
16769d4bf75SAndrey Smetanin     timer->sint = sint;
16869d4bf75SAndrey Smetanin     atomic_set(&timer->fire_count, 0);
16969d4bf75SAndrey Smetanin 
17069d4bf75SAndrey Smetanin     config = 0;
17169d4bf75SAndrey Smetanin     if (periodic) {
17269d4bf75SAndrey Smetanin         config |= HV_STIMER_PERIODIC;
17369d4bf75SAndrey Smetanin     }
17469d4bf75SAndrey Smetanin 
17569d4bf75SAndrey Smetanin     config |= ((u8)(sint & 0xFF)) << 16;
17669d4bf75SAndrey Smetanin     config |= HV_STIMER_ENABLE;
17769d4bf75SAndrey Smetanin     if (auto_enable) {
17869d4bf75SAndrey Smetanin         config |= HV_STIMER_AUTOENABLE;
17969d4bf75SAndrey Smetanin     }
18069d4bf75SAndrey Smetanin 
18169d4bf75SAndrey Smetanin     if (periodic) {
18269d4bf75SAndrey Smetanin         count = tick_100ns;
18369d4bf75SAndrey Smetanin     } else {
18469d4bf75SAndrey Smetanin         count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns;
18569d4bf75SAndrey Smetanin     }
18669d4bf75SAndrey Smetanin 
18769d4bf75SAndrey Smetanin     if (!auto_enable) {
18869d4bf75SAndrey Smetanin         wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
18969d4bf75SAndrey Smetanin         wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
19069d4bf75SAndrey Smetanin     } else {
19169d4bf75SAndrey Smetanin         wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
19269d4bf75SAndrey Smetanin         wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
19369d4bf75SAndrey Smetanin     }
19469d4bf75SAndrey Smetanin }
19569d4bf75SAndrey Smetanin 
19669d4bf75SAndrey Smetanin static void stimers_shutdown(void)
19769d4bf75SAndrey Smetanin {
19869d4bf75SAndrey Smetanin     int vcpu = smp_id(), i;
19969d4bf75SAndrey Smetanin     struct svcpu *svcpu = &g_synic_vcpu[vcpu];
20069d4bf75SAndrey Smetanin 
20169d4bf75SAndrey Smetanin     for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
20269d4bf75SAndrey Smetanin         stimer_shutdown(&svcpu->timer[i]);
20369d4bf75SAndrey Smetanin     }
20469d4bf75SAndrey Smetanin }
20569d4bf75SAndrey Smetanin 
20669d4bf75SAndrey Smetanin static void synic_disable(void)
20769d4bf75SAndrey Smetanin {
20869d4bf75SAndrey Smetanin     int vcpu = smp_id();
20969d4bf75SAndrey Smetanin     struct svcpu *svcpu = &g_synic_vcpu[vcpu];
21069d4bf75SAndrey Smetanin 
21169d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_SCONTROL, 0);
21269d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_SIMP, 0);
21369d4bf75SAndrey Smetanin     wrmsr(HV_X64_MSR_SIEFP, 0);
21469d4bf75SAndrey Smetanin     synic_free_page(svcpu->msg_page);
21569d4bf75SAndrey Smetanin }
21669d4bf75SAndrey Smetanin 
21769d4bf75SAndrey Smetanin 
21869d4bf75SAndrey Smetanin static void stimer_test_prepare(void *ctx)
21969d4bf75SAndrey Smetanin {
22069d4bf75SAndrey Smetanin     write_cr3((ulong)ctx);
22169d4bf75SAndrey Smetanin     synic_enable();
222d2c248afSRoman Kagan     synic_sint_create(SINT1_NUM, SINT1_VEC, false);
223d2c248afSRoman Kagan     synic_sint_create(SINT2_NUM, SINT2_VEC, true);
22469d4bf75SAndrey Smetanin }
22569d4bf75SAndrey Smetanin 
22669d4bf75SAndrey Smetanin static void stimer_test_periodic(int vcpu, struct stimer *timer1,
22769d4bf75SAndrey Smetanin                                  struct stimer *timer2)
22869d4bf75SAndrey Smetanin {
22969d4bf75SAndrey Smetanin     /* Check periodic timers */
23069d4bf75SAndrey Smetanin     stimer_start(timer1, false, true, ONE_MS_IN_100NS, SINT1_NUM);
23169d4bf75SAndrey Smetanin     stimer_start(timer2, false, true, ONE_MS_IN_100NS, SINT2_NUM);
23269d4bf75SAndrey Smetanin     while ((atomic_read(&timer1->fire_count) < 1000) ||
23369d4bf75SAndrey Smetanin            (atomic_read(&timer2->fire_count) < 1000)) {
23469d4bf75SAndrey Smetanin         pause();
23569d4bf75SAndrey Smetanin     }
23669d4bf75SAndrey Smetanin     report("Hyper-V SynIC periodic timers test vcpu %d", true, vcpu);
23769d4bf75SAndrey Smetanin     stimer_shutdown(timer1);
23869d4bf75SAndrey Smetanin     stimer_shutdown(timer2);
23969d4bf75SAndrey Smetanin }
24069d4bf75SAndrey Smetanin 
24169d4bf75SAndrey Smetanin static void stimer_test_one_shot(int vcpu, struct stimer *timer)
24269d4bf75SAndrey Smetanin {
24369d4bf75SAndrey Smetanin     /* Check one-shot timer */
24469d4bf75SAndrey Smetanin     stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
24569d4bf75SAndrey Smetanin     while (atomic_read(&timer->fire_count) < 1) {
24669d4bf75SAndrey Smetanin         pause();
24769d4bf75SAndrey Smetanin     }
24869d4bf75SAndrey Smetanin     report("Hyper-V SynIC one-shot test vcpu %d", true, vcpu);
24969d4bf75SAndrey Smetanin     stimer_shutdown(timer);
25069d4bf75SAndrey Smetanin }
25169d4bf75SAndrey Smetanin 
25269d4bf75SAndrey Smetanin static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer)
25369d4bf75SAndrey Smetanin {
25469d4bf75SAndrey Smetanin     /* Check auto-enable one-shot timer */
25569d4bf75SAndrey Smetanin     stimer_start(timer, true, false, ONE_MS_IN_100NS, SINT1_NUM);
25669d4bf75SAndrey Smetanin     while (atomic_read(&timer->fire_count) < 1) {
25769d4bf75SAndrey Smetanin         pause();
25869d4bf75SAndrey Smetanin     }
25969d4bf75SAndrey Smetanin     report("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", true, vcpu);
26069d4bf75SAndrey Smetanin     stimer_shutdown(timer);
26169d4bf75SAndrey Smetanin }
26269d4bf75SAndrey Smetanin 
26369d4bf75SAndrey Smetanin static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
26469d4bf75SAndrey Smetanin {
26569d4bf75SAndrey Smetanin     /* Check auto-enable periodic timer */
26669d4bf75SAndrey Smetanin     stimer_start(timer, true, true, ONE_MS_IN_100NS, SINT1_NUM);
26769d4bf75SAndrey Smetanin     while (atomic_read(&timer->fire_count) < 1000) {
26869d4bf75SAndrey Smetanin         pause();
26969d4bf75SAndrey Smetanin     }
27069d4bf75SAndrey Smetanin     report("Hyper-V SynIC auto-enable periodic timer test vcpu %d", true, vcpu);
27169d4bf75SAndrey Smetanin     stimer_shutdown(timer);
27269d4bf75SAndrey Smetanin }
27369d4bf75SAndrey Smetanin 
27469d4bf75SAndrey Smetanin static void stimer_test(void *ctx)
27569d4bf75SAndrey Smetanin {
27669d4bf75SAndrey Smetanin     int vcpu = smp_id();
27769d4bf75SAndrey Smetanin     struct svcpu *svcpu = &g_synic_vcpu[vcpu];
27869d4bf75SAndrey Smetanin     struct stimer *timer1, *timer2;
27969d4bf75SAndrey Smetanin 
28069d4bf75SAndrey Smetanin     irq_enable();
28169d4bf75SAndrey Smetanin 
28269d4bf75SAndrey Smetanin     timer1 = &svcpu->timer[0];
28369d4bf75SAndrey Smetanin     timer2 = &svcpu->timer[1];
28469d4bf75SAndrey Smetanin 
28569d4bf75SAndrey Smetanin     stimer_test_periodic(vcpu, timer1, timer2);
28669d4bf75SAndrey Smetanin     stimer_test_one_shot(vcpu, timer1);
28769d4bf75SAndrey Smetanin     stimer_test_auto_enable_one_shot(vcpu, timer2);
28869d4bf75SAndrey Smetanin     stimer_test_auto_enable_periodic(vcpu, timer1);
28969d4bf75SAndrey Smetanin 
29069d4bf75SAndrey Smetanin     irq_disable();
29169d4bf75SAndrey Smetanin }
29269d4bf75SAndrey Smetanin 
29369d4bf75SAndrey Smetanin static void stimer_test_cleanup(void *ctx)
29469d4bf75SAndrey Smetanin {
29569d4bf75SAndrey Smetanin     stimers_shutdown();
296d2c248afSRoman Kagan     synic_sint_destroy(SINT1_NUM);
297d2c248afSRoman Kagan     synic_sint_destroy(SINT2_NUM);
29869d4bf75SAndrey Smetanin     synic_disable();
29969d4bf75SAndrey Smetanin }
30069d4bf75SAndrey Smetanin 
30169d4bf75SAndrey Smetanin static void stimer_test_all(void)
30269d4bf75SAndrey Smetanin {
30369d4bf75SAndrey Smetanin     int ncpus;
30469d4bf75SAndrey Smetanin 
30569d4bf75SAndrey Smetanin     setup_vm();
30669d4bf75SAndrey Smetanin     smp_init();
30769d4bf75SAndrey Smetanin     enable_apic();
30869d4bf75SAndrey Smetanin 
309*9b910f27SAndrew Jones     ncpus = cpu_count();
310*9b910f27SAndrew Jones     if (ncpus > MAX_CPUS)
311*9b910f27SAndrew Jones         report_abort("number cpus exceeds %d", MAX_CPUS);
312*9b910f27SAndrew Jones     printf("cpus = %d\n", ncpus);
313*9b910f27SAndrew Jones 
31469d4bf75SAndrey Smetanin     handle_irq(SINT1_VEC, stimer_isr);
31569d4bf75SAndrey Smetanin     handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
31669d4bf75SAndrey Smetanin 
317*9b910f27SAndrew Jones     on_cpus(stimer_test_prepare, (void *)read_cr3());
318*9b910f27SAndrew Jones     on_cpus(stimer_test, NULL);
319*9b910f27SAndrew Jones     on_cpus(stimer_test_cleanup, NULL);
32069d4bf75SAndrey Smetanin }
32169d4bf75SAndrey Smetanin 
32269d4bf75SAndrey Smetanin int main(int ac, char **av)
32369d4bf75SAndrey Smetanin {
32469d4bf75SAndrey Smetanin 
32569d4bf75SAndrey Smetanin     if (!synic_supported()) {
32669d4bf75SAndrey Smetanin         report("Hyper-V SynIC is not supported", true);
32769d4bf75SAndrey Smetanin         goto done;
32869d4bf75SAndrey Smetanin     }
32969d4bf75SAndrey Smetanin 
33069d4bf75SAndrey Smetanin     if (!stimer_supported()) {
33169d4bf75SAndrey Smetanin         report("Hyper-V SynIC timers are not supported", true);
33269d4bf75SAndrey Smetanin         goto done;
33369d4bf75SAndrey Smetanin     }
33469d4bf75SAndrey Smetanin 
33569d4bf75SAndrey Smetanin     if (!hv_time_ref_counter_supported()) {
33669d4bf75SAndrey Smetanin         report("Hyper-V time reference counter is not supported", true);
33769d4bf75SAndrey Smetanin         goto done;
33869d4bf75SAndrey Smetanin     }
33969d4bf75SAndrey Smetanin 
34069d4bf75SAndrey Smetanin     stimer_test_all();
34169d4bf75SAndrey Smetanin done:
34269d4bf75SAndrey Smetanin     return report_summary();
34369d4bf75SAndrey Smetanin }
344