162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef _PKEYS_HELPER_H 362306a36Sopenharmony_ci#define _PKEYS_HELPER_H 462306a36Sopenharmony_ci#define _GNU_SOURCE 562306a36Sopenharmony_ci#include <string.h> 662306a36Sopenharmony_ci#include <stdarg.h> 762306a36Sopenharmony_ci#include <stdio.h> 862306a36Sopenharmony_ci#include <stdint.h> 962306a36Sopenharmony_ci#include <stdbool.h> 1062306a36Sopenharmony_ci#include <signal.h> 1162306a36Sopenharmony_ci#include <assert.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <ucontext.h> 1462306a36Sopenharmony_ci#include <sys/mman.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "../kselftest.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Define some kernel-like types */ 1962306a36Sopenharmony_ci#define u8 __u8 2062306a36Sopenharmony_ci#define u16 __u16 2162306a36Sopenharmony_ci#define u32 __u32 2262306a36Sopenharmony_ci#define u64 __u64 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define PTR_ERR_ENOTSUP ((void *)-ENOTSUP) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifndef DEBUG_LEVEL 2762306a36Sopenharmony_ci#define DEBUG_LEVEL 0 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci#define DPRINT_IN_SIGNAL_BUF_SIZE 4096 3062306a36Sopenharmony_ciextern int dprint_in_signal; 3162306a36Sopenharmony_ciextern char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE]; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciextern int test_nr; 3462306a36Sopenharmony_ciextern int iteration_nr; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#ifdef __GNUC__ 3762306a36Sopenharmony_ci__attribute__((format(printf, 1, 2))) 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_cistatic inline void sigsafe_printf(const char *format, ...) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci va_list ap; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!dprint_in_signal) { 4462306a36Sopenharmony_ci va_start(ap, format); 4562306a36Sopenharmony_ci vprintf(format, ap); 4662306a36Sopenharmony_ci va_end(ap); 4762306a36Sopenharmony_ci } else { 4862306a36Sopenharmony_ci int ret; 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * No printf() functions are signal-safe. 5162306a36Sopenharmony_ci * They deadlock easily. Write the format 5262306a36Sopenharmony_ci * string to get some output, even if 5362306a36Sopenharmony_ci * incomplete. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci ret = write(1, format, strlen(format)); 5662306a36Sopenharmony_ci if (ret < 0) 5762306a36Sopenharmony_ci exit(1); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci#define dprintf_level(level, args...) do { \ 6162306a36Sopenharmony_ci if (level <= DEBUG_LEVEL) \ 6262306a36Sopenharmony_ci sigsafe_printf(args); \ 6362306a36Sopenharmony_ci} while (0) 6462306a36Sopenharmony_ci#define dprintf0(args...) dprintf_level(0, args) 6562306a36Sopenharmony_ci#define dprintf1(args...) dprintf_level(1, args) 6662306a36Sopenharmony_ci#define dprintf2(args...) dprintf_level(2, args) 6762306a36Sopenharmony_ci#define dprintf3(args...) dprintf_level(3, args) 6862306a36Sopenharmony_ci#define dprintf4(args...) dprintf_level(4, args) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciextern void abort_hooks(void); 7162306a36Sopenharmony_ci#define pkey_assert(condition) do { \ 7262306a36Sopenharmony_ci if (!(condition)) { \ 7362306a36Sopenharmony_ci dprintf0("assert() at %s::%d test_nr: %d iteration: %d\n", \ 7462306a36Sopenharmony_ci __FILE__, __LINE__, \ 7562306a36Sopenharmony_ci test_nr, iteration_nr); \ 7662306a36Sopenharmony_ci dprintf0("errno at assert: %d", errno); \ 7762306a36Sopenharmony_ci abort_hooks(); \ 7862306a36Sopenharmony_ci exit(__LINE__); \ 7962306a36Sopenharmony_ci } \ 8062306a36Sopenharmony_ci} while (0) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci__attribute__((noinline)) int read_ptr(int *ptr); 8362306a36Sopenharmony_civoid expected_pkey_fault(int pkey); 8462306a36Sopenharmony_ciint sys_pkey_alloc(unsigned long flags, unsigned long init_val); 8562306a36Sopenharmony_ciint sys_pkey_free(unsigned long pkey); 8662306a36Sopenharmony_ciint mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot, 8762306a36Sopenharmony_ci unsigned long pkey); 8862306a36Sopenharmony_civoid record_pkey_malloc(void *ptr, long size, int prot); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) /* arch */ 9162306a36Sopenharmony_ci#include "pkey-x86.h" 9262306a36Sopenharmony_ci#elif defined(__powerpc64__) /* arch */ 9362306a36Sopenharmony_ci#include "pkey-powerpc.h" 9462306a36Sopenharmony_ci#else /* arch */ 9562306a36Sopenharmony_ci#error Architecture not supported 9662306a36Sopenharmony_ci#endif /* arch */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define PKEY_MASK (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 shift = pkey_bit_position(pkey); 10362306a36Sopenharmony_ci /* mask out bits from pkey in old value */ 10462306a36Sopenharmony_ci reg &= ~((u64)PKEY_MASK << shift); 10562306a36Sopenharmony_ci /* OR in new bits for pkey */ 10662306a36Sopenharmony_ci reg |= (flags & PKEY_MASK) << shift; 10762306a36Sopenharmony_ci return reg; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic inline u64 get_pkey_bits(u64 reg, int pkey) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u32 shift = pkey_bit_position(pkey); 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * shift down the relevant bits to the lowest two, then 11562306a36Sopenharmony_ci * mask off all the other higher bits 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci return ((reg >> shift) & PKEY_MASK); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciextern u64 shadow_pkey_reg; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline u64 _read_pkey_reg(int line) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci u64 pkey_reg = __read_pkey_reg(); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci dprintf4("read_pkey_reg(line=%d) pkey_reg: %016llx" 12762306a36Sopenharmony_ci " shadow: %016llx\n", 12862306a36Sopenharmony_ci line, pkey_reg, shadow_pkey_reg); 12962306a36Sopenharmony_ci assert(pkey_reg == shadow_pkey_reg); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return pkey_reg; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define read_pkey_reg() _read_pkey_reg(__LINE__) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void write_pkey_reg(u64 pkey_reg) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci dprintf4("%s() changing %016llx to %016llx\n", __func__, 13962306a36Sopenharmony_ci __read_pkey_reg(), pkey_reg); 14062306a36Sopenharmony_ci /* will do the shadow check for us: */ 14162306a36Sopenharmony_ci read_pkey_reg(); 14262306a36Sopenharmony_ci __write_pkey_reg(pkey_reg); 14362306a36Sopenharmony_ci shadow_pkey_reg = pkey_reg; 14462306a36Sopenharmony_ci dprintf4("%s(%016llx) pkey_reg: %016llx\n", __func__, 14562306a36Sopenharmony_ci pkey_reg, __read_pkey_reg()); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * These are technically racy. since something could 15062306a36Sopenharmony_ci * change PKEY register between the read and the write. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic inline void __pkey_access_allow(int pkey, int do_allow) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u64 pkey_reg = read_pkey_reg(); 15562306a36Sopenharmony_ci int bit = pkey * 2; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (do_allow) 15862306a36Sopenharmony_ci pkey_reg &= (1<<bit); 15962306a36Sopenharmony_ci else 16062306a36Sopenharmony_ci pkey_reg |= (1<<bit); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dprintf4("pkey_reg now: %016llx\n", read_pkey_reg()); 16362306a36Sopenharmony_ci write_pkey_reg(pkey_reg); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic inline void __pkey_write_allow(int pkey, int do_allow_write) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci u64 pkey_reg = read_pkey_reg(); 16962306a36Sopenharmony_ci int bit = pkey * 2 + 1; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (do_allow_write) 17262306a36Sopenharmony_ci pkey_reg &= (1<<bit); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci pkey_reg |= (1<<bit); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci write_pkey_reg(pkey_reg); 17762306a36Sopenharmony_ci dprintf4("pkey_reg now: %016llx\n", read_pkey_reg()); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) 18162306a36Sopenharmony_ci#define ALIGN_DOWN(x, align_to) ((x) & ~((align_to)-1)) 18262306a36Sopenharmony_ci#define ALIGN_PTR_UP(p, ptr_align_to) \ 18362306a36Sopenharmony_ci ((typeof(p))ALIGN_UP((unsigned long)(p), ptr_align_to)) 18462306a36Sopenharmony_ci#define ALIGN_PTR_DOWN(p, ptr_align_to) \ 18562306a36Sopenharmony_ci ((typeof(p))ALIGN_DOWN((unsigned long)(p), ptr_align_to)) 18662306a36Sopenharmony_ci#define __stringify_1(x...) #x 18762306a36Sopenharmony_ci#define __stringify(x...) __stringify_1(x) 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline u32 *siginfo_get_pkey_ptr(siginfo_t *si) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci#ifdef si_pkey 19262306a36Sopenharmony_ci return &si->si_pkey; 19362306a36Sopenharmony_ci#else 19462306a36Sopenharmony_ci return (u32 *)(((u8 *)si) + si_pkey_offset); 19562306a36Sopenharmony_ci#endif 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic inline int kernel_has_pkeys(void) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci /* try allocating a key and see if it succeeds */ 20162306a36Sopenharmony_ci int ret = sys_pkey_alloc(0, 0); 20262306a36Sopenharmony_ci if (ret <= 0) { 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci sys_pkey_free(ret); 20662306a36Sopenharmony_ci return 1; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline int is_pkeys_supported(void) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* check if the cpu supports pkeys */ 21262306a36Sopenharmony_ci if (!cpu_has_pkeys()) { 21362306a36Sopenharmony_ci dprintf1("SKIP: %s: no CPU support\n", __func__); 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* check if the kernel supports pkeys */ 21862306a36Sopenharmony_ci if (!kernel_has_pkeys()) { 21962306a36Sopenharmony_ci dprintf1("SKIP: %s: no kernel support\n", __func__); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 1; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#endif /* _PKEYS_HELPER_H */ 227