Skip to content

Commit 61e9b36

Browse files
hansendcIngo Molnar
authored andcommitted
x86/mm/pti: Add mapping helper functions
Add the pagetable helper functions do manage the separate user space page tables. [ tglx: Split out from the big combo kaiser patch. Folded Andys simplification and made it out of line as Boris suggested ] Signed-off-by: Dave Hansen <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Boris Ostrovsky <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: David Laight <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: Eduardo Valentin <[email protected]> Cc: Greg KH <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Juergen Gross <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Will Deacon <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 41f4c20 commit 61e9b36

File tree

3 files changed

+138
-1
lines changed

3 files changed

+138
-1
lines changed

arch/x86/include/asm/pgtable.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,11 @@ static inline int pgd_none(pgd_t pgd)
909909
* pgd_offset() returns a (pgd_t *)
910910
* pgd_index() is used get the offset into the pgd page's array of pgd_t's;
911911
*/
912-
#define pgd_offset(mm, address) ((mm)->pgd + pgd_index((address)))
912+
#define pgd_offset_pgd(pgd, address) (pgd + pgd_index((address)))
913+
/*
914+
* a shortcut to get a pgd_t in a given mm
915+
*/
916+
#define pgd_offset(mm, address) pgd_offset_pgd((mm)->pgd, (address))
913917
/*
914918
* a shortcut which implies the use of the kernel's pgd, instead
915919
* of a process's

arch/x86/include/asm/pgtable_64.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,97 @@ static inline pud_t native_pudp_get_and_clear(pud_t *xp)
131131
#endif
132132
}
133133

134+
#ifdef CONFIG_PAGE_TABLE_ISOLATION
135+
/*
136+
* All top-level PAGE_TABLE_ISOLATION page tables are order-1 pages
137+
* (8k-aligned and 8k in size). The kernel one is at the beginning 4k and
138+
* the user one is in the last 4k. To switch between them, you
139+
* just need to flip the 12th bit in their addresses.
140+
*/
141+
#define PTI_PGTABLE_SWITCH_BIT PAGE_SHIFT
142+
143+
/*
144+
* This generates better code than the inline assembly in
145+
* __set_bit().
146+
*/
147+
static inline void *ptr_set_bit(void *ptr, int bit)
148+
{
149+
unsigned long __ptr = (unsigned long)ptr;
150+
151+
__ptr |= BIT(bit);
152+
return (void *)__ptr;
153+
}
154+
static inline void *ptr_clear_bit(void *ptr, int bit)
155+
{
156+
unsigned long __ptr = (unsigned long)ptr;
157+
158+
__ptr &= ~BIT(bit);
159+
return (void *)__ptr;
160+
}
161+
162+
static inline pgd_t *kernel_to_user_pgdp(pgd_t *pgdp)
163+
{
164+
return ptr_set_bit(pgdp, PTI_PGTABLE_SWITCH_BIT);
165+
}
166+
167+
static inline pgd_t *user_to_kernel_pgdp(pgd_t *pgdp)
168+
{
169+
return ptr_clear_bit(pgdp, PTI_PGTABLE_SWITCH_BIT);
170+
}
171+
172+
static inline p4d_t *kernel_to_user_p4dp(p4d_t *p4dp)
173+
{
174+
return ptr_set_bit(p4dp, PTI_PGTABLE_SWITCH_BIT);
175+
}
176+
177+
static inline p4d_t *user_to_kernel_p4dp(p4d_t *p4dp)
178+
{
179+
return ptr_clear_bit(p4dp, PTI_PGTABLE_SWITCH_BIT);
180+
}
181+
#endif /* CONFIG_PAGE_TABLE_ISOLATION */
182+
183+
/*
184+
* Page table pages are page-aligned. The lower half of the top
185+
* level is used for userspace and the top half for the kernel.
186+
*
187+
* Returns true for parts of the PGD that map userspace and
188+
* false for the parts that map the kernel.
189+
*/
190+
static inline bool pgdp_maps_userspace(void *__ptr)
191+
{
192+
unsigned long ptr = (unsigned long)__ptr;
193+
194+
return (ptr & ~PAGE_MASK) < (PAGE_SIZE / 2);
195+
}
196+
197+
#ifdef CONFIG_PAGE_TABLE_ISOLATION
198+
pgd_t __pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd);
199+
200+
/*
201+
* Take a PGD location (pgdp) and a pgd value that needs to be set there.
202+
* Populates the user and returns the resulting PGD that must be set in
203+
* the kernel copy of the page tables.
204+
*/
205+
static inline pgd_t pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd)
206+
{
207+
if (!static_cpu_has(X86_FEATURE_PTI))
208+
return pgd;
209+
return __pti_set_user_pgd(pgdp, pgd);
210+
}
211+
#else
212+
static inline pgd_t pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd)
213+
{
214+
return pgd;
215+
}
216+
#endif
217+
134218
static inline void native_set_p4d(p4d_t *p4dp, p4d_t p4d)
135219
{
220+
#if defined(CONFIG_PAGE_TABLE_ISOLATION) && !defined(CONFIG_X86_5LEVEL)
221+
p4dp->pgd = pti_set_user_pgd(&p4dp->pgd, p4d.pgd);
222+
#else
136223
*p4dp = p4d;
224+
#endif
137225
}
138226

