Re: [PATCH v9 08/10] x86/mm/pti: Introduce a kernel/user CR3 software signal

From: Juri Lelli

Date: Mon Jun 01 2026 - 08:36:47 EST


Hello!

On 05/05/26 10:23, Valentin Schneider wrote:
> Later commits will rely on being able to check whether a remote CPU is
> using the kernel or the user CR3.
>
> This software signal needs to be updated before the actual CR3 write, IOW
> it always immediately precedes it:
>
> KERNEL_CR3_LOADED := 1
> SWITCH_TO_KERNEL_CR3
> [...]
> KERNEL_CR3_LOADED := 0
> SWITCH_TO_USER_CR3
>
> The variable also gets mapped into the user space visible pages.
> I tried really hard not to do that, and at some point had something mostly
> working with having an alias to it through the cpu_entry_area accessed like
> so before the switch to the kernel CR3:
>
> subq $10, %rsp
> sgdt (%rsp)
> movq 2(%rsp), \scratch_reg /* GDT address */
> addq $10, %rsp
>
> movl $1, CPU_ENTRY_AREA_kernel_cr3(\scratch_reg)
>
> however this explodes when running 64-bit user code that invokes SYSCALL,
> since the scratch reg is %rsp itself, and I figured this was enough headaches.
>
> This will only be really useful for NOHZ_FULL CPUs, but it should be
> cheaper to unconditionally update a never-used per-CPU variable living in
> its own cacheline than to check a shared cpumask such as
> housekeeping_cpumask(HK_TYPE_KERNEL_NOISE)
> at every entry.
>
> Signed-off-by: Valentin Schneider <vschneid@xxxxxxxxxx>
> ---

Just fyi, I had to do the below to make clang happy with this.

Best,
Juri
---
diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index deb8224b5ee48..07fabf762f700 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -186,7 +186,7 @@ For 32-bit we have the following conventions - kernel is built with

.macro SWITCH_TO_KERNEL_CR3 scratch_reg:req
ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_PTI
- NOTE_CR3_SWITCH \scratch_reg $1
+ NOTE_CR3_SWITCH \scratch_reg 1
mov %cr3, \scratch_reg
ADJUST_KERNEL_CR3 \scratch_reg
mov \scratch_reg, %cr3
@@ -197,7 +197,7 @@ For 32-bit we have the following conventions - kernel is built with
PER_CPU_VAR(cpu_tlbstate + TLB_STATE_user_pcid_flush_mask)

.macro SWITCH_TO_USER_CR3 scratch_reg:req scratch_reg2:req
- NOTE_CR3_SWITCH \scratch_reg $0
+ NOTE_CR3_SWITCH \scratch_reg 0
mov %cr3, \scratch_reg

ALTERNATIVE "jmp .Lwrcr3_\@", "", X86_FEATURE_PCID
@@ -245,7 +245,7 @@ For 32-bit we have the following conventions - kernel is built with

.macro SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg:req save_reg:req
ALTERNATIVE "jmp .Ldone_\@", "", X86_FEATURE_PTI
- NOTE_CR3_SWITCH \scratch_reg $1
+ NOTE_CR3_SWITCH \scratch_reg 1
movq %cr3, \scratch_reg
movq \scratch_reg, \save_reg
/*
@@ -274,7 +274,7 @@ For 32-bit we have the following conventions - kernel is built with
bt $PTI_USER_PGTABLE_BIT, \save_reg
jnc .Lend_\@

- NOTE_CR3_SWITCH \scratch_reg $0
+ NOTE_CR3_SWITCH \scratch_reg 0
ALTERNATIVE "jmp .Lwrcr3_\@", "", X86_FEATURE_PCID

/*