| @@ -47,11 +47,13 @@ · 46 unmodified lines |
| 47 | 47 | #include <linux/tick.h> |
| 48 | 48 | #include <trace/events/power.h> |
| 49 | 49 | #include <linux/sched.h> |
| 50 | +#include <linux/sched/smt.h> |
| 50 | 51 | #include <linux/notifier.h> |
| 51 | 52 | #include <linux/cpu.h> |
| 52 | 53 | #include <linux/moduleparam.h> |
| 53 | 54 | #include <asm/cpu_device_id.h> |
| 54 | 55 | #include <asm/intel-family.h> |
| 56 | +#include <asm/nospec-branch.h> |
| 55 | 57 | #include <asm/mwait.h> |
| 56 | 58 | #include <asm/msr.h> |
| 57 | 59 | |
| @@ -105,6 +107,12 @@ static unsigned int mwait_substates __initdata; · 47 unmodified lines |
| 105 | 107 | */ |
| 106 | 108 | #define CPUIDLE_FLAG_ALWAYS_ENABLE BIT(15) |
| 107 | 109 | |
| 110 | +/* |
| 111 | + * Disable IBRS across idle (when KERNEL_IBRS), is exclusive vs IRQ_ENABLE |
| 112 | + * above. |
| 113 | + */ |
| 114 | +#define CPUIDLE_FLAG_IBRS BIT(16) |
| 115 | + |
| 108 | 116 | /* |
| 109 | 117 | * MWAIT takes an 8-bit "hint" in EAX "suggesting" |
| 110 | 118 | * the C-state (top nibble) and sub-state (bottom nibble) |
| @@ -159,6 +167,24 @@ static __cpuidle int intel_idle_irq(struct cpuidle_device *dev, · 48 unmodified lines |
| 159 | 167 | return ret; |
| 160 | 168 | } |
| 161 | 169 | |
| 170 | +static __cpuidle int intel_idle_ibrs(struct cpuidle_device *dev, |
| 171 | + struct cpuidle_driver *drv, int index) |
| 172 | +{ |
| 173 | + bool smt_active = sched_smt_active(); |
| 174 | + u64 spec_ctrl = spec_ctrl_current(); |
| 175 | + int ret; |
| 176 | + |
| 177 | + if (smt_active) |
| 178 | + wrmsrl(MSR_IA32_SPEC_CTRL, 0); |
| 179 | + |
| 180 | + ret = __intel_idle(dev, drv, index); |
| 181 | + |
| 182 | + if (smt_active) |
| 183 | + wrmsrl(MSR_IA32_SPEC_CTRL, spec_ctrl); |
| 184 | + |
| 185 | + return ret; |
| 186 | +} |
| 187 | + |
| 162 | 188 | /** |
| 163 | 189 | * intel_idle_s2idle - Ask the processor to enter the given idle state. |
| 164 | 190 | * @dev: cpuidle device of the target CPU. |
| @@ -680,7 +706,7 @@ static struct cpuidle_state skl_cstates[] __initdata = { · 515 unmodified lines |
| 680 | 706 | { |
| 681 | 707 | .name = "C6", |
| 682 | 708 | .desc = "MWAIT 0x20", |
| 683 | | − .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 709 | + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 684 | 710 | .exit_latency = 85, |
| 685 | 711 | .target_residency = 200, |
| 686 | 712 | .enter = &intel_idle, |
| @@ -688,7 +714,7 @@ static struct cpuidle_state skl_cstates[] __initdata = { · 1 unmodified line |
| 688 | 714 | { |
| 689 | 715 | .name = "C7s", |
| 690 | 716 | .desc = "MWAIT 0x33", |
| 691 | | − .flags = MWAIT2flg(0x33) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 717 | + .flags = MWAIT2flg(0x33) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 692 | 718 | .exit_latency = 124, |
| 693 | 719 | .target_residency = 800, |
| 694 | 720 | .enter = &intel_idle, |
| @@ -696,7 +722,7 @@ static struct cpuidle_state skl_cstates[] __initdata = { · 1 unmodified line |
| 696 | 722 | { |
| 697 | 723 | .name = "C8", |
| 698 | 724 | .desc = "MWAIT 0x40", |
| 699 | | − .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 725 | + .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 700 | 726 | .exit_latency = 200, |
| 701 | 727 | .target_residency = 800, |
| 702 | 728 | .enter = &intel_idle, |
| @@ -704,7 +730,7 @@ static struct cpuidle_state skl_cstates[] __initdata = { · 1 unmodified line |
| 704 | 730 | { |
| 705 | 731 | .name = "C9", |
| 706 | 732 | .desc = "MWAIT 0x50", |
| 707 | | − .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 733 | + .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 708 | 734 | .exit_latency = 480, |
| 709 | 735 | .target_residency = 5000, |
| 710 | 736 | .enter = &intel_idle, |
| @@ -712,7 +738,7 @@ static struct cpuidle_state skl_cstates[] __initdata = { · 1 unmodified line |
| 712 | 738 | { |
| 713 | 739 | .name = "C10", |
| 714 | 740 | .desc = "MWAIT 0x60", |
| 715 | | − .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 741 | + .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 716 | 742 | .exit_latency = 890, |
| 717 | 743 | .target_residency = 5000, |
| 718 | 744 | .enter = &intel_idle, |
| @@ -741,7 +767,7 @@ static struct cpuidle_state skx_cstates[] __initdata = { · 22 unmodified lines |
| 741 | 767 | { |
| 742 | 768 | .name = "C6", |
| 743 | 769 | .desc = "MWAIT 0x20", |
| 744 | | − .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, |
| 770 | + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED | CPUIDLE_FLAG_IBRS, |
| 745 | 771 | .exit_latency = 133, |
| 746 | 772 | .target_residency = 600, |
| 747 | 773 | .enter = &intel_idle, |
| @@ -1819,6 +1845,12 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) · 1071 unmodified lines |
| 1819 | 1845 | if (cpuidle_state_table[cstate].flags & CPUIDLE_FLAG_IRQ_ENABLE) |
| 1820 | 1846 | drv->states[drv->state_count].enter = intel_idle_irq; |
| 1821 | 1847 | |
| 1848 | + if (cpu_feature_enabled(X86_FEATURE_KERNEL_IBRS) && |
| 1849 | + cpuidle_state_table[cstate].flags & CPUIDLE_FLAG_IBRS) { |
| 1850 | + WARN_ON_ONCE(cpuidle_state_table[cstate].flags & CPUIDLE_FLAG_IRQ_ENABLE); |
| 1851 | + drv->states[drv->state_count].enter = intel_idle_ibrs; |
| 1852 | + } |
| 1853 | + |
| 1822 | 1854 | if ((disabled_states_mask & BIT(drv->state_count)) || |
| 1823 | 1855 | ((icpu->use_acpi || force_use_acpi) && |
| 1824 | 1856 | intel_idle_off_by_default(mwait_hint) && |