| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Copyright (C) 2023 Arm Ltd. |
| */ |
| |
| #ifndef _PKEYS_ARM64_H |
| #define _PKEYS_ARM64_H |
| |
| #include "vm_util.h" |
| /* for signal frame parsing */ |
| #include "../arm64/signal/testcases/testcases.h" |
| |
| #ifndef SYS_mprotect_key |
| # define SYS_mprotect_key 288 |
| #endif |
| #ifndef SYS_pkey_alloc |
| # define SYS_pkey_alloc 289 |
| # define SYS_pkey_free 290 |
| #endif |
| #define MCONTEXT_IP(mc) mc.pc |
| #define MCONTEXT_TRAPNO(mc) -1 |
| |
| #define PKEY_MASK 0xf |
| |
| #define POE_NONE 0x0 |
| #define POE_X 0x2 |
| #define POE_RX 0x3 |
| #define POE_RWX 0x7 |
| |
| #define NR_PKEYS 8 |
| #define NR_RESERVED_PKEYS 1 /* pkey-0 */ |
| |
| #define PKEY_ALLOW_ALL 0x77777777 |
| |
| #define PKEY_BITS_PER_PKEY 4 |
| #define PAGE_SIZE sysconf(_SC_PAGESIZE) |
| #undef HPAGE_SIZE |
| #define HPAGE_SIZE default_huge_page_size() |
| |
| /* 4-byte instructions * 16384 = 64K page */ |
| #define __page_o_noops() asm(".rept 16384 ; nop; .endr") |
| |
| static inline u64 __read_pkey_reg(void) |
| { |
| u64 pkey_reg = 0; |
| |
| // POR_EL0 |
| asm volatile("mrs %0, S3_3_c10_c2_4" : "=r" (pkey_reg)); |
| |
| return pkey_reg; |
| } |
| |
| static inline void __write_pkey_reg(u64 pkey_reg) |
| { |
| u64 por = pkey_reg; |
| |
| dprintf4("%s() changing %016llx to %016llx\n", |
| __func__, __read_pkey_reg(), pkey_reg); |
| |
| // POR_EL0 |
| asm volatile("msr S3_3_c10_c2_4, %0\nisb" :: "r" (por) :); |
| |
| dprintf4("%s() pkey register after changing %016llx to %016llx\n", |
| __func__, __read_pkey_reg(), pkey_reg); |
| } |
| |
| static inline int cpu_has_pkeys(void) |
| { |
| /* No simple way to determine this */ |
| return 1; |
| } |
| |
| static inline u32 pkey_bit_position(int pkey) |
| { |
| return pkey * PKEY_BITS_PER_PKEY; |
| } |
| |
| static inline int get_arch_reserved_keys(void) |
| { |
| return NR_RESERVED_PKEYS; |
| } |
| |
| void expect_fault_on_read_execonly_key(void *p1, int pkey) |
| { |
| } |
| |
| void *malloc_pkey_with_mprotect_subpage(long size, int prot, u16 pkey) |
| { |
| return PTR_ERR_ENOTSUP; |
| } |
| |
| #define set_pkey_bits set_pkey_bits |
| static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags) |
| { |
| u32 shift = pkey_bit_position(pkey); |
| u64 new_val = POE_RWX; |
| |
| /* mask out bits from pkey in old value */ |
| reg &= ~((u64)PKEY_MASK << shift); |
| |
| if (flags & PKEY_DISABLE_ACCESS) |
| new_val = POE_X; |
| else if (flags & PKEY_DISABLE_WRITE) |
| new_val = POE_RX; |
| |
| /* OR in new bits for pkey */ |
| reg |= new_val << shift; |
| |
| return reg; |
| } |
| |
| #define get_pkey_bits get_pkey_bits |
| static inline u64 get_pkey_bits(u64 reg, int pkey) |
| { |
| u32 shift = pkey_bit_position(pkey); |
| /* |
| * shift down the relevant bits to the lowest four, then |
| * mask off all the other higher bits |
| */ |
| u32 perm = (reg >> shift) & PKEY_MASK; |
| |
| if (perm == POE_X) |
| return PKEY_DISABLE_ACCESS; |
| if (perm == POE_RX) |
| return PKEY_DISABLE_WRITE; |
| return 0; |
| } |
| |
| static void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey) |
| { |
| struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt); |
| struct poe_context *poe_ctx = |
| (struct poe_context *) get_header(ctx, POE_MAGIC, |
| sizeof(uctxt->uc_mcontext), NULL); |
| if (poe_ctx) |
| poe_ctx->por_el0 = pkey; |
| } |
| |
| #endif /* _PKEYS_ARM64_H */ |