139227
static inline void native_p4d_clear(p4d_t *p4d)
@@ -147,7 +235,11 @@ static inline void native_p4d_clear(p4d_t *p4d)
147235

148236
static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd)
149237
{
238+
#ifdef CONFIG_PAGE_TABLE_ISOLATION
239+
*pgdp = pti_set_user_pgd(pgdp, pgd);
240+
#else
150241
*pgdp = pgd;
242+
#endif
151243
}
152244

153245
static inline void native_pgd_clear(pgd_t *pgd)

arch/x86/mm/pti.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,47 @@ void __init pti_check_boottime_disable(void)
9696
setup_force_cpu_cap(X86_FEATURE_PTI);
9797
}
9898

99+
pgd_t __pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd)
100+
{
101+
/*
102+
* Changes to the high (kernel) portion of the kernelmode page
103+
* tables are not automatically propagated to the usermode tables.
104+
*
105+
* Users should keep in mind that, unlike the kernelmode tables,
106+
* there is no vmalloc_fault equivalent for the usermode tables.
107+
* Top-level entries added to init_mm's usermode pgd after boot
108+
* will not be automatically propagated to other mms.
109+
*/
110+
if (!pgdp_maps_userspace(pgdp))
111+
return pgd;
112+
113+
/*
114+
* The user page tables get the full PGD, accessible from
115+
* userspace:
116+
*/
117+
kernel_to_user_pgdp(pgdp)->pgd = pgd.pgd;
118+
119+
/*
120+
* If this is normal user memory, make it NX in the kernel
121+
* pagetables so that, if we somehow screw up and return to
122+
* usermode with the kernel CR3 loaded, we'll get a page fault
123+
* instead of allowing user code to execute with the wrong CR3.
124+
*
125+
* As exceptions, we don't set NX if:
126+
* - _PAGE_USER is not set. This could be an executable
127+
* EFI runtime mapping or something similar, and the kernel
128+
* may execute from it
129+
* - we don't have NX support
130+
* - we're clearing the PGD (i.e. the new pgd is not present).
131+
*/
132+
if ((pgd.pgd & (_PAGE_USER|_PAGE_PRESENT)) == (_PAGE_USER|_PAGE_PRESENT) &&
133+
(__supported_pte_mask & _PAGE_NX))
134+
pgd.pgd |= _PAGE_NX;
135+
136+
/* return the copy of the PGD we want the kernel to use: */
137+
return pgd;
138+
}
139+
99140
/*
100141
* Initialize kernel page table isolation
101142
*/

0 commit comments

Comments
 (0)