linux/tools/testing/selftests/vm/pkey-helpers.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#ifndef _PKEYS_HELPER_H
   3#define _PKEYS_HELPER_H
   4#define _GNU_SOURCE
   5#include <string.h>
   6#include <stdarg.h>
   7#include <stdio.h>
   8#include <stdint.h>
   9#include <stdbool.h>
  10#include <signal.h>
  11#include <assert.h>
  12#include <stdlib.h>
  13#include <ucontext.h>
  14#include <sys/mman.h>
  15
  16/* Define some kernel-like types */
  17#define  u8 __u8
  18#define u16 __u16
  19#define u32 __u32
  20#define u64 __u64
  21
  22#define PTR_ERR_ENOTSUP ((void *)-ENOTSUP)
  23
  24#ifndef DEBUG_LEVEL
  25#define DEBUG_LEVEL 0
  26#endif
  27#define DPRINT_IN_SIGNAL_BUF_SIZE 4096
  28extern int dprint_in_signal;
  29extern char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE];
  30
  31extern int test_nr;
  32extern int iteration_nr;
  33
  34#ifdef __GNUC__
  35__attribute__((format(printf, 1, 2)))
  36#endif
  37static inline void sigsafe_printf(const char *format, ...)
  38{
  39        va_list ap;
  40
  41        if (!dprint_in_signal) {
  42                va_start(ap, format);
  43                vprintf(format, ap);
  44                va_end(ap);
  45        } else {
  46                int ret;
  47                /*
  48                 * No printf() functions are signal-safe.
  49                 * They deadlock easily. Write the format
  50                 * string to get some output, even if
  51                 * incomplete.
  52                 */
  53                ret = write(1, format, strlen(format));
  54                if (ret < 0)
  55                        exit(1);
  56        }
  57}
  58#define dprintf_level(level, args...) do {      \
  59        if (level <= DEBUG_LEVEL)               \
  60                sigsafe_printf(args);           \
  61} while (0)
  62#define dprintf0(args...) dprintf_level(0, args)
  63#define dprintf1(args...) dprintf_level(1, args)
  64#define dprintf2(args...) dprintf_level(2, args)
  65#define dprintf3(args...) dprintf_level(3, args)
  66#define dprintf4(args...) dprintf_level(4, args)
  67
  68extern void abort_hooks(void);
  69#define pkey_assert(condition) do {             \
  70        if (!(condition)) {                     \
  71                dprintf0("assert() at %s::%d test_nr: %d iteration: %d\n", \
  72                                __FILE__, __LINE__,     \
  73                                test_nr, iteration_nr); \
  74                dprintf0("errno at assert: %d", errno); \
  75                abort_hooks();                  \
  76                exit(__LINE__);                 \
  77        }                                       \
  78} while (0)
  79
  80__attribute__((noinline)) int read_ptr(int *ptr);
  81void expected_pkey_fault(int pkey);
  82int sys_pkey_alloc(unsigned long flags, unsigned long init_val);
  83int sys_pkey_free(unsigned long pkey);
  84int mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot,
  85                unsigned long pkey);
  86void record_pkey_malloc(void *ptr, long size, int prot);
  87
  88#if defined(__i386__) || defined(__x86_64__) /* arch */
  89#include "pkey-x86.h"
  90#elif defined(__powerpc64__) /* arch */
  91#include "pkey-powerpc.h"
  92#else /* arch */
  93#error Architecture not supported
  94#endif /* arch */
  95
  96#define PKEY_MASK       (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)
  97
  98static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
  99{
 100        u32 shift = pkey_bit_position(pkey);
 101        /* mask out bits from pkey in old value */
 102        reg &= ~((u64)PKEY_MASK << shift);
 103        /* OR in new bits for pkey */
 104        reg |= (flags & PKEY_MASK) << shift;
 105        return reg;
 106}
 107
 108static inline u64 get_pkey_bits(u64 reg, int pkey)
 109{
 110        u32 shift = pkey_bit_position(pkey);
 111        /*
 112         * shift down the relevant bits to the lowest two, then
 113         * mask off all the other higher bits
 114         */
 115        return ((reg >> shift) & PKEY_MASK);
 116}
 117
 118extern u64 shadow_pkey_reg;
 119
 120static inline u64 _read_pkey_reg(int line)
 121{
 122        u64 pkey_reg = __read_pkey_reg();
 123
 124        dprintf4("read_pkey_reg(line=%d) pkey_reg: %016llx"
 125                        " shadow: %016llx\n",
 126                        line, pkey_reg, shadow_pkey_reg);
 127        assert(pkey_reg == shadow_pkey_reg);
 128
 129        return pkey_reg;
 130}
 131
 132#define read_pkey_reg() _read_pkey_reg(__LINE__)
 133
 134static inline void write_pkey_reg(u64 pkey_reg)
 135{
 136        dprintf4("%s() changing %016llx to %016llx\n", __func__,
 137                        __read_pkey_reg(), pkey_reg);
 138        /* will do the shadow check for us: */
 139        read_pkey_reg();
 140        __write_pkey_reg(pkey_reg);
 141        shadow_pkey_reg = pkey_reg;
 142        dprintf4("%s(%016llx) pkey_reg: %016llx\n", __func__,
 143                        pkey_reg, __read_pkey_reg());
 144}
 145
 146/*
 147 * These are technically racy. since something could
 148 * change PKEY register between the read and the write.
 149 */
 150static inline void __pkey_access_allow(int pkey, int do_allow)
 151{
 152        u64 pkey_reg = read_pkey_reg();
 153        int bit = pkey * 2;
 154
 155        if (do_allow)
 156                pkey_reg &= (1<<bit);
 157        else
 158                pkey_reg |= (1<<bit);
 159
 160        dprintf4("pkey_reg now: %016llx\n", read_pkey_reg());
 161        write_pkey_reg(pkey_reg);
 162}
 163
 164static inline void __pkey_write_allow(int pkey, int do_allow_write)
 165{
 166        u64 pkey_reg = read_pkey_reg();
 167        int bit = pkey * 2 + 1;
 168
 169        if (do_allow_write)
 170                pkey_reg &= (1<<bit);
 171        else
 172                pkey_reg |= (1<<bit);
 173
 174        write_pkey_reg(pkey_reg);
 175        dprintf4("pkey_reg now: %016llx\n", read_pkey_reg());
 176}
 177
 178#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
 179#define ALIGN_UP(x, align_to)   (((x) + ((align_to)-1)) & ~((align_to)-1))
 180#define ALIGN_DOWN(x, align_to) ((x) & ~((align_to)-1))
 181#define ALIGN_PTR_UP(p, ptr_align_to)   \
 182        ((typeof(p))ALIGN_UP((unsigned long)(p), ptr_align_to))
 183#define ALIGN_PTR_DOWN(p, ptr_align_to) \
 184        ((typeof(p))ALIGN_DOWN((unsigned long)(p), ptr_align_to))
 185#define __stringify_1(x...)     #x
 186#define __stringify(x...)       __stringify_1(x)
 187
 188static inline u32 *siginfo_get_pkey_ptr(siginfo_t *si)
 189{
 190#ifdef si_pkey
 191        return &si->si_pkey;
 192#else
 193        return (u32 *)(((u8 *)si) + si_pkey_offset);
 194#endif
 195}
 196
 197static inline int kernel_has_pkeys(void)
 198{
 199        /* try allocating a key and see if it succeeds */
 200        int ret = sys_pkey_alloc(0, 0);
 201        if (ret <= 0) {
 202                return 0;
 203        }
 204        sys_pkey_free(ret);
 205        return 1;
 206}
 207
 208static inline int is_pkeys_supported(void)
 209{
 210        /* check if the cpu supports pkeys */
 211        if (!cpu_has_pkeys()) {
 212                dprintf1("SKIP: %s: no CPU support\n", __func__);
 213                return 0;
 214        }
 215
 216        /* check if the kernel supports pkeys */
 217        if (!kernel_has_pkeys()) {
 218                dprintf1("SKIP: %s: no kernel support\n", __func__);
 219                return 0;
 220        }
 221
 222        return 1;
 223}
 224
 225#endif /* _PKEYS_HELPER_H */
 226