Skip to content

Commit 9782a71

Browse files
hansendcsuryasaimadhu
authored andcommitted
x86/fpu: Add PKRU storage outside of task XSAVE buffer
PKRU is currently partly XSAVE-managed and partly not. It has space in the task XSAVE buffer and is context-switched by XSAVE/XRSTOR. However, it is switched more eagerly than FPU because there may be a need for PKRU to be up-to-date for things like copy_to/from_user() since PKRU affects user-permission memory accesses, not just accesses from userspace itself. This leaves PKRU in a very odd position. XSAVE brings very little value to the table for how Linux uses PKRU except for signal related XSTATE handling. Prepare to move PKRU away from being XSAVE-managed. Allocate space in the thread_struct for it and save/restore it in the context-switch path separately from the XSAVE-managed features. task->thread_struct.pkru is only valid when the task is scheduled out. For the current task the authoritative source is the hardware, i.e. it has to be retrieved via rdpkru(). Leave the XSAVE code in place for now to ensure bisectability. Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Borislav Petkov <bp@suse.de> Link: https://lkml.kernel.org/r/20210623121456.399107624@linutronix.de
1 parent 2ebe81c commit 9782a71

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

arch/x86/include/asm/processor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,15 @@ struct thread_struct {
518518

519519
unsigned int sig_on_uaccess_err:1;
520520

521+
/*
522+
* Protection Keys Register for Userspace. Loaded immediately on
523+
* context switch. Store it in thread_struct to avoid a lookup in
524+
* the tasks's FPU xstate buffer. This value is only valid when a
525+
* task is scheduled out. For 'current' the authoritative source of
526+
* PKRU is the hardware itself.
527+
*/
528+
u32 pkru;
529+
521530
/* Floating point and extended processor state */
522531
struct fpu fpu;
523532
/*

arch/x86/kernel/process.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,18 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,
156156

157157
/* Kernel thread ? */
158158
if (unlikely(p->flags & PF_KTHREAD)) {
159+
p->thread.pkru = pkru_get_init_value();
159160
memset(childregs, 0, sizeof(struct pt_regs));
160161
kthread_frame_init(frame, sp, arg);
161162
return 0;
162163
}
163164

165+
/*
166+
* Clone current's PKRU value from hardware. tsk->thread.pkru
167+
* is only valid when scheduled out.
168+
*/
169+
p->thread.pkru = read_pkru();
170+
164171
frame->bx = 0;
165172
*childregs = *current_pt_regs();
166173
childregs->ax = 0;

arch/x86/kernel/process_64.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,29 @@ static __always_inline void load_seg_legacy(unsigned short prev_index,
340340
}
341341
}
342342

343+
/*
344+
* Store prev's PKRU value and load next's PKRU value if they differ. PKRU
345+
* is not XSTATE managed on context switch because that would require a
346+
* lookup in the task's FPU xsave buffer and require to keep that updated
347+
* in various places.
348+
*/
349+
static __always_inline void x86_pkru_load(struct thread_struct *prev,
350+
struct thread_struct *next)
351+
{
352+
if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
353+
return;
354+
355+
/* Stash the prev task's value: */
356+
prev->pkru = rdpkru();
357+
358+
/*
359+
* PKRU writes are slightly expensive. Avoid them when not
360+
* strictly necessary:
361+
*/
362+
if (prev->pkru != next->pkru)
363+
wrpkru(next->pkru);
364+
}
365+
343366
static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
344367
struct thread_struct *next)
345368
{
@@ -589,6 +612,8 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
589612

590613
x86_fsgsbase_load(prev, next);
591614

615+
x86_pkru_load(prev, next);
616+
592617
/*
593618
* Switch the PDA and FPU contexts.
594619
*/

0 commit comments

Comments
 (0)