162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifndef _PKEYS_POWERPC_H
462306a36Sopenharmony_ci#define _PKEYS_POWERPC_H
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#ifndef SYS_pkey_alloc
762306a36Sopenharmony_ci# define SYS_pkey_alloc		384
862306a36Sopenharmony_ci# define SYS_pkey_free		385
962306a36Sopenharmony_ci#endif
1062306a36Sopenharmony_ci#define REG_IP_IDX		PT_NIP
1162306a36Sopenharmony_ci#define REG_TRAPNO		PT_TRAP
1262306a36Sopenharmony_ci#define gregs			gp_regs
1362306a36Sopenharmony_ci#define fpregs			fp_regs
1462306a36Sopenharmony_ci#define si_pkey_offset		0x20
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#undef PKEY_DISABLE_ACCESS
1762306a36Sopenharmony_ci#define PKEY_DISABLE_ACCESS	0x3  /* disable read and write */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#undef PKEY_DISABLE_WRITE
2062306a36Sopenharmony_ci#define PKEY_DISABLE_WRITE	0x2
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define NR_PKEYS		32
2362306a36Sopenharmony_ci#define NR_RESERVED_PKEYS_4K	27 /* pkey-0, pkey-1, exec-only-pkey
2462306a36Sopenharmony_ci				      and 24 other keys that cannot be
2562306a36Sopenharmony_ci				      represented in the PTE */
2662306a36Sopenharmony_ci#define NR_RESERVED_PKEYS_64K_3KEYS	3 /* PowerNV and KVM: pkey-0,
2762306a36Sopenharmony_ci					     pkey-1 and exec-only key */
2862306a36Sopenharmony_ci#define NR_RESERVED_PKEYS_64K_4KEYS	4 /* PowerVM: pkey-0, pkey-1,
2962306a36Sopenharmony_ci					     pkey-31 and exec-only key */
3062306a36Sopenharmony_ci#define PKEY_BITS_PER_PKEY	2
3162306a36Sopenharmony_ci#define HPAGE_SIZE		(1UL << 24)
3262306a36Sopenharmony_ci#define PAGE_SIZE		sysconf(_SC_PAGESIZE)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline u32 pkey_bit_position(int pkey)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	return (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic inline u64 __read_pkey_reg(void)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	u64 pkey_reg;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	asm volatile("mfspr %0, 0xd" : "=r" (pkey_reg));
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return pkey_reg;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void __write_pkey_reg(u64 pkey_reg)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u64 amr = pkey_reg;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	dprintf4("%s() changing %016llx to %016llx\n",
5362306a36Sopenharmony_ci			 __func__, __read_pkey_reg(), pkey_reg);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	asm volatile("isync; mtspr 0xd, %0; isync"
5662306a36Sopenharmony_ci		     : : "r" ((unsigned long)(amr)) : "memory");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	dprintf4("%s() pkey register after changing %016llx to %016llx\n",
5962306a36Sopenharmony_ci			__func__, __read_pkey_reg(), pkey_reg);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic inline int cpu_has_pkeys(void)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	/* No simple way to determine this */
6562306a36Sopenharmony_ci	return 1;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic inline bool arch_is_powervm()
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct stat buf;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if ((stat("/sys/firmware/devicetree/base/ibm,partition-name", &buf) == 0) &&
7362306a36Sopenharmony_ci	    (stat("/sys/firmware/devicetree/base/hmc-managed?", &buf) == 0) &&
7462306a36Sopenharmony_ci	    (stat("/sys/firmware/devicetree/base/chosen/qemu,graphic-width", &buf) == -1) )
7562306a36Sopenharmony_ci		return true;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return false;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline int get_arch_reserved_keys(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	if (sysconf(_SC_PAGESIZE) == 4096)
8362306a36Sopenharmony_ci		return NR_RESERVED_PKEYS_4K;
8462306a36Sopenharmony_ci	else
8562306a36Sopenharmony_ci		if (arch_is_powervm())
8662306a36Sopenharmony_ci			return NR_RESERVED_PKEYS_64K_4KEYS;
8762306a36Sopenharmony_ci		else
8862306a36Sopenharmony_ci			return NR_RESERVED_PKEYS_64K_3KEYS;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_civoid expect_fault_on_read_execonly_key(void *p1, int pkey)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * powerpc does not allow userspace to change permissions of exec-only
9562306a36Sopenharmony_ci	 * keys since those keys are not allocated by userspace. The signal
9662306a36Sopenharmony_ci	 * handler wont be able to reset the permissions, which means the code
9762306a36Sopenharmony_ci	 * will infinitely continue to segfault here.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	return;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* 4-byte instructions * 16384 = 64K page */
10362306a36Sopenharmony_ci#define __page_o_noops() asm(".rept 16384 ; nop; .endr")
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid *malloc_pkey_with_mprotect_subpage(long size, int prot, u16 pkey)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	void *ptr;
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__,
11162306a36Sopenharmony_ci			size, prot, pkey);
11262306a36Sopenharmony_ci	pkey_assert(pkey < NR_PKEYS);
11362306a36Sopenharmony_ci	ptr = mmap(NULL, size, prot, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
11462306a36Sopenharmony_ci	pkey_assert(ptr != (void *)-1);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ret = syscall(__NR_subpage_prot, ptr, size, NULL);
11762306a36Sopenharmony_ci	if (ret) {
11862306a36Sopenharmony_ci		perror("subpage_perm");
11962306a36Sopenharmony_ci		return PTR_ERR_ENOTSUP;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = mprotect_pkey((void *)ptr, PAGE_SIZE, prot, pkey);
12362306a36Sopenharmony_ci	pkey_assert(!ret);
12462306a36Sopenharmony_ci	record_pkey_malloc(ptr, size, prot);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	dprintf1("%s() for pkey %d @ %p\n", __func__, pkey, ptr);
12762306a36Sopenharmony_ci	return ptr;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#endif /* _PKEYS_POWERPC_H */
131