Commit f491fe3f authored by Aneesh Kumar K.V's avatar Aneesh Kumar K.V Committed by Michael Ellerman
Browse files

powerpc/book3s64/pkeys: Simplify the key initialization



Add documentation explaining the execute_only_key. The reservation and initialization mask
details are also explained in this patch.

No functional change in this patch.

Signed-off-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200709032946.881753-7-aneesh.kumar@linux.ibm.com
parent 1f404058
Loading
Loading
Loading
Loading
+116 −83
Original line number Diff line number Diff line
@@ -15,14 +15,27 @@
DEFINE_STATIC_KEY_TRUE(pkey_disabled);
int  pkeys_total;		/* Total pkeys as per device tree */
u32  initial_allocation_mask;   /* Bits set for the initially allocated keys */
u32  reserved_allocation_mask;  /* Bits set for reserved keys */
/*
 *  Keys marked in the reservation list cannot be allocated by  userspace
 */
u32  reserved_allocation_mask;
static bool pkey_execute_disable_supported;
static bool pkeys_devtree_defined;	/* property exported by device tree */
static u64 pkey_amr_mask;		/* Bits in AMR not to be touched */
static u64 pkey_iamr_mask;		/* Bits in AMR not to be touched */
static u64 pkey_uamor_mask;		/* Bits in UMOR not to be touched */
/*
 * Even if we allocate keys with sys_pkey_alloc(), we need to make sure
 * other thread still find the access denied using the same keys.
 */
static u64 default_amr = ~0x0UL;
static u64 default_iamr = 0x5555555555555555UL;

/* Allow all keys to be modified by default */
static u64 default_uamor = ~0x0UL;
/*
 * Key used to implement PROT_EXEC mmap. Denies READ/WRITE
 * We pick key 2 because 0 is special key and 1 is reserved as per ISA.
 */
static int execute_only_key = 2;


#define AMR_BITS_PER_PKEY 2
#define AMR_RD_BIT 0x1UL
#define AMR_WR_BIT 0x2UL
@@ -30,33 +43,52 @@ static int execute_only_key = 2;
#define PKEY_REG_BITS (sizeof(u64) * 8)
#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY))

static void scan_pkey_feature(void)
static int scan_pkey_feature(void)
{
	u32 vals[2];
	int pkeys_total = 0;
	struct device_node *cpu;

	/*
	 * Pkey is not supported with Radix translation.
	 */
	if (radix_enabled())
		return 0;

	cpu = of_find_node_by_type(NULL, "cpu");
	if (!cpu)
		return;
		return 0;

	if (of_property_read_u32_array(cpu,
			"ibm,processor-storage-keys", vals, 2))
		return;

				       "ibm,processor-storage-keys", vals, 2) == 0) {
		/*
	 * Since any pkey can be used for data or execute, we will just treat
	 * all keys as equal and track them as one entity.
		 * Since any pkey can be used for data or execute, we will
		 * just treat all keys as equal and track them as one entity.
		 */
		pkeys_total = vals[0];
	pkeys_devtree_defined = true;
	} else {

		/*
		 * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device
		 * tree. We make this exception since some version of skiboot forgot to
		 * expose this property on power8/9.
		 */
		if (!firmware_has_feature(FW_FEATURE_LPAR)) {
			unsigned long pvr = mfspr(SPRN_PVR);

			if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E ||
			    PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9)
				pkeys_total = 32;
		}
	}

static inline bool pkey_mmu_enabled(void)
{
	if (firmware_has_feature(FW_FEATURE_LPAR))
	/*
	 * Adjust the upper limit, based on the number of bits supported by
	 * arch-neutral code.
	 */
	pkeys_total = min_t(int, pkeys_total,
			    ((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1));
	return pkeys_total;
	else
		return cpu_has_feature(CPU_FTR_PKEY);
}

static int pkey_initialize(void)
@@ -80,35 +112,13 @@ static int pkey_initialize(void)
				!= (sizeof(u64) * BITS_PER_BYTE));

	/* scan the device tree for pkey feature */
	scan_pkey_feature();

	/*
	 * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device
	 * tree. We make this exception since some version of skiboot forgot to
	 * expose this property on power8/9.
	 */
	if (!pkeys_devtree_defined && !firmware_has_feature(FW_FEATURE_LPAR)) {
		unsigned long pvr = mfspr(SPRN_PVR);

		if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E ||
		    PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9)
			pkeys_total = 32;
	}

	/*
	 * Adjust the upper limit, based on the number of bits supported by
	 * arch-neutral code.
	 */
	pkeys_total = min_t(int, pkeys_total,
			((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT)+1));

	if (!pkey_mmu_enabled() || radix_enabled() || !pkeys_total)
		static_branch_enable(&pkey_disabled);
	else
	pkeys_total = scan_pkey_feature();
	if (pkeys_total)
		static_branch_disable(&pkey_disabled);

	if (static_branch_likely(&pkey_disabled))
	else {
		static_branch_enable(&pkey_disabled);
		return 0;
	}

	/*
	 * The device tree cannot be relied to indicate support for
@@ -122,47 +132,70 @@ static int pkey_initialize(void)
#ifdef CONFIG_PPC_4K_PAGES
	/*
	 * The OS can manage only 8 pkeys due to its inability to represent them
	 * in the Linux 4K PTE.
	 * in the Linux 4K PTE. Mark all other keys reserved.
	 */
	os_reserved = pkeys_total - 8;
