From 6bd9427e6879f79e0e2c1e03d8411439da5bb692 Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 25 Jan 2023 09:53:53 +0000 Subject: [PATCH] Implement execute-only mappings by using the Virtual Page Class Key Protection mechanism provided by modern POWER CPUs. This is implemented in a way data allows us to use the Data Address Compare mechanism that was available on older versions of the architecture if we ever add support for these older CPUs (e.g. the PowerPC 970 aka G5). Special thanks to gkoehler@ for spotting the bug in my initial implementation that made this not work at all. ok deraadt@, gkoehler@ --- sys/arch/powerpc64/include/cpufunc.h | 20 +++++++++++++++++++- sys/arch/powerpc64/include/pte.h | 3 ++- sys/arch/powerpc64/powerpc64/cpu.c | 19 ++++++++++++++++++- sys/arch/powerpc64/powerpc64/pmap.c | 8 +++++++- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/sys/arch/powerpc64/include/cpufunc.h b/sys/arch/powerpc64/include/cpufunc.h index d2ba98b630f..ed41ce2357a 100644 --- a/sys/arch/powerpc64/include/cpufunc.h +++ b/sys/arch/powerpc64/include/cpufunc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpufunc.h,v 1.10 2020/12/30 06:06:30 gkoehler Exp $ */ +/* $OpenBSD: cpufunc.h,v 1.11 2023/01/25 09:53:53 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -137,12 +137,24 @@ mtsdr1(uint64_t value) __asm volatile ("mtsdr1 %0" :: "r"(value)); } +static inline void +mtamr(uint64_t value) +{ + __asm volatile ("mtspr 29, %0" :: "r"(value)); +} + static inline void mtfscr(uint64_t value) { __asm volatile ("mtspr 153, %0" :: "r"(value)); } +static inline void +mtuamor(uint64_t value) +{ + __asm volatile ("mtspr 157, %0" :: "r"(value)); +} + static inline uint32_t mfpvr(void) { @@ -169,6 +181,12 @@ mtlpcr(uint64_t value) #define LPCR_LPES 0x0000000000000008UL #define LPCR_HVICE 0x0000000000000002UL +static inline void +mtamor(uint64_t value) +{ + __asm volatile ("mtspr 349, %0" :: "r"(value)); +} + static inline void mtptcr(uint64_t value) { diff --git a/sys/arch/powerpc64/include/pte.h b/sys/arch/powerpc64/include/pte.h index 875b38afd19..d22ea49b25f 100644 --- a/sys/arch/powerpc64/include/pte.h +++ b/sys/arch/powerpc64/include/pte.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pte.h,v 1.7 2020/09/07 18:51:47 kettenis Exp $ */ +/* $OpenBSD: pte.h,v 1.8 2023/01/25 09:53:53 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -47,6 +47,7 @@ struct pte { #define PTE_W 0x0000000000000040ULL #define PTE_CHG 0x0000000000000080ULL #define PTE_REF 0x0000000000000100ULL +#define PTE_AC 0x0000000000000200ULL #define PTE_RPGN 0x0ffffffffffff000ULL #define ADDR_PIDX 0x000000000ffff000ULL diff --git a/sys/arch/powerpc64/powerpc64/cpu.c b/sys/arch/powerpc64/powerpc64/cpu.c index bffdb01ba79..f8f10741264 100644 --- a/sys/arch/powerpc64/powerpc64/cpu.c +++ b/sys/arch/powerpc64/powerpc64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.24 2022/04/06 18:59:27 naddy Exp $ */ +/* $OpenBSD: cpu.c,v 1.25 2023/01/25 09:53:53 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -243,6 +243,23 @@ cpu_init(void) mtfscr(0); isync(); + + /* + * Set AMR to inhibit loads and stores for all virtual page + * class keys, except for Key0 which is used for normal kernel + * access. This means we can pick any other key to implement + * execute-only mappings. But we pick Key1 since that allows + * us to use the same bit in the PTE as was used to enable the + * Data Access Compare mechanism on CPUs based on older + * versions of the architecture (such as the PowerPC 970). + * + * Set UAMOR (and AMOR just to be safe) to zero to prevent + * userland from modifying any bits in AMR. + */ + mtamr(0x3fffffffffffffff); + mtuamor(0); + mtamor(0); + isync(); } void diff --git a/sys/arch/powerpc64/powerpc64/pmap.c b/sys/arch/powerpc64/powerpc64/pmap.c index 2fc2a10b378..a5615dc8bda 100644 --- a/sys/arch/powerpc64/powerpc64/pmap.c +++ b/sys/arch/powerpc64/powerpc64/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.58 2022/09/10 20:35:28 miod Exp $ */ +/* $OpenBSD: pmap.c,v 1.59 2023/01/25 09:53:53 kettenis Exp $ */ /* * Copyright (c) 2015 Martin Pieuchot @@ -738,6 +738,9 @@ pmap_fill_pte(pmap_t pm, vaddr_t va, paddr_t pa, struct pte_desc *pted, pte->pte_lo |= PTE_M; else pte->pte_lo |= (PTE_M | PTE_I | PTE_G); + + if ((prot & (PROT_READ | PROT_WRITE)) == 0) + pte->pte_lo |= PTE_AC; } void @@ -1195,6 +1198,9 @@ pmap_pted_ro(struct pte_desc *pted, vm_prot_t prot) if ((prot & PROT_EXEC) == 0) pted->pted_pte.pte_lo |= PTE_N; + if ((prot & (PROT_READ | PROT_WRITE)) == 0) + pted->pted_pte.pte_lo |= PTE_AC; + PMAP_HASH_LOCK(s); if ((pte = pmap_ptedinhash(pted)) != NULL) { pte_del(pte, pmap_pted2ava(pted)); -- 2.20.1