Unable to update APIC_TPR when x2APIC is enabled and -global kvm-pit.lost_tick_policy=discard parameter provided

Host environment

  • Operating system: Gentoo Linux
  • OS/kernel version: 6.6.1-gentoo #1 SMP PREEMPT_DYNAMIC Tue Nov 14 13:32:33 CET 2023 x86_64
  • Architecture: x86
  • QEMU flavor: qemu-system-x86_64 & qemu-system-i386
  • QEMU version: QEMU emulator version 8.1.2
  • QEMU command line:
qemu-system-x86_64 \
    -machine type=pc-q35-7.0,kernel_irqchip=on,accel=kvm,mem-merge=off,vmport=off \
    -cpu host,-hypervisor,-amd-stibp,+topoext,invtsc=on,kvm=off,kvm-pv-eoi=on,kvm-hint-dedicated=on,hv_spinlocks=0x1fff,hv_relaxed,hv_vapic,hv_time,hv_crash,hv_reset,hv_vpindex,hv_runtime,hv_synic,hv_stimer,host-cache-info=on,l3-cache=off \
    -smp 4,sockets=1,cores=2,threads=2 \
    -enable-kvm \
    -m 10G \
    -mem-prealloc \
    -overcommit mem-lock=off \
    -rtc clock=host,base=localtime,driftfix=none \
    -global kvm-pit.lost_tick_policy=discard \
    -global ICH9-LPC.disable_s3=1 \
    -global ICH9-LPC.disable_s4=1 \
    -drive file=ovmf_code_amd64.fd,if=pflash,format=raw,unit=0,readonly=on \
    -drive file=ovmf_vars_amd64.fd,if=pflash,format=raw,unit=1 \
    (...)

Emulated/Virtualized environment

  • Operating system: OSDEV (custom OS)
  • OS/kernel version: Custom OS
  • Architecture: x86 (both i686 and amd64)

Description of problem

I am developing a custom OS and I wanted to implement x2APIC support. I was able to enable x2APIC, read and write some registers, like APIC_VER and APIC_SIVR. Everything looks good, except that I cannot update APIC_TPR register. Reading it always returns 0. The code I wrote works properly on bare metal. Below some observations:

Scenario 1:

  1. Enable x2APIC
  2. Write to CR8 - success
  3. Read from CR8 - gives correct value
  4. Read from APIC_TPR - gives correct value

Scenario 2:

  1. Enable x2APIC
  2. Read from APIC_TPR - gives 0
  3. Write to APIC_TPR
  4. Read from APIC_TPR - gives 0 again

Scenario 3:

  1. Initialize APIC (LAPIC or xAPIC)
  2. Write to APIC_TPR
  3. Read from APIC_TPR - gives correct value
  4. Switch to x2APIC
  5. Read from APIC_TPR - gives correct value stored in pt. 2
  6. Write to APIC_TPR
  7. Read from APIC_TPR - gives values stored in pt.2, not in point 6!

Looks like APIC_TPR is stuck at value stored there before switching to x2APIC and it cannot be updated with MSR. Only update CR8 works. I have checked parameters I passed to qemu. After removing -global kvm-pit.lost_tick_policy=discard problem is gone and APIC_TPR is updated correctly.

Additional information

Please let me know if you need additional information.

Edited by Rafal Kupiec