Expand the APIC register mask infrastructure from a single u64 to a u64[2] array to accommodate both standard APIC registers (0x0-0x3f0) and extended APIC registers (0x400-0x530). Current AMD processors support four extended LVT registers; future processors may support up to 255. The existing single 64-bit mask can track at most 64 registers (with 16- byte granularity), which is insufficient for the extended APIC register space. A 128-bit mask (u64[2]) provides coverage for both standard and extended register ranges. Convert the bitmask infrastructure to use kernel bitmap APIs. APIC_REG_MASK now sets bits in the u64[2] array via bitmap_set(), APIC_REG_TEST checks register validity via test_bit(), and kvm_lapic_readable_reg_mask() populates the expanded mask as an output parameter instead of a return value. Update all callers, including vmx_update_msr_bitmap_x2apic() in VMX, to pass and utilize the new two-element mask array. Currently, extended APIC registers are only supported for AMD processors. No functional change intended; extended APIC register emulation will be added in a subsequent patch. Suggested-by: Naveen N Rao (AMD) Suggested-by: Dapeng Mi Signed-off-by: Manali Shukla --- arch/x86/kvm/lapic.c | 96 ++++++++++++++++++++++++++---------------- arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/vmx/vmx.c | 10 +++-- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 2e513f1c8988..66819397e073 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1664,53 +1664,74 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev) return container_of(dev, struct kvm_lapic, dev); } -#define APIC_REG_MASK(reg) (1ull << ((reg) >> 4)) -#define APIC_REGS_MASK(first, count) \ - (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) - -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) -{ - /* Leave bits '0' for reserved and write-only registers. */ - u64 valid_reg_mask = - APIC_REG_MASK(APIC_ID) | - APIC_REG_MASK(APIC_LVR) | - APIC_REG_MASK(APIC_TASKPRI) | - APIC_REG_MASK(APIC_PROCPRI) | - APIC_REG_MASK(APIC_LDR) | - APIC_REG_MASK(APIC_SPIV) | - APIC_REGS_MASK(APIC_ISR, APIC_ISR_NR) | - APIC_REGS_MASK(APIC_TMR, APIC_ISR_NR) | - APIC_REGS_MASK(APIC_IRR, APIC_ISR_NR) | - APIC_REG_MASK(APIC_ESR) | - APIC_REG_MASK(APIC_ICR) | - APIC_REG_MASK(APIC_LVTT) | - APIC_REG_MASK(APIC_LVTTHMR) | - APIC_REG_MASK(APIC_LVTPC) | - APIC_REG_MASK(APIC_LVT0) | - APIC_REG_MASK(APIC_LVT1) | - APIC_REG_MASK(APIC_LVTERR) | - APIC_REG_MASK(APIC_TMICT) | - APIC_REG_MASK(APIC_TMCCT) | - APIC_REG_MASK(APIC_TDCR); +/* + * Helper macros for APIC register bitmask handling + * 2 element array is being used to represent 128-bit mask, where: + * - mask[0] tracks standard APIC registers + * - mask[1] tracks extended APIC registers + */ + +#define APIC_REG_BITMAP_WORDS 2 /* 2 x 64-bit words = 128 bits */ +#define APIC_REG_BITMAP_BITS (APIC_REG_BITMAP_WORDS * BITS_PER_LONG) + +#define APIC_REG_TO_BIT(reg) (((reg) < 0x400) ? ((reg) >> 4) : \ + (64 + (((reg) - 0x400) >> 4))) + +#define APIC_REG_MASK(reg, mask) \ + bitmap_set((unsigned long *)(mask), APIC_REG_TO_BIT(reg), 1) + +#define APIC_REGS_MASK(first, count, mask) \ + bitmap_set((unsigned long *)(mask), APIC_REG_TO_BIT(first), (count)) + +#define APIC_REG_TEST(reg, mask) \ + test_bit(APIC_REG_TO_BIT(reg), (unsigned long *)(mask)) + +#define APIC_LAST_REG_OFFSET 0x3f0 + +void kvm_lapic_readable_reg_mask(struct kvm_lapic *apic, u64 mask[2]) +{ + bitmap_zero((unsigned long *)(mask), APIC_REG_BITMAP_BITS); + + APIC_REG_MASK(APIC_ID, mask); + APIC_REG_MASK(APIC_LVR, mask); + APIC_REG_MASK(APIC_TASKPRI, mask); + APIC_REG_MASK(APIC_PROCPRI, mask); + APIC_REG_MASK(APIC_LDR, mask); + APIC_REG_MASK(APIC_SPIV, mask); + APIC_REGS_MASK(APIC_ISR, APIC_ISR_NR, mask); + APIC_REGS_MASK(APIC_TMR, APIC_ISR_NR, mask); + APIC_REGS_MASK(APIC_IRR, APIC_ISR_NR, mask); + APIC_REG_MASK(APIC_ESR, mask); + APIC_REG_MASK(APIC_ICR, mask); + APIC_REG_MASK(APIC_LVTT, mask); + APIC_REG_MASK(APIC_LVTTHMR, mask); + APIC_REG_MASK(APIC_LVTPC, mask); + APIC_REG_MASK(APIC_LVT0, mask); + APIC_REG_MASK(APIC_LVT1, mask); + APIC_REG_MASK(APIC_LVTERR, mask); + APIC_REG_MASK(APIC_TMICT, mask); + APIC_REG_MASK(APIC_TMCCT, mask); + APIC_REG_MASK(APIC_TDCR, mask); if (kvm_lapic_lvt_supported(apic, LVT_CMCI)) - valid_reg_mask |= APIC_REG_MASK(APIC_LVTCMCI); + APIC_REG_MASK(APIC_LVTCMCI, mask); /* ARBPRI, DFR, and ICR2 are not valid in x2APIC mode. */ - if (!apic_x2apic_mode(apic)) - valid_reg_mask |= APIC_REG_MASK(APIC_ARBPRI) | - APIC_REG_MASK(APIC_DFR) | - APIC_REG_MASK(APIC_ICR2); - - return valid_reg_mask; + if (!apic_x2apic_mode(apic)) { + APIC_REG_MASK(APIC_ARBPRI, mask); + APIC_REG_MASK(APIC_DFR, mask); + APIC_REG_MASK(APIC_ICR2, mask); + } } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_lapic_readable_reg_mask); static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, void *data) { + unsigned int last_reg = APIC_LAST_REG_OFFSET; unsigned char alignment = offset & 0xf; u32 result; + u64 mask[2]; /* * WARN if KVM reads ICR in x2APIC mode, as it's an 8-byte register in @@ -1721,8 +1742,9 @@ static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, if (alignment + len > 4) return 1; - if (offset > 0x3f0 || - !(kvm_lapic_readable_reg_mask(apic) & APIC_REG_MASK(offset))) + kvm_lapic_readable_reg_mask(apic, mask); + + if (offset > last_reg || !APIC_REG_TEST(offset, mask)) return 1; result = __apic_read(apic, offset & ~0xf); diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index adf04a9bd57d..152f17903ff0 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -156,7 +156,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len); void kvm_lapic_exit(void); -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); +void kvm_lapic_readable_reg_mask(struct kvm_lapic *apic, u64 mask[2]); static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 9e6c78a22b10..35440753e5c6 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4231,10 +4231,14 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) * through reads for all valid registers by default in x2APIC+APICv * mode, only the current timer count needs on-demand emulation by KVM. */ - if (mode & MSR_BITMAP_MODE_X2APIC_APICV) - msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask(vcpu->arch.apic); - else + if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { + u64 mask[2]; + + kvm_lapic_readable_reg_mask(vcpu->arch.apic, mask); + msr_bitmap[read_idx] = ~mask[0]; + } else { msr_bitmap[read_idx] = ~0ull; + } msr_bitmap[write_idx] = ~0ull; /* -- 2.43.0