Skip to content

Commit 06bb53b

Browse files
Ram Paimpe
authored andcommitted
powerpc: store and restore the pkey state across context switches
Store and restore the AMR, IAMR and UAMOR register state of the task before scheduling out and after scheduling in, respectively. Signed-off-by: Ram Pai <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent dcf8729 commit 06bb53b

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

arch/powerpc/include/asm/mmu_context.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
195195

196196
#ifndef CONFIG_PPC_MEM_KEYS
197197
#define pkey_mm_init(mm)
198+
#define thread_pkey_regs_save(thread)
199+
#define thread_pkey_regs_restore(new_thread, old_thread)
200+
#define thread_pkey_regs_init(thread)
198201
#endif /* CONFIG_PPC_MEM_KEYS */
199202

200203
#endif /* __KERNEL__ */

arch/powerpc/include/asm/pkeys.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,8 @@ static inline int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
139139
}
140140

141141
extern void pkey_mm_init(struct mm_struct *mm);
142+
extern void thread_pkey_regs_save(struct thread_struct *thread);
143+
extern void thread_pkey_regs_restore(struct thread_struct *new_thread,
144+
struct thread_struct *old_thread);
145+
extern void thread_pkey_regs_init(struct thread_struct *thread);
142146
#endif /*_ASM_POWERPC_KEYS_H */

arch/powerpc/include/asm/processor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ struct thread_struct {
309309
struct thread_vr_state ckvr_state; /* Checkpointed VR state */
310310
unsigned long ckvrsave; /* Checkpointed VRSAVE */
311311
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
312+
#ifdef CONFIG_PPC_MEM_KEYS
313+
unsigned long amr;
314+
unsigned long iamr;
315+
unsigned long uamor;
316+
#endif
312317
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
313318
void* kvm_shadow_vcpu; /* KVM internal data */
314319
#endif /* CONFIG_KVM_BOOK3S_32_HANDLER */

arch/powerpc/kernel/process.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/hw_breakpoint.h>
4343
#include <linux/uaccess.h>
4444
#include <linux/elf-randomize.h>
45+
#include <linux/pkeys.h>
4546

4647
#include <asm/pgtable.h>
4748
#include <asm/io.h>
@@ -1103,6 +1104,8 @@ static inline void save_sprs(struct thread_struct *t)
11031104
t->tar = mfspr(SPRN_TAR);
11041105
}
11051106
#endif
1107+
1108+
thread_pkey_regs_save(t);
11061109
}
11071110

11081111
static inline void restore_sprs(struct thread_struct *old_thread,
@@ -1142,6 +1145,8 @@ static inline void restore_sprs(struct thread_struct *old_thread,
11421145
old_thread->tidr != new_thread->tidr)
11431146
mtspr(SPRN_TIDR, new_thread->tidr);
11441147
#endif
1148+
1149+
thread_pkey_regs_restore(new_thread, old_thread);
11451150
}
11461151

11471152
#ifdef CONFIG_PPC_BOOK3S_64
@@ -1867,6 +1872,8 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
18671872
current->thread.tm_tfiar = 0;
18681873
current->thread.load_tm = 0;
18691874
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
1875+
1876+
thread_pkey_regs_init(&current->thread);
18701877
}
18711878
EXPORT_SYMBOL(start_thread);
18721879

arch/powerpc/mm/pkeys.c

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ DEFINE_STATIC_KEY_TRUE(pkey_disabled);
1212
bool pkey_execute_disable_supported;
1313
int pkeys_total; /* Total pkeys as per device tree */
1414
u32 initial_allocation_mask; /* Bits set for reserved keys */
15+
u64 pkey_amr_uamor_mask; /* Bits in AMR/UMOR not to be touched */
16+
u64 pkey_iamr_mask; /* Bits in AMR not to be touched */
1517

1618
#define AMR_BITS_PER_PKEY 2
1719
#define AMR_RD_BIT 0x1UL
@@ -70,8 +72,16 @@ int pkey_initialize(void)
7072
* programming note.
7173
*/
7274
initial_allocation_mask = ~0x0;
73-
for (i = 2; i < (pkeys_total - os_reserved); i++)
75+
76+
/* register mask is in BE format */
77+
pkey_amr_uamor_mask = ~0x0ul;
78+
pkey_iamr_mask = ~0x0ul;
79+
80+
for (i = 2; i < (pkeys_total - os_reserved); i++) {
7481
initial_allocation_mask &= ~(0x1 << i);
82+
pkey_amr_uamor_mask &= ~(0x3ul << pkeyshift(i));
83+
pkey_iamr_mask &= ~(0x1ul << pkeyshift(i));
84+
}
7585
return 0;
7686
}
7787

@@ -206,3 +216,43 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
206216
init_amr(pkey, new_amr_bits);
207217
return 0;
208218
}
219+
220+
void thread_pkey_regs_save(struct thread_struct *thread)
221+
{
222+
if (static_branch_likely(&pkey_disabled))
223+
return;
224+
225+
/*
226+
* TODO: Skip saving registers if @thread hasn't used any keys yet.
227+
*/
228+
thread->amr = read_amr();
229+
thread->iamr = read_iamr();
230+
thread->uamor = read_uamor();
231+
}
232+
233+
void thread_pkey_regs_restore(struct thread_struct *new_thread,
234+
struct thread_struct *old_thread)
235+
{
236+
if (static_branch_likely(&pkey_disabled))
237+
return;
238+
239+
/*
240+
* TODO: Just set UAMOR to zero if @new_thread hasn't used any keys yet.
241+
*/
242+
if (old_thread->amr != new_thread->amr)
243+
write_amr(new_thread->amr);
244+
if (old_thread->iamr != new_thread->iamr)
245+
write_iamr(new_thread->iamr);
246+
if (old_thread->uamor != new_thread->uamor)
247+
write_uamor(new_thread->uamor);
248+
}
249+
250+
void thread_pkey_regs_init(struct thread_struct *thread)
251+
{
252+
if (static_branch_likely(&pkey_disabled))
253+
return;
254+
255+
write_amr(read_amr() & pkey_amr_uamor_mask);
256+
write_iamr(read_iamr() & pkey_iamr_mask);
257+
write_uamor(read_uamor() & pkey_amr_uamor_mask);
258+
}

0 commit comments

Comments
 (0)