| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- // SPDX-License-Identifier: GPL-2.0-only
- #include <fcntl.h>
- #include <stdatomic.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <unistd.h>
- #include "apic.h"
- #include "kvm_util.h"
- #include "processor.h"
- #include "test_util.h"
- static bool is_x2apic;
- #define IRQ_VECTOR 0x20
- /* See also the comment at similar assertion in memslot_perf_test.c */
- static_assert(ATOMIC_INT_LOCK_FREE == 2, "atomic int is not lockless");
- static atomic_uint tpr_guest_irq_sync_val;
- static void tpr_guest_irq_sync_flag_reset(void)
- {
- atomic_store_explicit(&tpr_guest_irq_sync_val, 0,
- memory_order_release);
- }
- static unsigned int tpr_guest_irq_sync_val_get(void)
- {
- return atomic_load_explicit(&tpr_guest_irq_sync_val,
- memory_order_acquire);
- }
- static void tpr_guest_irq_sync_val_inc(void)
- {
- atomic_fetch_add_explicit(&tpr_guest_irq_sync_val, 1,
- memory_order_acq_rel);
- }
- static void tpr_guest_irq_handler_xapic(struct ex_regs *regs)
- {
- tpr_guest_irq_sync_val_inc();
- xapic_write_reg(APIC_EOI, 0);
- }
- static void tpr_guest_irq_handler_x2apic(struct ex_regs *regs)
- {
- tpr_guest_irq_sync_val_inc();
- x2apic_write_reg(APIC_EOI, 0);
- }
- static void tpr_guest_irq_queue(void)
- {
- if (is_x2apic) {
- x2apic_write_reg(APIC_SELF_IPI, IRQ_VECTOR);
- } else {
- uint32_t icr, icr2;
- icr = APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
- IRQ_VECTOR;
- icr2 = 0;
- xapic_write_reg(APIC_ICR2, icr2);
- xapic_write_reg(APIC_ICR, icr);
- }
- }
- static uint8_t tpr_guest_tpr_get(void)
- {
- uint32_t taskpri;
- if (is_x2apic)
- taskpri = x2apic_read_reg(APIC_TASKPRI);
- else
- taskpri = xapic_read_reg(APIC_TASKPRI);
- return GET_APIC_PRI(taskpri);
- }
- static uint8_t tpr_guest_ppr_get(void)
- {
- uint32_t procpri;
- if (is_x2apic)
- procpri = x2apic_read_reg(APIC_PROCPRI);
- else
- procpri = xapic_read_reg(APIC_PROCPRI);
- return GET_APIC_PRI(procpri);
- }
- static uint8_t tpr_guest_cr8_get(void)
- {
- uint64_t cr8;
- asm volatile ("mov %%cr8, %[cr8]\n\t" : [cr8] "=r"(cr8));
- return cr8 & GENMASK(3, 0);
- }
- static void tpr_guest_check_tpr_ppr_cr8_equal(void)
- {
- uint8_t tpr;
- tpr = tpr_guest_tpr_get();
- GUEST_ASSERT_EQ(tpr_guest_ppr_get(), tpr);
- GUEST_ASSERT_EQ(tpr_guest_cr8_get(), tpr);
- }
- static void tpr_guest_code(void)
- {
- cli();
- if (is_x2apic)
- x2apic_enable();
- else
- xapic_enable();
- GUEST_ASSERT_EQ(tpr_guest_tpr_get(), 0);
- tpr_guest_check_tpr_ppr_cr8_equal();
- tpr_guest_irq_queue();
- /* TPR = 0 but IRQ masked by IF=0, should not fire */
- udelay(1000);
- GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 0);
- sti();
- /* IF=1 now, IRQ should fire */
- while (tpr_guest_irq_sync_val_get() == 0)
- cpu_relax();
- GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
- GUEST_SYNC(true);
- tpr_guest_check_tpr_ppr_cr8_equal();
- tpr_guest_irq_queue();
- /* IRQ masked by barely high enough TPR now, should not fire */
- udelay(1000);
- GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
- GUEST_SYNC(false);
- tpr_guest_check_tpr_ppr_cr8_equal();
- /* TPR barely low enough now to unmask IRQ, should fire */
- while (tpr_guest_irq_sync_val_get() == 1)
- cpu_relax();
- GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 2);
- GUEST_DONE();
- }
- static uint8_t lapic_tpr_get(struct kvm_lapic_state *xapic)
- {
- return GET_APIC_PRI(*((u32 *)&xapic->regs[APIC_TASKPRI]));
- }
- static void lapic_tpr_set(struct kvm_lapic_state *xapic, uint8_t val)
- {
- u32 *taskpri = (u32 *)&xapic->regs[APIC_TASKPRI];
- *taskpri = SET_APIC_PRI(*taskpri, val);
- }
- static uint8_t sregs_tpr(struct kvm_sregs *sregs)
- {
- return sregs->cr8 & GENMASK(3, 0);
- }
- static void test_tpr_check_tpr_zero(struct kvm_vcpu *vcpu)
- {
- struct kvm_lapic_state xapic;
- vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
- TEST_ASSERT_EQ(lapic_tpr_get(&xapic), 0);
- }
- static void test_tpr_check_tpr_cr8_equal(struct kvm_vcpu *vcpu)
- {
- struct kvm_sregs sregs;
- struct kvm_lapic_state xapic;
- vcpu_sregs_get(vcpu, &sregs);
- vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
- TEST_ASSERT_EQ(sregs_tpr(&sregs), lapic_tpr_get(&xapic));
- }
- static void test_tpr_set_tpr_for_irq(struct kvm_vcpu *vcpu, bool mask)
- {
- struct kvm_lapic_state xapic;
- uint8_t tpr;
- static_assert(IRQ_VECTOR >= 16, "invalid IRQ vector number");
- tpr = IRQ_VECTOR / 16;
- if (!mask)
- tpr--;
- vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
- lapic_tpr_set(&xapic, tpr);
- vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic);
- }
- static void test_tpr(bool __is_x2apic)
- {
- struct kvm_vcpu *vcpu;
- struct kvm_vm *vm;
- bool done = false;
- is_x2apic = __is_x2apic;
- vm = vm_create_with_one_vcpu(&vcpu, tpr_guest_code);
- if (is_x2apic) {
- vm_install_exception_handler(vm, IRQ_VECTOR,
- tpr_guest_irq_handler_x2apic);
- } else {
- vm_install_exception_handler(vm, IRQ_VECTOR,
- tpr_guest_irq_handler_xapic);
- vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_X2APIC);
- virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
- }
- sync_global_to_guest(vcpu->vm, is_x2apic);
- /* According to the SDM/APM the TPR value at reset is 0 */
- test_tpr_check_tpr_zero(vcpu);
- test_tpr_check_tpr_cr8_equal(vcpu);
- tpr_guest_irq_sync_flag_reset();
- sync_global_to_guest(vcpu->vm, tpr_guest_irq_sync_val);
- while (!done) {
- struct ucall uc;
- alarm(2);
- vcpu_run(vcpu);
- alarm(0);
- switch (get_ucall(vcpu, &uc)) {
- case UCALL_ABORT:
- REPORT_GUEST_ASSERT(uc);
- break;
- case UCALL_DONE:
- test_tpr_check_tpr_cr8_equal(vcpu);
- done = true;
- break;
- case UCALL_SYNC:
- test_tpr_check_tpr_cr8_equal(vcpu);
- test_tpr_set_tpr_for_irq(vcpu, uc.args[1]);
- break;
- default:
- TEST_FAIL("Unknown ucall result 0x%lx", uc.cmd);
- break;
- }
- }
- kvm_vm_free(vm);
- }
- int main(int argc, char *argv[])
- {
- /*
- * Use separate VMs for the xAPIC and x2APIC tests so that x2APIC can
- * be fully hidden from the guest. KVM disallows changing CPUID after
- * KVM_RUN and AVIC is disabled if _any_ vCPU is allowed to use x2APIC.
- */
- test_tpr(false);
- test_tpr(true);
- }
|