Save a WRMSR GS.base?

Borislav Petkov posted 1 patch 4 days, 6 hours ago
Save a WRMSR GS.base?
Posted by Borislav Petkov 4 days, 6 hours ago
Hi,

so here:

diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index b85e715ebb30..ffa894bdb4ee 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -400,7 +400,9 @@ static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
 
 		/* Update the bases. */
 		wrfsbase(next->fsbase);
-		__wrgsbase_inactive(next->gsbase);
+
+		if (!cpu_feature_enabled(X86_FEATURE_LKGS))
+			__wrgsbase_inactive(next->gsbase);
 	} else {
 		load_seg_legacy(prev->fsindex, prev->fsbase,
 				next->fsindex, next->fsbase, FS);

a couple of lines above in that function we have:

                if (unlikely(prev->gsindex || next->gsindex))
                        loadseg(GS, next->gsindex);

which, on a FRED machine, would do LKGS. Now that insn does:

		GS.selector := SRC;
		GS.attributes := descriptor.attributes;
		IA32_KERNEL_GS_BASE := descriptor.base; // bits 63:32 cleared

so I can save myself the __wrgsbase_inactive() which ends up doing WRMSR
GS.base. 

Right? I.e., the diff above.

We're also not doing the optimization of checking whether prev.GS.base and
next.GS.base are equal. I see them both 0 in a trace here but I guess
luserpace can change them so I guess we wanna overwrite GS.base on context
switch unconditionally.

But LKGS does that for us so we don't need the WRMSR GS.base there, right?

Or am I missing something?

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by Andrew Cooper 3 days, 23 hours ago
On 04/06/2026 2:53 am, Borislav Petkov wrote:
> Hi,
>
> so here:
>
> diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
> index b85e715ebb30..ffa894bdb4ee 100644
> --- a/arch/x86/kernel/process_64.c
> +++ b/arch/x86/kernel/process_64.c
> @@ -400,7 +400,9 @@ static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
>  
>  		/* Update the bases. */
>  		wrfsbase(next->fsbase);
> -		__wrgsbase_inactive(next->gsbase);
> +
> +		if (!cpu_feature_enabled(X86_FEATURE_LKGS))
> +			__wrgsbase_inactive(next->gsbase);
>  	} else {
>  		load_seg_legacy(prev->fsindex, prev->fsbase,
>  				next->fsindex, next->fsbase, FS);
>
> a couple of lines above in that function we have:
>
>                 if (unlikely(prev->gsindex || next->gsindex))
>                         loadseg(GS, next->gsindex);
>
> which, on a FRED machine, would do LKGS. Now that insn does:
>
> 		GS.selector := SRC;
> 		GS.attributes := descriptor.attributes;
> 		IA32_KERNEL_GS_BASE := descriptor.base; // bits 63:32 cleared
>
> so I can save myself the __wrgsbase_inactive() which ends up doing WRMSR
> GS.base. 
>
> Right? I.e., the diff above.
>
> We're also not doing the optimization of checking whether prev.GS.base and
> next.GS.base are equal. I see them both 0 in a trace here but I guess
> luserpace can change them so I guess we wanna overwrite GS.base on context
> switch unconditionally.
>
> But LKGS does that for us so we don't need the WRMSR GS.base there, right?
>
> Or am I missing something?

Yes, but it took me writing a "no" email to spot it.

If the LKGS (in load seg) was called unconditionally, then yes it would
be safe to drop the __wrgsbase_inactive(), but it's not.

Consider a prev and next which both have the same ->gsindex (so skips
loadseg()), but have different ->gsbase (still need to update KERN_GS_BASE).

~Andrew
Re: Save a WRMSR GS.base?
Posted by Borislav Petkov 3 days, 6 hours ago
On Thu, Jun 04, 2026 at 10:17:57AM +0100, Andrew Cooper wrote:
> Yes, but it took me writing a "no" email to spot it.

Oh, I know those situations.

> If the LKGS (in load seg) was called unconditionally, then yes it would
> be safe to drop the __wrgsbase_inactive(), but it's not.
> 
> Consider a prev and next which both have the same ->gsindex (so skips
> loadseg()), but have different ->gsbase (still need to update KERN_GS_BASE).

Gah, ofc.

So we'll have to do something like this which is ugly as hell:

---

diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index b85e715ebb30..248c39da9ba0 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -391,16 +391,23 @@ static __always_inline void x86_pkru_load(struct thread_struct *prev,
 static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
 					      struct thread_struct *next)
 {
+	bool loaded_gs = false;
+
 	if (static_cpu_has(X86_FEATURE_FSGSBASE)) {
 		/* Update the FS and GS selectors if they could have changed. */
 		if (unlikely(prev->fsindex || next->fsindex))
 			loadseg(FS, next->fsindex);
-		if (unlikely(prev->gsindex || next->gsindex))
+
+		if (unlikely(prev->gsindex || next->gsindex)) {
 			loadseg(GS, next->gsindex);
+			loaded_gs = true;
+		}
 
 		/* Update the bases. */
 		wrfsbase(next->fsbase);
-		__wrgsbase_inactive(next->gsbase);
+
+		if (!(cpu_feature_enabled(X86_FEATURE_LKGS) && loaded_gs))
+			__wrgsbase_inactive(next->gsbase);
 	} else {
 		load_seg_legacy(prev->fsindex, prev->fsbase,
 				next->fsindex, next->fsbase, FS);


Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 3 days, 6 hours ago
On June 4, 2026 7:24:28 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>On Thu, Jun 04, 2026 at 10:17:57AM +0100, Andrew Cooper wrote:
>> Yes, but it took me writing a "no" email to spot it.
>
>Oh, I know those situations.
>
>> If the LKGS (in load seg) was called unconditionally, then yes it would
>> be safe to drop the __wrgsbase_inactive(), but it's not.
>> 
>> Consider a prev and next which both have the same ->gsindex (so skips
>> loadseg()), but have different ->gsbase (still need to update KERN_GS_BASE).
>
>Gah, ofc.
>
>So we'll have to do something like this which is ugly as hell:
>
>---
>
>diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
>index b85e715ebb30..248c39da9ba0 100644
>--- a/arch/x86/kernel/process_64.c
>+++ b/arch/x86/kernel/process_64.c
>@@ -391,16 +391,23 @@ static __always_inline void x86_pkru_load(struct thread_struct *prev,
> static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
> 					      struct thread_struct *next)
> {
>+	bool loaded_gs = false;
>+
> 	if (static_cpu_has(X86_FEATURE_FSGSBASE)) {
> 		/* Update the FS and GS selectors if they could have changed. */
> 		if (unlikely(prev->fsindex || next->fsindex))
> 			loadseg(FS, next->fsindex);
>-		if (unlikely(prev->gsindex || next->gsindex))
>+
>+		if (unlikely(prev->gsindex || next->gsindex)) {
> 			loadseg(GS, next->gsindex);
>+			loaded_gs = true;
>+		}
> 
> 		/* Update the bases. */
> 		wrfsbase(next->fsbase);
>-		__wrgsbase_inactive(next->gsbase);
>+
>+		if (!(cpu_feature_enabled(X86_FEATURE_LKGS) && loaded_gs))
>+			__wrgsbase_inactive(next->gsbase);
> 	} else {
> 		load_seg_legacy(prev->fsindex, prev->fsbase,
> 				next->fsindex, next->fsbase, FS);
>
>
>Thx.
>

Also consider that user space might have done: 

    mov gs,...
    wrgsbase ...

So gs.selector > 3 doesn't necessarily mean that the base is consistent with the descriptor.
Re: Save a WRMSR GS.base?
Posted by Borislav Petkov 3 days, 5 hours ago
On Thu, Jun 04, 2026 at 07:36:01PM -0700, H. Peter Anvin wrote:
> >diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
> >index b85e715ebb30..248c39da9ba0 100644
> >--- a/arch/x86/kernel/process_64.c
> >+++ b/arch/x86/kernel/process_64.c
> >@@ -391,16 +391,23 @@ static __always_inline void x86_pkru_load(struct thread_struct *prev,
> > static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
> > 					      struct thread_struct *next)
> > {
> >+	bool loaded_gs = false;
> >+
> > 	if (static_cpu_has(X86_FEATURE_FSGSBASE)) {
> > 		/* Update the FS and GS selectors if they could have changed. */
> > 		if (unlikely(prev->fsindex || next->fsindex))
> > 			loadseg(FS, next->fsindex);
> >-		if (unlikely(prev->gsindex || next->gsindex))
> >+
> >+		if (unlikely(prev->gsindex || next->gsindex)) {
> > 			loadseg(GS, next->gsindex);
> >+			loaded_gs = true;
> >+		}
> > 
> > 		/* Update the bases. */
> > 		wrfsbase(next->fsbase);
> >-		__wrgsbase_inactive(next->gsbase);
> >+
> >+		if (!(cpu_feature_enabled(X86_FEATURE_LKGS) && loaded_gs))
> >+			__wrgsbase_inactive(next->gsbase);
> > 	} else {
> > 		load_seg_legacy(prev->fsindex, prev->fsbase,
> > 				next->fsindex, next->fsbase, FS);
> >
> >
> >Thx.
> >
> 
> Also consider that user space might have done: 
> 
>     mov gs,...
>     wrgsbase ...
> 
> So gs.selector > 3 doesn't necessarily mean that the base is consistent with the descriptor.

Right, I want to avoid the second write to KERNEL_GS_BASE iff we have done
LKGS before.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 3 days, 5 hours ago
On June 4, 2026 7:54:53 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>On Thu, Jun 04, 2026 at 07:36:01PM -0700, H. Peter Anvin wrote:
>> >diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
>> >index b85e715ebb30..248c39da9ba0 100644
>> >--- a/arch/x86/kernel/process_64.c
>> >+++ b/arch/x86/kernel/process_64.c
>> >@@ -391,16 +391,23 @@ static __always_inline void x86_pkru_load(struct thread_struct *prev,
>> > static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
>> > 					      struct thread_struct *next)
>> > {
>> >+	bool loaded_gs = false;
>> >+
>> > 	if (static_cpu_has(X86_FEATURE_FSGSBASE)) {
>> > 		/* Update the FS and GS selectors if they could have changed. */
>> > 		if (unlikely(prev->fsindex || next->fsindex))
>> > 			loadseg(FS, next->fsindex);
>> >-		if (unlikely(prev->gsindex || next->gsindex))
>> >+
>> >+		if (unlikely(prev->gsindex || next->gsindex)) {
>> > 			loadseg(GS, next->gsindex);
>> >+			loaded_gs = true;
>> >+		}
>> > 
>> > 		/* Update the bases. */
>> > 		wrfsbase(next->fsbase);
>> >-		__wrgsbase_inactive(next->gsbase);
>> >+
>> >+		if (!(cpu_feature_enabled(X86_FEATURE_LKGS) && loaded_gs))
>> >+			__wrgsbase_inactive(next->gsbase);
>> > 	} else {
>> > 		load_seg_legacy(prev->fsindex, prev->fsbase,
>> > 				next->fsindex, next->fsbase, FS);
>> >
>> >
>> >Thx.
>> >
>> 
>> Also consider that user space might have done: 
>> 
>>     mov gs,...
>>     wrgsbase ...
>> 
>> So gs.selector > 3 doesn't necessarily mean that the base is consistent with the descriptor.
>
>Right, I want to avoid the second write to KERNEL_GS_BASE iff we have done
>LKGS before.
>

I guess the question is why there is a "first" one.

Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS can be replaced with swapgs/mov gs/swapgs on legacy.
Re: Save a WRMSR GS.base?
Posted by Borislav Petkov 3 days, 4 hours ago
On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
> I guess the question is why there is a "first" one.

That happens when we do:

x86_fsgsbase_load()

	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
                native_lkgs(selector);

then back in x86_fsgsbase_load() we do:

	__wrgsbase_inactive(next->gsbase);

which does

	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);

on FRED.

But LKGS already wrote MSR_KERNEL_GS_BASE...

> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
> can be replaced with swapgs/mov gs/swapgs on legacy.

Right.

I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
back...

Although, I need to think how to make it pretty...

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 3 days, 4 hours ago
On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>> I guess the question is why there is a "first" one.
>
>That happens when we do:
>
>x86_fsgsbase_load()
>
>	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>                native_lkgs(selector);
>
>then back in x86_fsgsbase_load() we do:
>
>	__wrgsbase_inactive(next->gsbase);
>
>which does
>
>	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>
>on FRED.
>
>But LKGS already wrote MSR_KERNEL_GS_BASE...
>
>> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>> can be replaced with swapgs/mov gs/swapgs on legacy.
>
>Right.
>
>I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>back...
>
>Although, I need to think how to make it pretty...
>

Should be doing wrmsrns...
Re: Save a WRMSR GS.base?
Posted by Borislav Petkov 3 days, 4 hours ago
On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
> >On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
> >> I guess the question is why there is a "first" one.
> >
> >That happens when we do:
> >
> >x86_fsgsbase_load()
> >
> >	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
> >	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
> >                native_lkgs(selector);
> >
> >then back in x86_fsgsbase_load() we do:
> >
> >	__wrgsbase_inactive(next->gsbase);
> >
> >which does
> >
> >	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
> >
> >on FRED.
> >
> >But LKGS already wrote MSR_KERNEL_GS_BASE...
> >
> >> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
> >> can be replaced with swapgs/mov gs/swapgs on legacy.
> >
> >Right.
> >
> >I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
> >back...
> >
> >Although, I need to think how to make it pretty...
> >
> 
> Should be doing wrmsrns...

No, I think that second WRMSR* should not happen at all if we have executed
LKGS which has already written MSR_KERNEL_GS_BASE, right?


-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 3 days, 3 hours ago
On June 4, 2026 9:38:46 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
>> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>> >On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>> >> I guess the question is why there is a "first" one.
>> >
>> >That happens when we do:
>> >
>> >x86_fsgsbase_load()
>> >
>> >	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>> >	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>> >                native_lkgs(selector);
>> >
>> >then back in x86_fsgsbase_load() we do:
>> >
>> >	__wrgsbase_inactive(next->gsbase);
>> >
>> >which does
>> >
>> >	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>> >
>> >on FRED.
>> >
>> >But LKGS already wrote MSR_KERNEL_GS_BASE...
>> >
>> >> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>> >> can be replaced with swapgs/mov gs/swapgs on legacy.
>> >
>> >Right.
>> >
>> >I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>> >back...
>> >
>> >Although, I need to think how to make it pretty...
>> >
>> 
>> Should be doing wrmsrns...
>
>No, I think that second WRMSR* should not happen at all if we have executed
>LKGS which has already written MSR_KERNEL_GS_BASE, right?
>
>

You can't do that (at least not without further checks) if user space has WRGSBASE enabled, since you have no guarantee that the active GS.base is consistent with GS.selector.

Since GS > 3 is pretty rare in 64-bit code at least, it doesn't seem to be a code path that needs to be that heavily optimized.
Re: Save a WRMSR GS.base?
Posted by Andrew Cooper 2 days, 23 hours ago
On 05/06/2026 6:05 am, H. Peter Anvin wrote:
> On June 4, 2026 9:38:46 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>> On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
>>> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>> On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>>>>> I guess the question is why there is a "first" one.
>>>> That happens when we do:
>>>>
>>>> x86_fsgsbase_load()
>>>>
>>>> 	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>>>> 	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>>>>                native_lkgs(selector);
>>>>
>>>> then back in x86_fsgsbase_load() we do:
>>>>
>>>> 	__wrgsbase_inactive(next->gsbase);
>>>>
>>>> which does
>>>>
>>>> 	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>>>>
>>>> on FRED.
>>>>
>>>> But LKGS already wrote MSR_KERNEL_GS_BASE...
>>>>
>>>>> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>>>>> can be replaced with swapgs/mov gs/swapgs on legacy.
>>>> Right.
>>>>
>>>> I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>>>> back...
>>>>
>>>> Although, I need to think how to make it pretty...
>>>>
>>> Should be doing wrmsrns...
>> No, I think that second WRMSR* should not happen at all if we have executed
>> LKGS which has already written MSR_KERNEL_GS_BASE, right?
>>
>>
> You can't do that (at least not without further checks) if user space has WRGSBASE enabled, since you have no guarantee that the active GS.base is consistent with GS.selector.
>
> Since GS > 3 is pretty rare in 64-bit code at least, it doesn't seem to be a code path that needs to be that heavily optimized.

I think you're slightly talking past each other, and I also made a
mistake on the original reply, so lets try rephrasing it.

LGKS only writes a zero-extended 32bit value into KERN_GS_BASE.  This is
because there's only 32 bits of information in the GDT/LDT.

So the real write into KERN_GS_BASE is still needed.  Sorry - you can't
optimise this away.  Also, I'm pretty sure amluto did some x86 selftests
covering this last time the logic was rewritten.


As to WRMSR vs WRMSRNS, yes Intel CPUs want this to be WRMSRNS.  AMD
don't have WRMSRNS but this particular MSR index is architecturally not
architecturally serialising anyway.

~Andrew
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 2 days, 17 hours ago
On June 5, 2026 2:13:07 AM PDT, Andrew Cooper <andrew.cooper3@citrix.com> wrote:
>On 05/06/2026 6:05 am, H. Peter Anvin wrote:
>> On June 4, 2026 9:38:46 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>> On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
>>>> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>>> On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>>>>>> I guess the question is why there is a "first" one.
>>>>> That happens when we do:
>>>>>
>>>>> x86_fsgsbase_load()
>>>>>
>>>>> 	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>>>>> 	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>>>>>                native_lkgs(selector);
>>>>>
>>>>> then back in x86_fsgsbase_load() we do:
>>>>>
>>>>> 	__wrgsbase_inactive(next->gsbase);
>>>>>
>>>>> which does
>>>>>
>>>>> 	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>>>>>
>>>>> on FRED.
>>>>>
>>>>> But LKGS already wrote MSR_KERNEL_GS_BASE...
>>>>>
>>>>>> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>>>>>> can be replaced with swapgs/mov gs/swapgs on legacy.
>>>>> Right.
>>>>>
>>>>> I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>>>>> back...
>>>>>
>>>>> Although, I need to think how to make it pretty...
>>>>>
>>>> Should be doing wrmsrns...
>>> No, I think that second WRMSR* should not happen at all if we have executed
>>> LKGS which has already written MSR_KERNEL_GS_BASE, right?
>>>
>>>
>> You can't do that (at least not without further checks) if user space has WRGSBASE enabled, since you have no guarantee that the active GS.base is consistent with GS.selector.
>>
>> Since GS > 3 is pretty rare in 64-bit code at least, it doesn't seem to be a code path that needs to be that heavily optimized.
>
>I think you're slightly talking past each other, and I also made a
>mistake on the original reply, so lets try rephrasing it.
>
>LGKS only writes a zero-extended 32bit value into KERN_GS_BASE.  This is
>because there's only 32 bits of information in the GDT/LDT.
>
>So the real write into KERN_GS_BASE is still needed.  Sorry - you can't
>optimise this away.  Also, I'm pretty sure amluto did some x86 selftests
>covering this last time the logic was rewritten.
>
>
>As to WRMSR vs WRMSRNS, yes Intel CPUs want this to be WRMSRNS.  AMD
>don't have WRMSRNS but this particular MSR index is architecturally not
>architecturally serialising anyway.
>
>~Andrew

It's not just a matter of it being a 32-bit base, it might not even be the correct one even so.
Re: Save a WRMSR GS.base?
Posted by Andrew Cooper 2 days, 17 hours ago
On 05/06/2026 4:13 pm, H. Peter Anvin wrote:
> On June 5, 2026 2:13:07 AM PDT, Andrew Cooper <andrew.cooper3@citrix.com> wrote:
>> On 05/06/2026 6:05 am, H. Peter Anvin wrote:
>>> On June 4, 2026 9:38:46 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>> On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
>>>>> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>>>> On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>>>>>>> I guess the question is why there is a "first" one.
>>>>>> That happens when we do:
>>>>>>
>>>>>> x86_fsgsbase_load()
>>>>>>
>>>>>> 	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>>>>>> 	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>>>>>>                native_lkgs(selector);
>>>>>>
>>>>>> then back in x86_fsgsbase_load() we do:
>>>>>>
>>>>>> 	__wrgsbase_inactive(next->gsbase);
>>>>>>
>>>>>> which does
>>>>>>
>>>>>> 	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>>>>>>
>>>>>> on FRED.
>>>>>>
>>>>>> But LKGS already wrote MSR_KERNEL_GS_BASE...
>>>>>>
>>>>>>> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>>>>>>> can be replaced with swapgs/mov gs/swapgs on legacy.
>>>>>> Right.
>>>>>>
>>>>>> I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>>>>>> back...
>>>>>>
>>>>>> Although, I need to think how to make it pretty...
>>>>>>
>>>>> Should be doing wrmsrns...
>>>> No, I think that second WRMSR* should not happen at all if we have executed
>>>> LKGS which has already written MSR_KERNEL_GS_BASE, right?
>>>>
>>>>
>>> You can't do that (at least not without further checks) if user space has WRGSBASE enabled, since you have no guarantee that the active GS.base is consistent with GS.selector.
>>>
>>> Since GS > 3 is pretty rare in 64-bit code at least, it doesn't seem to be a code path that needs to be that heavily optimized.
>> I think you're slightly talking past each other, and I also made a
>> mistake on the original reply, so lets try rephrasing it.
>>
>> LGKS only writes a zero-extended 32bit value into KERN_GS_BASE.  This is
>> because there's only 32 bits of information in the GDT/LDT.
>>
>> So the real write into KERN_GS_BASE is still needed.  Sorry - you can't
>> optimise this away.  Also, I'm pretty sure amluto did some x86 selftests
>> covering this last time the logic was rewritten.
>>
>>
>> As to WRMSR vs WRMSRNS, yes Intel CPUs want this to be WRMSRNS.  AMD
>> don't have WRMSRNS but this particular MSR index is architecturally not
>> architecturally serialising anyway.
>>
>> ~Andrew
> It's not just a matter of it being a 32-bit base, it might not even be the correct one even so.

Indeed, GS might be an LDT selector.

~Andrew
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin 2 days, 16 hours ago
On June 5, 2026 8:16:53 AM PDT, Andrew Cooper <andrew.cooper3@citrix.com> wrote:
>On 05/06/2026 4:13 pm, H. Peter Anvin wrote:
>> On June 5, 2026 2:13:07 AM PDT, Andrew Cooper <andrew.cooper3@citrix.com> wrote:
>>> On 05/06/2026 6:05 am, H. Peter Anvin wrote:
>>>> On June 4, 2026 9:38:46 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>>> On Thu, Jun 04, 2026 at 09:30:33PM -0700, H. Peter Anvin wrote:
>>>>>> On June 4, 2026 9:26:52 PM PDT, Borislav Petkov <bp@alien8.de> wrote:
>>>>>>> On Thu, Jun 04, 2026 at 08:20:57PM -0700, H. Peter Anvin wrote:
>>>>>>>> I guess the question is why there is a "first" one.
>>>>>>> That happens when we do:
>>>>>>>
>>>>>>> x86_fsgsbase_load()
>>>>>>>
>>>>>>> 	loadseg(GS) -> load_gs_index() -> native_load_gs_index() ->
>>>>>>> 	  if (cpu_feature_enabled(X86_FEATURE_LKGS))
>>>>>>>                native_lkgs(selector);
>>>>>>>
>>>>>>> then back in x86_fsgsbase_load() we do:
>>>>>>>
>>>>>>> 	__wrgsbase_inactive(next->gsbase);
>>>>>>>
>>>>>>> which does
>>>>>>>
>>>>>>> 	wrmsrq(MSR_KERNEL_GS_BASE, gsbase);
>>>>>>>
>>>>>>> on FRED.
>>>>>>>
>>>>>>> But LKGS already wrote MSR_KERNEL_GS_BASE...
>>>>>>>
>>>>>>>> Logically the sequence should be LKGS first, if needed; then WRMSR(NS). LKGS
>>>>>>>> can be replaced with swapgs/mov gs/swapgs on legacy.
>>>>>>> Right.
>>>>>>>
>>>>>>> I think avoiding that second WRMSR(MSR_KERNEL_GS_BASE) should give some perf
>>>>>>> back...
>>>>>>>
>>>>>>> Although, I need to think how to make it pretty...
>>>>>>>
>>>>>> Should be doing wrmsrns...
>>>>> No, I think that second WRMSR* should not happen at all if we have executed
>>>>> LKGS which has already written MSR_KERNEL_GS_BASE, right?
>>>>>
>>>>>
>>>> You can't do that (at least not without further checks) if user space has WRGSBASE enabled, since you have no guarantee that the active GS.base is consistent with GS.selector.
>>>>
>>>> Since GS > 3 is pretty rare in 64-bit code at least, it doesn't seem to be a code path that needs to be that heavily optimized.
>>> I think you're slightly talking past each other, and I also made a
>>> mistake on the original reply, so lets try rephrasing it.
>>>
>>> LGKS only writes a zero-extended 32bit value into KERN_GS_BASE.  This is
>>> because there's only 32 bits of information in the GDT/LDT.
>>>
>>> So the real write into KERN_GS_BASE is still needed.  Sorry - you can't
>>> optimise this away.  Also, I'm pretty sure amluto did some x86 selftests
>>> covering this last time the logic was rewritten.
>>>
>>>
>>> As to WRMSR vs WRMSRNS, yes Intel CPUs want this to be WRMSRNS.  AMD
>>> don't have WRMSRNS but this particular MSR index is architecturally not
>>> architecturally serialising anyway.
>>>
>>> ~Andrew
>> It's not just a matter of it being a 32-bit base, it might not even be the correct one even so.
>
>Indeed, GS might be an LDT selector.
>
>~Andrew

No, GS.base might have been loaded (with wrgsbase) after GS was loaded, so it could be *completely different*.
Re: Save a WRMSR GS.base?
Posted by Borislav Petkov 2 days, 15 hours ago
On Fri, Jun 05, 2026 at 08:51:04AM -0700, H. Peter Anvin wrote:
> No, GS.base might have been loaded (with wrgsbase) after GS was loaded, so
> it could be *completely different*.

So you're basically saying, LKGS would load the IA32_KERNEL_GS_BASE which
belongs to the segment selector. This is what the pseudo code in the SDM says:

  GS.selector := SRC;
  GS.attributes := descriptor.attributes;
  IA32_KERNEL_GS_BASE := descriptor.base; // bits 63:32 cleared

Now, luserspace might've put something else in GS.base with WRGSBASE:

  GS.base := SRC;

So now, on context switch, we need to load IA32_KERNEL_GS_BASE with
next->gsbase which is the GS.base of the next task we're switching to.

And yes, GS.base is mapped to IA32_KERNEL_GS_BASE so yes, we must do that
update.

And yes, as Andrew points out, both LKGS and WRMGSBASE do 32-bit writes only
so we need to do the full MSR write.

Ok, thanks guys, that makes sense.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: Save a WRMSR GS.base?
Posted by H. Peter Anvin an hour ago
On June 5, 2026 10:17:11 AM PDT, Borislav Petkov <bp@alien8.de> wrote:
>On Fri, Jun 05, 2026 at 08:51:04AM -0700, H. Peter Anvin wrote:
>> No, GS.base might have been loaded (with wrgsbase) after GS was loaded, so
>> it could be *completely different*.
>
>So you're basically saying, LKGS would load the IA32_KERNEL_GS_BASE which
>belongs to the segment selector. This is what the pseudo code in the SDM says:
>
>  GS.selector := SRC;
>  GS.attributes := descriptor.attributes;
>  IA32_KERNEL_GS_BASE := descriptor.base; // bits 63:32 cleared
>
>Now, luserspace might've put something else in GS.base with WRGSBASE:
>
>  GS.base := SRC;
>
>So now, on context switch, we need to load IA32_KERNEL_GS_BASE with
>next->gsbase which is the GS.base of the next task we're switching to.
>
>And yes, GS.base is mapped to IA32_KERNEL_GS_BASE so yes, we must do that
>update.
>
>And yes, as Andrew points out, both LKGS and WRMGSBASE do 32-bit writes only
>so we need to do the full MSR write.
>
>Ok, thanks guys, that makes sense.
>

WRxSBASE does a 64-bit write, but for GS it would incorrectly address the kernel GS.base. For legacy it can be used under swapgs, but with FRED that is disallowed.