• Borislav Petkov's avatar
    x86/thermal: Fix LVT thermal setup for SMI delivery mode · 9a90ed06
    Borislav Petkov authored
    There are machines out there with added value crap^WBIOS which provide an
    SMI handler for the local APIC thermal sensor interrupt. Out of reset,
    the BSP on those machines has something like 0x200 in that APIC register
    (timestamps left in because this whole issue is timing sensitive):
    
      [    0.033858] read lvtthmr: 0x330, val: 0x200
    
    which means:
    
     - bit 16 - the interrupt mask bit is clear and thus that interrupt is enabled
     - bits [10:8] have 010b which means SMI delivery mode.
    
    Now, later during boot, when the kernel programs the local APIC, it
    soft-disables it temporarily through the spurious vector register:
    
      setup_local_APIC:
    
      	...
    
    	/*
    	 * If this comes from kexec/kcrash the APIC might be enabled in
    	 * SPIV. Soft disable it before doing further initialization.
    	 */
    	value = apic_read(APIC_SPIV);
    	value &= ~APIC_SPIV_APIC_ENABLED;
    	apic_write(APIC_SPIV, value);
    
    which means (from the SDM):
    
    "10.4.7.2 Local APIC State After It Has Been Software Disabled
    
    ...
    
    * The mask bits for all the LVT entries are set. Attempts to reset these
    bits will be ignored."
    
    And this happens too:
    
      [    0.124111] APIC: Switch to symmetric I/O mode setup
      [    0.124117] lvtthmr 0x200 before write 0xf to APIC 0xf0
      [    0.124118] lvtthmr 0x10200 after write 0xf to APIC 0xf0
    
    This results in CPU 0 soft lockups depending on the placement in time
    when the APIC soft-disable happens. Those soft lockups are not 100%
    reproducible and the reason for that can only be speculated as no one
    tells you what SMM does. Likely, it confuses the SMM code that the APIC
    is disabled and the thermal interrupt doesn't doesn't fire at all,
    leading to CPU 0 stuck in SMM forever...
    
    Now, before
    
      4f432e8b ("x86/mce: Get rid of mcheck_intel_therm_init()")
    
    due to how the APIC_LVTTHMR was read before APIC initialization in
    mcheck_intel_therm_init(), it would read the value with the mask bit 16
    clear and then intel_init_thermal() would replicate it onto the APs and
    all would be peachy - the thermal interrupt would remain enabled.
    
    But that commit moved that reading to a later moment in
    intel_init_thermal(), resulting in reading APIC_LVTTHMR on the BSP too
    late and with its interrupt mask bit set.
    
    Thus, revert back to the old behavior of reading the thermal LVT
    register before the APIC gets initialized.
    
    Fixes: 4f432e8b ("x86/mce: Get rid of mcheck_intel_therm_init()")
    Reported-by: default avatarJames Feeney <james@nurealm.net>
    Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
    Cc: <stable@vger.kernel.org>
    Cc: Zhang Rui <rui.zhang@intel.com>
    Cc: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
    Link: https://lkml.kernel.org/r/YKIqDdFNaXYd39wz@zn.tnic
    9a90ed06
thermal.h 428 Bytes