diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index a58800973aed..5de423a24777 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -671,6 +671,48 @@ DEFINE_IDTENTRY_RAW(exc_int3) } #ifdef CONFIG_X86_64 + +/* + * Reload the %gs selector for user space (setting GS_BASE) with + * exception handling: if the selector is invalid, set %gs to NULL. + * + * selector: new selector + * + * This function is noinstr as it must not be instrumented. + */ +noinstr void native_load_gs_index(unsigned int selector) +{ + unsigned long flags; + + local_irq_save(flags); + +again: + native_swapgs(); + + if (static_cpu_has_bug(X86_BUG_NULL_SEG)) { + if (likely(selector <= 3)) + asm volatile("mov %[seg],%%gs" + : : [seg] "rm" (__USER_DS)); + } + + asm_volatile_goto("1: mov %[seg],%%gs\n" + _ASM_EXTABLE(1b, %l[bad_seg]) + : : [seg] "rm" (selector) + : : bad_seg); + + alternative("", "mfence", X86_BUG_SWAPGS_FENCE); + native_swapgs(); + local_irq_restore(flags); + return; + +bad_seg: + /* + * Invalid selector, set %gs to null (0). + */ + selector = 0; + goto again; +} + /* * Help handler running on a per-cpu (IST or entry trampoline) stack * to switch to the normal thread stack if the interrupted code was in