123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- #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 "asm/barrier.h"
- #define MAX_CPUS 4
- #define SINT1_VEC 0xF1
- #define SINT2_VEC 0xF2
- #define SINT1_NUM 2
- #define SINT2_NUM 3
- #define ONE_MS_IN_100NS 10000
- static struct spinlock g_synic_alloc_lock;
- struct stimer {
- int sint;
- int index;
- atomic_t fire_count;
- };
- struct svcpu {
- int vcpu;
- void *msg_page;
- void *evt_page;
- struct stimer timer[HV_SYNIC_STIMER_COUNT];
- };
- static struct svcpu g_synic_vcpu[MAX_CPUS];
- static void *synic_alloc_page(void)
- {
- void *page;
- spin_lock(&g_synic_alloc_lock);
- page = alloc_page();
- spin_unlock(&g_synic_alloc_lock);
- return page;
- }
- static void synic_free_page(void *page)
- {
- spin_lock(&g_synic_alloc_lock);
- free_page(page);
- spin_unlock(&g_synic_alloc_lock);
- }
- static void stimer_init(struct stimer *timer, int index)
- {
- memset(timer, 0, sizeof(*timer));
- timer->index = index;
- }
- static void synic_enable(void)
- {
- int vcpu = smp_id(), i;
- struct svcpu *svcpu = &g_synic_vcpu[vcpu];
- memset(svcpu, 0, sizeof(*svcpu));
- svcpu->vcpu = vcpu;
- svcpu->msg_page = synic_alloc_page();
- for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
- stimer_init(&svcpu->timer[i], i);
- }
- wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(svcpu->msg_page) |
- HV_SYNIC_SIMP_ENABLE);
- wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
- }
- static void stimer_shutdown(struct stimer *timer)
- {
- wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
- }
- static void process_stimer_expired(struct svcpu *svcpu, struct stimer *timer,
- u64 expiration_time, u64 delivery_time)
- {
- atomic_inc(&timer->fire_count);
- }
- static void process_stimer_msg(struct svcpu *svcpu,
- struct hv_message *msg, int sint)
- {
- struct hv_timer_message_payload *payload =
- (struct hv_timer_message_payload *)msg->u.payload;
- struct stimer *timer;
- if (msg->header.message_type != HVMSG_TIMER_EXPIRED &&
- msg->header.message_type != HVMSG_NONE) {
- report("invalid Hyper-V SynIC msg type", false);
- report_summary();
- abort();
- }
- if (msg->header.message_type == HVMSG_NONE) {
- return;
- }
- if (msg->header.payload_size < sizeof(*payload)) {
- report("invalid Hyper-V SynIC msg payload size", false);
- report_summary();
- abort();
- }
- /* Now process timer expiration message */
- if (payload->timer_index >= ARRAY_SIZE(svcpu->timer)) {
- report("invalid Hyper-V SynIC timer index", false);
- report_summary();
- abort();
- }
- timer = &svcpu->timer[payload->timer_index];
- process_stimer_expired(svcpu, timer, payload->expiration_time,
- payload->delivery_time);
- msg->header.message_type = HVMSG_NONE;
- mb();
- if (msg->header.message_flags.msg_pending) {
- wrmsr(HV_X64_MSR_EOM, 0);
- }
- }
- static void __stimer_isr(int vcpu)
- {
- struct svcpu *svcpu = &g_synic_vcpu[vcpu];
- struct hv_message_page *msg_page;
- struct hv_message *msg;
- int i;
- msg_page = (struct hv_message_page *)svcpu->msg_page;
- for (i = 0; i < ARRAY_SIZE(msg_page->sint_message); i++) {
- msg = &msg_page->sint_message[i];
- process_stimer_msg(svcpu, msg, i);
- }
- }
- static void stimer_isr(isr_regs_t *regs)
- {
- int vcpu = smp_id();
- __stimer_isr(vcpu);
- eoi();
- }
- static void stimer_isr_auto_eoi(isr_regs_t *regs)
- {
- int vcpu = smp_id();
- __stimer_isr(vcpu);
- }
- static void stimer_start(struct stimer *timer,
- bool auto_enable, bool periodic,
- u64 tick_100ns, int sint)
- {
- u64 config, count;
- timer->sint = sint;
- atomic_set(&timer->fire_count, 0);
- config = 0;
- if (periodic) {
- config |= HV_STIMER_PERIODIC;
- }
- config |= ((u8)(sint & 0xFF)) << 16;
- config |= HV_STIMER_ENABLE;
- if (auto_enable) {
- config |= HV_STIMER_AUTOENABLE;
- }
- if (periodic) {
- count = tick_100ns;
- } else {
- count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns;
- }
- if (!auto_enable) {
- wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
- wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
- } else {
- wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
- wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
- }
- }
- static void stimers_shutdown(void)
- {
- int vcpu = smp_id(), i;
- struct svcpu *svcpu = &g_synic_vcpu[vcpu];
- for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
- stimer_shutdown(&svcpu->timer[i]);
- }
- }
- static void synic_disable(void)
- {
- int vcpu = smp_id();
- struct svcpu *svcpu = &g_synic_vcpu[vcpu];
- wrmsr(HV_X64_MSR_SCONTROL, 0);
- wrmsr(HV_X64_MSR_SIMP, 0);
- wrmsr(HV_X64_MSR_SIEFP, 0);
- synic_free_page(svcpu->msg_page);
- }
- static void stimer_test_prepare(void *ctx)
- {
- write_cr3((ulong)ctx);
- synic_enable();
- synic_sint_create(SINT1_NUM, SINT1_VEC, false);
- synic_sint_create(SINT2_NUM, SINT2_VEC, true);
- }
- static void stimer_test_periodic(int vcpu, struct stimer *timer1,
- struct stimer *timer2)
- {
- /* Check periodic timers */
- stimer_start(timer1, false, true, ONE_MS_IN_100NS, SINT1_NUM);
- stimer_start(timer2, false, true, ONE_MS_IN_100NS, SINT2_NUM);
- while ((atomic_read(&timer1->fire_count) < 1000) ||
- (atomic_read(&timer2->fire_count) < 1000)) {
- pause();
- }
- report("Hyper-V SynIC periodic timers test vcpu %d", true, vcpu);
- stimer_shutdown(timer1);
- stimer_shutdown(timer2);
- }
- static void stimer_test_one_shot(int vcpu, struct stimer *timer)
- {
- /* Check one-shot timer */
- stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
- while (atomic_read(&timer->fire_count) < 1) {
- pause();
- }
- report("Hyper-V SynIC one-shot test vcpu %d", true, vcpu);
- stimer_shutdown(timer);
- }
- static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer)
- {
- /* Check auto-enable one-shot timer */
- stimer_start(timer, true, false, ONE_MS_IN_100NS, SINT1_NUM);
- while (atomic_read(&timer->fire_count) < 1) {
- pause();
- }
- report("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", true, vcpu);
- stimer_shutdown(timer);
- }
- static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
- {
- /* Check auto-enable periodic timer */
- stimer_start(timer, true, true, ONE_MS_IN_100NS, SINT1_NUM);
- while (atomic_read(&timer->fire_count) < 1000) {
- pause();
- }
- report("Hyper-V SynIC auto-enable periodic timer test vcpu %d", true, vcpu);
- stimer_shutdown(timer);
- }
- static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer)
- {
- struct hv_message_page *msg_page = g_synic_vcpu[vcpu].msg_page;
- struct hv_message *msg = &msg_page->sint_message[timer->sint];
- msg->header.message_type = HVMSG_TIMER_EXPIRED;
- wmb();
- stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
- do
- rmb();
- while (!msg->header.message_flags.msg_pending);
- report("no timer fired while msg slot busy: vcpu %d",
- !atomic_read(&timer->fire_count), vcpu);
- msg->header.message_type = HVMSG_NONE;
- wmb();
- wrmsr(HV_X64_MSR_EOM, 0);
- while (atomic_read(&timer->fire_count) < 1) {
- pause();
- }
- report("timer resumed when msg slot released: vcpu %d", true, vcpu);
- stimer_shutdown(timer);
- }
- static void stimer_test(void *ctx)
- {
- int vcpu = smp_id();
- struct svcpu *svcpu = &g_synic_vcpu[vcpu];
- struct stimer *timer1, *timer2;
- irq_enable();
- timer1 = &svcpu->timer[0];
- timer2 = &svcpu->timer[1];
- stimer_test_periodic(vcpu, timer1, timer2);
- stimer_test_one_shot(vcpu, timer1);
- stimer_test_auto_enable_one_shot(vcpu, timer2);
- stimer_test_auto_enable_periodic(vcpu, timer1);
- stimer_test_one_shot_busy(vcpu, timer1);
- irq_disable();
- }
- static void stimer_test_cleanup(void *ctx)
- {
- stimers_shutdown();
- synic_sint_destroy(SINT1_NUM);
- synic_sint_destroy(SINT2_NUM);
- synic_disable();
- }
- static void stimer_test_all(void)
- {
- int ncpus;
- setup_vm();
- smp_init();
- enable_apic();
- ncpus = cpu_count();
- if (ncpus > MAX_CPUS)
- report_abort("number cpus exceeds %d", MAX_CPUS);
- printf("cpus = %d\n", ncpus);
- handle_irq(SINT1_VEC, stimer_isr);
- handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
- on_cpus(stimer_test_prepare, (void *)read_cr3());
- on_cpus(stimer_test, NULL);
- on_cpus(stimer_test_cleanup, NULL);
- }
- int main(int ac, char **av)
- {
- if (!synic_supported()) {
- report("Hyper-V SynIC is not supported", true);
- goto done;
- }
- if (!stimer_supported()) {
- report("Hyper-V SynIC timers are not supported", true);
- goto done;
- }
- if (!hv_time_ref_counter_supported()) {
- report("Hyper-V time reference counter is not supported", true);
- goto done;
- }
- stimer_test_all();
- done:
- return report_summary();
- }
|