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