Implement execute-only mappings by using the Virtual Page Class Key
authorkettenis <kettenis@openbsd.org>
Wed, 25 Jan 2023 09:53:53 +0000 (09:53 +0000)
committerkettenis <kettenis@openbsd.org>
Wed, 25 Jan 2023 09:53:53 +0000 (09:53 +0000)
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
sys/arch/powerpc64/include/pte.h
sys/arch/powerpc64/powerpc64/cpu.c
sys/arch/powerpc64/powerpc64/pmap.c

index d2ba98b..ed41ce2 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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)
 {
index 875b38a..d22ea49 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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
index bffdb01..f8f1074 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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
index 2fc2a10..a5615dc 100644 (file)
@@ -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));