#else
	os_reserved = 0;
#endif

	if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) {
		/*
	 * key 1 is recommended not to be used. PowerISA(3.0) page 1015,
	 * programming note.
		 * Insufficient number of keys to support
		 * execute only key. Mark it unavailable.
		 */
		execute_only_key = -1;
	} else {
		/*
		 * Mark the execute_only_pkey as not available for
		 * user allocation via pkey_alloc.
		 */
	reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key);
		reserved_allocation_mask |= (0x1 << execute_only_key);

	/* register mask is in BE format */
	pkey_amr_mask = ~0x0ul;
	pkey_amr_mask &= ~(0x3ul << pkeyshift(0));
		/*
		 * Deny READ/WRITE for execute_only_key.
		 * Allow execute in IAMR.
		 */
		default_amr  |= (0x3ul << pkeyshift(execute_only_key));
		default_iamr &= ~(0x1ul << pkeyshift(execute_only_key));

	pkey_iamr_mask = ~0x0ul;
	pkey_iamr_mask &= ~(0x3ul << pkeyshift(0));
	pkey_iamr_mask &= ~(0x3ul << pkeyshift(execute_only_key));
		/*
		 * Clear the uamor bits for this key.
		 */
		default_uamor &= ~(0x3ul << pkeyshift(execute_only_key));
	}

	pkey_uamor_mask = ~0x0ul;
	pkey_uamor_mask &= ~(0x3ul << pkeyshift(0));
	pkey_uamor_mask &= ~(0x3ul << pkeyshift(execute_only_key));
	/*
	 * Allow access for only key 0. And prevent any other modification.
	 */
	default_amr   &= ~(0x3ul << pkeyshift(0));
	default_iamr  &= ~(0x1ul << pkeyshift(0));
	default_uamor &= ~(0x3ul << pkeyshift(0));
	/*
	 * key 0 is special in that we want to consider it an allocated
	 * key which is preallocated. We don't allow changing AMR bits
	 * w.r.t key 0. But one can pkey_free(key0)
	 */
	initial_allocation_mask |= (0x1 << 0);

	/* mark the rest of the keys as reserved and hence unavailable */
	/*
	 * key 1 is recommended not to be used. PowerISA(3.0) page 1015,
	 * programming note.
	 */
	reserved_allocation_mask |= (0x1 << 1);

	/*
	 * Prevent the usage of OS reserved the keys. Update UAMOR
	 * for those keys.
	 */
	for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) {
		reserved_allocation_mask |= (0x1 << i);
		pkey_uamor_mask &= ~(0x3ul << pkeyshift(i));
		default_uamor &= ~(0x3ul << pkeyshift(i));
	}
	initial_allocation_mask = reserved_allocation_mask | (0x1 << 0);

	if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) {
	/*
		 * Insufficient number of keys to support
		 * execute only key. Mark it unavailable.
		 * Any AMR, UAMOR, IAMR bit set for
		 * this key is irrelevant since this key
		 * can never be allocated.
	 * Prevent the allocation of reserved keys too.
	 */
		execute_only_key = -1;
	}
	initial_allocation_mask |= reserved_allocation_mask;

	return 0;
}
@@ -305,13 +338,13 @@ void thread_pkey_regs_init(struct thread_struct *thread)
	if (static_branch_likely(&pkey_disabled))
		return;

	thread->amr = pkey_amr_mask;
	thread->iamr = pkey_iamr_mask;
	thread->uamor = pkey_uamor_mask;
	thread->amr   = default_amr;
	thread->iamr  = default_iamr;
	thread->uamor = default_uamor;

	write_uamor(pkey_uamor_mask);
	write_amr(pkey_amr_mask);
	write_iamr(pkey_iamr_mask);
	write_amr(default_amr);
	write_iamr(default_iamr);
	write_uamor(default_uamor);
}

int __execute_only_pkey(struct mm_struct *mm)