KVM: x86: Use explicit case-statements for MCx banks in {g,s}et_msr_mce()
authorSean Christopherson <seanjc@google.com>
Thu, 12 May 2022 22:27:15 +0000 (22:27 +0000)
committerSean Christopherson <seanjc@google.com>
Fri, 8 Jul 2022 21:57:12 +0000 (14:57 -0700)
Use an explicit case statement to grab the full range of MCx bank MSRs
in {g,s}et_msr_mce(), and manually check only the "end" (the number of
banks configured by userspace may be less than the max).  The "default"
trick works, but is a bit odd now, and will be quite odd if/when support
for accessing MCx_CTL2 MSRs is added, which has near identical logic.

Hoist "offset" to function scope so as to avoid curly braces for the case
statement, and because MCx_CTL2 support will need the same variables.

Opportunstically clean up the comment about allowing bit 10 to be cleared
from bank 4.

No functional change intended.

Cc: Jue Wang <juew@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Link: https://lore.kernel.org/r/20220512222716.4112548-3-seanjc@google.com
arch/x86/kvm/x86.c

index 891ca97..c18d570 100644 (file)
@@ -3209,6 +3209,7 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        unsigned bank_num = mcg_cap & 0xff;
        u32 msr = msr_info->index;
        u64 data = msr_info->data;
+       u32 offset, last_msr;
 
        switch (msr) {
        case MSR_IA32_MCG_STATUS:
@@ -3222,35 +3223,36 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                        return 1;
                vcpu->arch.mcg_ctl = data;
                break;
-       default:
-               if (msr >= MSR_IA32_MC0_CTL &&
-                   msr < MSR_IA32_MCx_CTL(bank_num)) {
-                       u32 offset = array_index_nospec(
-                               msr - MSR_IA32_MC0_CTL,
-                               MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL);
-
-                       /* only 0 or all 1s can be written to IA32_MCi_CTL
-                        * some Linux kernels though clear bit 10 in bank 4 to
-                        * workaround a BIOS/GART TBL issue on AMD K8s, ignore
-                        * this to avoid an uncatched #GP in the guest.
-                        *
-                        * UNIXWARE clears bit 0 of MC1_CTL to ignore
-                        * correctable, single-bit ECC data errors.
-                        */
-                       if ((offset & 0x3) == 0 &&
-                           data != 0 && (data | (1 << 10) | 1) != ~(u64)0)
-                               return 1;
+       case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1:
+               last_msr = MSR_IA32_MCx_CTL(bank_num) - 1;
+               if (msr > last_msr)
+                       return 1;
 
-                       /* MCi_STATUS */
-                       if (!msr_info->host_initiated &&
-                           (offset & 0x3) == 1 && data != 0) {
-                               if (!can_set_mci_status(vcpu))
-                                       return 1;
-                       }
+               offset = array_index_nospec(msr - MSR_IA32_MC0_CTL,
+                                           last_msr + 1 - MSR_IA32_MC0_CTL);
 
-                       vcpu->arch.mce_banks[offset] = data;
-                       break;
-               }
+               /*
+                * Only 0 or all 1s can be written to IA32_MCi_CTL, all other
+                * values are architecturally undefined.  But, some Linux
+                * kernels clear bit 10 in bank 4 to workaround a BIOS/GART TLB
+                * issue on AMD K8s, allow bit 10 to be clear when setting all
+                * other bits in order to avoid an uncaught #GP in the guest.
+                *
+                * UNIXWARE clears bit 0 of MC1_CTL to ignore correctable,
+                * single-bit ECC data errors.
+                */
+               if ((offset & 0x3) == 0 &&
+                   data != 0 && (data | (1 << 10) | 1) != ~(u64)0)
+                       return 1;
+
+               /* MCi_STATUS */
+               if (!msr_info->host_initiated && (offset & 0x3) == 1 &&
+                   data != 0 && !can_set_mci_status(vcpu))
+                       return 1;
+
+               vcpu->arch.mce_banks[offset] = data;
+               break;
+       default:
                return 1;
        }
        return 0;
@@ -3819,6 +3821,7 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host)
        u64 data;
        u64 mcg_cap = vcpu->arch.mcg_cap;
        unsigned bank_num = mcg_cap & 0xff;
+       u32 offset, last_msr;
 
        switch (msr) {
        case MSR_IA32_P5_MC_ADDR:
@@ -3836,16 +3839,16 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host)
        case MSR_IA32_MCG_STATUS:
                data = vcpu->arch.mcg_status;
                break;
-       default:
-               if (msr >= MSR_IA32_MC0_CTL &&
-                   msr < MSR_IA32_MCx_CTL(bank_num)) {
-                       u32 offset = array_index_nospec(
-                               msr - MSR_IA32_MC0_CTL,
-                               MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL);
+       case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1:
+               last_msr = MSR_IA32_MCx_CTL(bank_num) - 1;
+               if (msr > last_msr)
+                       return 1;
 
-                       data = vcpu->arch.mce_banks[offset];
-                       break;
-               }
+               offset = array_index_nospec(msr - MSR_IA32_MC0_CTL,
+                                           last_msr + 1 - MSR_IA32_MC0_CTL);
+               data = vcpu->arch.mce_banks[offset];
+               break;
+       default:
                return 1;
        }
        *pdata = data;