On Fri, May 20, 2022 at 10:36 AM Jue Wang <juew@xxxxxxxxxx> wrote: > > This patch adds the handling of APIC_LVTCMCI, conditioned on whether the > vCPU has set MCG_CMCI_P in MCG_CAP register. > > Signed-off-by: Jue Wang <juew@xxxxxxxxxx> > --- > arch/x86/kvm/lapic.c | 40 +++++++++++++++++++++++++++++++++------- > arch/x86/kvm/lapic.h | 3 ++- > 2 files changed, 35 insertions(+), 8 deletions(-) > > diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c > index db12d2ef1aef..e2186a7c0eed 100644 > --- a/arch/x86/kvm/lapic.c > +++ b/arch/x86/kvm/lapic.c > @@ -27,6 +27,7 @@ > #include <linux/math64.h> > #include <linux/slab.h> > #include <asm/processor.h> > +#include <asm/mce.h> > #include <asm/msr.h> > #include <asm/page.h> > #include <asm/current.h> > @@ -398,14 +399,26 @@ static inline int apic_lvt_nmi_mode(u32 lvt_val) > return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI; > } > > +static inline bool kvm_is_cmci_supported(struct kvm_vcpu *vcpu) > +{ > + return vcpu->arch.mcg_cap & MCG_CMCI_P; > +} > + > +static inline int kvm_apic_get_nr_lvt_entries(struct kvm_lapic *apic) > +{ > + return KVM_APIC_MAX_NR_LVT_ENTRIES - !kvm_is_cmci_supported(apic->vcpu); > +} Suggesting computing the number of LVT entries once as part of KVM_X86_SETUP_MCE and storing it in struct kvm_lapic (e.g. apic->nr_lvt_entries). I would also suggest replacing kvm_is_cmci_supported() with kvm_lapic_lvt_cmci_supported(), which checks if the local APIC supports the LVT CMCI register, rather than looking at mcg_cap. I think that will result in more readable code because it more directly checks that the local APIC supports the LVT entry we care about. > + > void kvm_apic_set_version(struct kvm_vcpu *vcpu) > { > struct kvm_lapic *apic = vcpu->arch.apic; > - u32 v = APIC_VERSION | ((KVM_APIC_MAX_NR_LVT_ENTRIES - 1) << 16); > + u32 v = 0; > > if (!lapic_in_kernel(vcpu)) > return; > > + v = APIC_VERSION | ((kvm_apic_get_nr_lvt_entries(apic) - 1) << 16); > + > /* > * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation) > * which doesn't have EOI register; Some buggy OSes (e.g. Windows with > @@ -425,7 +438,8 @@ static const unsigned int apic_lvt_mask[KVM_APIC_MAX_NR_LVT_ENTRIES] = { > [LVT_PERFORMANCE_COUNTER] = LVT_MASK | APIC_MODE_MASK, > [LVT_LINT0] = LINT_MASK, > [LVT_LINT1] = LINT_MASK, > - [LVT_ERROR] = LVT_MASK > + [LVT_ERROR] = LVT_MASK, > + [LVT_CMCI] = LVT_MASK | APIC_MODE_MASK > }; > > static int find_highest_vector(void *bitmap) > @@ -1445,6 +1459,9 @@ static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, > APIC_REG_MASK(APIC_TMCCT) | > APIC_REG_MASK(APIC_TDCR); > > + if (kvm_is_cmci_supported(apic->vcpu)) > + valid_reg_mask |= APIC_REG_MASK(APIC_LVTCMCI); > + > /* > * ARBPRI and ICR2 are not valid in x2APIC mode. WARN if KVM reads ICR > * in x2APIC mode as it's an 8-byte register in x2APIC and needs to be > @@ -2083,12 +2100,10 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) > apic_set_spiv(apic, val & mask); > if (!(val & APIC_SPIV_APIC_ENABLED)) { > int i; > - u32 lvt_val; > > - for (i = 0; i < KVM_APIC_MAX_NR_LVT_ENTRIES; i++) { > - lvt_val = kvm_lapic_get_reg(apic, APIC_LVTx(i)); > + for (i = 0; i < kvm_apic_get_nr_lvt_entries(apic); i++) { > kvm_lapic_set_reg(apic, APIC_LVTx(i), > - lvt_val | APIC_LVT_MASKED); > + kvm_lapic_get_reg(apic, APIC_LVTx(i)) | APIC_LVT_MASKED); > } > apic_update_lvtt(apic); > atomic_set(&apic->lapic_timer.pending, 0); > @@ -2140,6 +2155,17 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) > apic_update_lvtt(apic); > break; > > + case APIC_LVTCMCI: > + if (!kvm_is_cmci_supported(apic->vcpu)) { > + ret = 1; > + break; > + } > + if (!kvm_apic_sw_enabled(apic)) > + val |= APIC_LVT_MASKED; > + val &= apic_lvt_mask[LVT_CMCI]; > + kvm_lapic_set_reg(apic, APIC_LVTCMCI, val); This should be folded into the handling of the other LVT registers. The code is basically the same. Then you can also drop the kvm_is_cmci_supported() and replace it with a more generic check that checks if the LVT entry is supported. > + break; > + > case APIC_TMICT: > if (apic_lvtt_tscdeadline(apic)) > break; > @@ -2383,7 +2409,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event) > kvm_apic_set_xapic_id(apic, vcpu->vcpu_id); > kvm_apic_set_version(apic->vcpu); > > - for (i = 0; i < KVM_APIC_MAX_NR_LVT_ENTRIES; i++) > + for (i = 0; i < kvm_apic_get_nr_lvt_entries(apic); i++) > kvm_lapic_set_reg(apic, APIC_LVTx(i), APIC_LVT_MASKED); > apic_update_lvtt(apic); > if (kvm_vcpu_is_reset_bsp(vcpu) && > diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h > index 2d197ed0b8ce..16298bcb2abf 100644 > --- a/arch/x86/kvm/lapic.h > +++ b/arch/x86/kvm/lapic.h > @@ -35,11 +35,12 @@ enum lapic_lvt_entry { > LVT_LINT0, > LVT_LINT1, > LVT_ERROR, > + LVT_CMCI, > > KVM_APIC_MAX_NR_LVT_ENTRIES, > }; > > -#define APIC_LVTx(x) (APIC_LVTT + 0x10 * (x)) > +#define APIC_LVTx(x) ((x) == LVT_CMCI ? APIC_LVTCMCI : APIC_LVTT + 0x10 * (x)) > > struct kvm_timer { > struct hrtimer timer; > -- > 2.36.1.124.g0e6072fb45-goog >