Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 276 lines 5.8 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2#include <fcntl.h> 3#include <stdatomic.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <sys/ioctl.h> 8#include <unistd.h> 9 10#include "apic.h" 11#include "kvm_util.h" 12#include "processor.h" 13#include "test_util.h" 14 15static bool is_x2apic; 16 17#define IRQ_VECTOR 0x20 18 19/* See also the comment at similar assertion in memslot_perf_test.c */ 20static_assert(ATOMIC_INT_LOCK_FREE == 2, "atomic int is not lockless"); 21 22static atomic_uint tpr_guest_irq_sync_val; 23 24static void tpr_guest_irq_sync_flag_reset(void) 25{ 26 atomic_store_explicit(&tpr_guest_irq_sync_val, 0, 27 memory_order_release); 28} 29 30static unsigned int tpr_guest_irq_sync_val_get(void) 31{ 32 return atomic_load_explicit(&tpr_guest_irq_sync_val, 33 memory_order_acquire); 34} 35 36static void tpr_guest_irq_sync_val_inc(void) 37{ 38 atomic_fetch_add_explicit(&tpr_guest_irq_sync_val, 1, 39 memory_order_acq_rel); 40} 41 42static void tpr_guest_irq_handler_xapic(struct ex_regs *regs) 43{ 44 tpr_guest_irq_sync_val_inc(); 45 46 xapic_write_reg(APIC_EOI, 0); 47} 48 49static void tpr_guest_irq_handler_x2apic(struct ex_regs *regs) 50{ 51 tpr_guest_irq_sync_val_inc(); 52 53 x2apic_write_reg(APIC_EOI, 0); 54} 55 56static void tpr_guest_irq_queue(void) 57{ 58 if (is_x2apic) { 59 x2apic_write_reg(APIC_SELF_IPI, IRQ_VECTOR); 60 } else { 61 u32 icr, icr2; 62 63 icr = APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 64 IRQ_VECTOR; 65 icr2 = 0; 66 67 xapic_write_reg(APIC_ICR2, icr2); 68 xapic_write_reg(APIC_ICR, icr); 69 } 70} 71 72static u8 tpr_guest_tpr_get(void) 73{ 74 u32 taskpri; 75 76 if (is_x2apic) 77 taskpri = x2apic_read_reg(APIC_TASKPRI); 78 else 79 taskpri = xapic_read_reg(APIC_TASKPRI); 80 81 return GET_APIC_PRI(taskpri); 82} 83 84static u8 tpr_guest_ppr_get(void) 85{ 86 u32 procpri; 87 88 if (is_x2apic) 89 procpri = x2apic_read_reg(APIC_PROCPRI); 90 else 91 procpri = xapic_read_reg(APIC_PROCPRI); 92 93 return GET_APIC_PRI(procpri); 94} 95 96static u8 tpr_guest_cr8_get(void) 97{ 98 u64 cr8; 99 100 asm volatile ("mov %%cr8, %[cr8]\n\t" : [cr8] "=r"(cr8)); 101 102 return cr8 & GENMASK(3, 0); 103} 104 105static void tpr_guest_check_tpr_ppr_cr8_equal(void) 106{ 107 u8 tpr; 108 109 tpr = tpr_guest_tpr_get(); 110 111 GUEST_ASSERT_EQ(tpr_guest_ppr_get(), tpr); 112 GUEST_ASSERT_EQ(tpr_guest_cr8_get(), tpr); 113} 114 115static void tpr_guest_code(void) 116{ 117 cli(); 118 119 if (is_x2apic) 120 x2apic_enable(); 121 else 122 xapic_enable(); 123 124 GUEST_ASSERT_EQ(tpr_guest_tpr_get(), 0); 125 tpr_guest_check_tpr_ppr_cr8_equal(); 126 127 tpr_guest_irq_queue(); 128 129 /* TPR = 0 but IRQ masked by IF=0, should not fire */ 130 udelay(1000); 131 GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 0); 132 133 sti(); 134 135 /* IF=1 now, IRQ should fire */ 136 while (tpr_guest_irq_sync_val_get() == 0) 137 cpu_relax(); 138 GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1); 139 140 GUEST_SYNC(true); 141 tpr_guest_check_tpr_ppr_cr8_equal(); 142 143 tpr_guest_irq_queue(); 144 145 /* IRQ masked by barely high enough TPR now, should not fire */ 146 udelay(1000); 147 GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1); 148 149 GUEST_SYNC(false); 150 tpr_guest_check_tpr_ppr_cr8_equal(); 151 152 /* TPR barely low enough now to unmask IRQ, should fire */ 153 while (tpr_guest_irq_sync_val_get() == 1) 154 cpu_relax(); 155 GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 2); 156 157 GUEST_DONE(); 158} 159 160static u8 lapic_tpr_get(struct kvm_lapic_state *xapic) 161{ 162 return GET_APIC_PRI(*((u32 *)&xapic->regs[APIC_TASKPRI])); 163} 164 165static void lapic_tpr_set(struct kvm_lapic_state *xapic, u8 val) 166{ 167 u32 *taskpri = (u32 *)&xapic->regs[APIC_TASKPRI]; 168 169 *taskpri = SET_APIC_PRI(*taskpri, val); 170} 171 172static u8 sregs_tpr(struct kvm_sregs *sregs) 173{ 174 return sregs->cr8 & GENMASK(3, 0); 175} 176 177static void test_tpr_check_tpr_zero(struct kvm_vcpu *vcpu) 178{ 179 struct kvm_lapic_state xapic; 180 181 vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); 182 183 TEST_ASSERT_EQ(lapic_tpr_get(&xapic), 0); 184} 185 186static void test_tpr_check_tpr_cr8_equal(struct kvm_vcpu *vcpu) 187{ 188 struct kvm_sregs sregs; 189 struct kvm_lapic_state xapic; 190 191 vcpu_sregs_get(vcpu, &sregs); 192 vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); 193 194 TEST_ASSERT_EQ(sregs_tpr(&sregs), lapic_tpr_get(&xapic)); 195} 196 197static void test_tpr_set_tpr_for_irq(struct kvm_vcpu *vcpu, bool mask) 198{ 199 struct kvm_lapic_state xapic; 200 u8 tpr; 201 202 static_assert(IRQ_VECTOR >= 16, "invalid IRQ vector number"); 203 tpr = IRQ_VECTOR / 16; 204 if (!mask) 205 tpr--; 206 207 vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); 208 lapic_tpr_set(&xapic, tpr); 209 vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic); 210} 211 212static void test_tpr(bool __is_x2apic) 213{ 214 struct kvm_vcpu *vcpu; 215 struct kvm_vm *vm; 216 bool done = false; 217 218 is_x2apic = __is_x2apic; 219 220 vm = vm_create_with_one_vcpu(&vcpu, tpr_guest_code); 221 if (is_x2apic) { 222 vm_install_exception_handler(vm, IRQ_VECTOR, 223 tpr_guest_irq_handler_x2apic); 224 } else { 225 vm_install_exception_handler(vm, IRQ_VECTOR, 226 tpr_guest_irq_handler_xapic); 227 vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_X2APIC); 228 virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); 229 } 230 231 sync_global_to_guest(vcpu->vm, is_x2apic); 232 233 /* According to the SDM/APM the TPR value at reset is 0 */ 234 test_tpr_check_tpr_zero(vcpu); 235 test_tpr_check_tpr_cr8_equal(vcpu); 236 237 tpr_guest_irq_sync_flag_reset(); 238 sync_global_to_guest(vcpu->vm, tpr_guest_irq_sync_val); 239 240 while (!done) { 241 struct ucall uc; 242 243 alarm(2); 244 vcpu_run(vcpu); 245 alarm(0); 246 247 switch (get_ucall(vcpu, &uc)) { 248 case UCALL_ABORT: 249 REPORT_GUEST_ASSERT(uc); 250 break; 251 case UCALL_DONE: 252 test_tpr_check_tpr_cr8_equal(vcpu); 253 done = true; 254 break; 255 case UCALL_SYNC: 256 test_tpr_check_tpr_cr8_equal(vcpu); 257 test_tpr_set_tpr_for_irq(vcpu, uc.args[1]); 258 break; 259 default: 260 TEST_FAIL("Unknown ucall result 0x%lx", uc.cmd); 261 break; 262 } 263 } 264 kvm_vm_free(vm); 265} 266 267int main(int argc, char *argv[]) 268{ 269 /* 270 * Use separate VMs for the xAPIC and x2APIC tests so that x2APIC can 271 * be fully hidden from the guest. KVM disallows changing CPUID after 272 * KVM_RUN and AVIC is disabled if _any_ vCPU is allowed to use x2APIC. 273 */ 274 test_tpr(false); 275 test_tpr(true); 276}