[PATCH v1 42/46] powerpc/8xx: Allow STRICT_KERNEL_RwX with pinned TLB

From: Christophe Leroy
Date: Mon Mar 16 2020 - 08:37:08 EST


Pinned TLB are 8M. Now that there is no strict boundary anymore
between text and RO data, it is possible to use 8M pinned executable
TLB that covers both text and RO data.

When PIN_TLB_DATA or PIN_TLB_TEXT is selected, enforce 8M RW data
alignment and allow STRICT_KERNEL_RWX.

Signed-off-by: Christophe Leroy <christophe.leroy@xxxxxx>
---
arch/powerpc/Kconfig | 8 +++++---
arch/powerpc/mm/nohash/8xx.c | 32 ++++++++++++++++++++++++++++++
arch/powerpc/platforms/8xx/Kconfig | 2 +-
3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 66d02667b43d..305c7b6a9229 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -775,9 +775,10 @@ config THREAD_SHIFT
want. Only change this if you know what you are doing.

config DATA_SHIFT_BOOL
- bool "Set custom data alignment" if STRICT_KERNEL_RWX && \
- (PPC_BOOK3S_32 || PPC_8xx)
+ bool "Set custom data alignment"
depends on ADVANCED_OPTIONS
+ depends on STRICT_KERNEL_RWX
+ depends on PPC_BOOK3S_32 || (PPC_8xx && !PIN_TLB_DATA && !PIN_TLB_TEXT)
help
This option allows you to set the kernel data alignment. When
RAM is mapped by blocks, the alignment needs to fit the size and
@@ -799,7 +800,8 @@ config DATA_SHIFT

On 8xx, large pages (512kb or 8M) are used to map kernel linear
memory. Aligning to 8M reduces TLB misses as only 8M pages are used
- in that case.
+ in that case. If PIN_TLB is selected, it must be aligned to 8M as
+ 8M pages will be pinned.

config FORCE_MAX_ZONEORDER
int "Maximum zone order"
diff --git a/arch/powerpc/mm/nohash/8xx.c b/arch/powerpc/mm/nohash/8xx.c
index d670f3f82091..40815eba96f2 100644
--- a/arch/powerpc/mm/nohash/8xx.c
+++ b/arch/powerpc/mm/nohash/8xx.c
@@ -171,6 +171,20 @@ unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top)
return top;
}

+static void mmu_pin_text(unsigned long boundary)
+{
+ unsigned long addr = PAGE_OFFSET;
+ unsigned long twc = MD_SVALID | MD_PS8MEG;
+ unsigned long rpn = __pa(addr) | 0xf0 | _PAGE_RO |
+ _PAGE_SPS | _PAGE_SH | _PAGE_PRESENT;
+ int i;
+
+ for (i = 28; i < 32 && __pa(addr) < boundary; i++, addr += SZ_8M, rpn += SZ_8M)
+ mpc8xx_update_tlb(0, i, addr | MI_EVALID, twc, rpn);
+ for (; i < 32; i++)
+ mpc8xx_update_tlb(0, i, 0, 0, 0);
+}
+
void mmu_mark_initmem_nx(void)
{
unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M);
@@ -180,14 +194,32 @@ void mmu_mark_initmem_nx(void)

mmu_mapin_ram_chunk(0, boundary, PAGE_KERNEL_TEXT, false);
mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL, false);
+
+ if (IS_ENABLED(CONFIG_PIN_TLB_TEXT))
+ mmu_pin_text(boundary);
}

#ifdef CONFIG_STRICT_KERNEL_RWX
+static void mmu_pin_data(unsigned long sinittext)
+{
+ unsigned long addr = PAGE_OFFSET;
+ unsigned long twc = MD_SVALID | MD_PS8MEG;
+ unsigned long rpn = __pa(addr) | 0xf0 | _PAGE_RO |
+ _PAGE_SPS | _PAGE_SH | _PAGE_PRESENT;
+ int i;
+ int max = IS_ENABLED(CONFIG_PIN_TLB_IMMR) ? 30 : 31;
+
+ for (i = 28; i <= max && __pa(addr) < sinittext; i++, addr += SZ_8M, rpn += SZ_8M)
+ mpc8xx_update_tlb(0, i, addr | MI_EVALID, twc, rpn);
+}
+
void mmu_mark_rodata_ro(void)
{
unsigned long sinittext = __pa(_sinittext);

mmu_mapin_ram_chunk(0, sinittext, PAGE_KERNEL_ROX, false);
+ if (IS_ENABLED(CONFIG_PIN_TLB_DATA))
+ mmu_pin_data(sinittext);
}
#endif

diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig
index 04ea1a8a0bdc..05669f2fadce 100644
--- a/arch/powerpc/platforms/8xx/Kconfig
+++ b/arch/powerpc/platforms/8xx/Kconfig
@@ -167,7 +167,7 @@ menu "8xx advanced setup"

config PIN_TLB
bool "Pinned Kernel TLBs"
- depends on ADVANCED_OPTIONS && !DEBUG_PAGEALLOC && !STRICT_KERNEL_RWX
+ depends on ADVANCED_OPTIONS && !DEBUG_PAGEALLOC
help
On the 8xx, we have 32 instruction TLBs and 32 data TLBs. In each
table 4 TLBs can be pinned.
--
2.25.0