|
@@ -131,9 +131,97 @@ static inline pud_t native_pudp_get_and_clear(pud_t *xp)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
|
|
|
+/*
|
|
|
+ * All top-level PAGE_TABLE_ISOLATION page tables are order-1 pages
|
|
|
+ * (8k-aligned and 8k in size). The kernel one is at the beginning 4k and
|
|
|
+ * the user one is in the last 4k. To switch between them, you
|
|
|
+ * just need to flip the 12th bit in their addresses.
|
|
|
+ */
|
|
|
+#define PTI_PGTABLE_SWITCH_BIT PAGE_SHIFT
|
|
|
+
|
|
|
+/*
|
|
|
+ * This generates better code than the inline assembly in
|
|
|
+ * __set_bit().
|
|
|
+ */
|
|
|
+static inline void *ptr_set_bit(void *ptr, int bit)
|
|
|
+{
|
|
|
+ unsigned long __ptr = (unsigned long)ptr;
|
|
|
+
|
|
|
+ __ptr |= BIT(bit);
|
|
|
+ return (void *)__ptr;
|
|
|
+}
|
|
|
+static inline void *ptr_clear_bit(void *ptr, int bit)
|
|
|
+{
|
|
|
+ unsigned long __ptr = (unsigned long)ptr;
|
|
|
+
|
|
|
+ __ptr &= ~BIT(bit);
|
|
|
+ return (void *)__ptr;
|
|
|
+}
|
|
|
+
|
|
|
+static inline pgd_t *kernel_to_user_pgdp(pgd_t *pgdp)
|
|
|
+{
|
|
|
+ return ptr_set_bit(pgdp, PTI_PGTABLE_SWITCH_BIT);
|
|
|
+}
|
|
|
+
|
|
|
+static inline pgd_t *user_to_kernel_pgdp(pgd_t *pgdp)
|
|
|
+{
|
|
|
+ return ptr_clear_bit(pgdp, PTI_PGTABLE_SWITCH_BIT);
|
|
|
+}
|
|
|
+
|
|
|
+static inline p4d_t *kernel_to_user_p4dp(p4d_t *p4dp)
|
|
|
+{
|
|
|
+ return ptr_set_bit(p4dp, PTI_PGTABLE_SWITCH_BIT);
|
|
|
+}
|
|
|
+
|
|
|
+static inline p4d_t *user_to_kernel_p4dp(p4d_t *p4dp)
|
|
|
+{
|
|
|
+ return ptr_clear_bit(p4dp, PTI_PGTABLE_SWITCH_BIT);
|
|
|
+}
|
|
|
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Page table pages are page-aligned. The lower half of the top
|
|
|
+ * level is used for userspace and the top half for the kernel.
|
|
|
+ *
|
|
|
+ * Returns true for parts of the PGD that map userspace and
|
|
|
+ * false for the parts that map the kernel.
|
|
|
+ */
|
|
|
+static inline bool pgdp_maps_userspace(void *__ptr)
|
|
|
+{
|
|
|
+ unsigned long ptr = (unsigned long)__ptr;
|
|
|
+
|
|
|
+ return (ptr & ~PAGE_MASK) < (PAGE_SIZE / 2);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
|
|
|
+pgd_t __pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Take a PGD location (pgdp) and a pgd value that needs to be set there.
|
|
|
+ * Populates the user and returns the resulting PGD that must be set in
|
|
|
+ * the kernel copy of the page tables.
|
|
|
+ */
|
|
|
+static inline pgd_t pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd)
|
|
|
+{
|
|
|
+ if (!static_cpu_has(X86_FEATURE_PTI))
|
|
|
+ return pgd;
|
|
|
+ return __pti_set_user_pgd(pgdp, pgd);
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline pgd_t pti_set_user_pgd(pgd_t *pgdp, pgd_t pgd)
|
|
|
+{
|
|
|
+ return pgd;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
static inline void native_set_p4d(p4d_t *p4dp, p4d_t p4d)
|
|
|
{
|
|
|
+#if defined(CONFIG_PAGE_TABLE_ISOLATION) && !defined(CONFIG_X86_5LEVEL)
|
|
|
+ p4dp->pgd = pti_set_user_pgd(&p4dp->pgd, p4d.pgd);
|
|
|
+#else
|
|
|
*p4dp = p4d;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
static inline void native_p4d_clear(p4d_t *p4d)
|
|
@@ -147,7 +235,11 @@ static inline void native_p4d_clear(p4d_t *p4d)
|
|
|
|
|
|
static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd)
|
|
|
{
|
|
|
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
|
|
|
+ *pgdp = pti_set_user_pgd(pgdp, pgd);
|
|
|
+#else
|
|
|
*pgdp = pgd;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
static inline void native_pgd_clear(pgd_t *pgd)
|