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: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20200709032946.881753-7-aneesh.kumar@linux.ibm.com
This commit is contained in:
parent
1f404058e2
commit
f491fe3fb4
@ -15,48 +15,80 @@
|
|||||||
DEFINE_STATIC_KEY_TRUE(pkey_disabled);
|
DEFINE_STATIC_KEY_TRUE(pkey_disabled);
|
||||||
int pkeys_total; /* Total pkeys as per device tree */
|
int pkeys_total; /* Total pkeys as per device tree */
|
||||||
u32 initial_allocation_mask; /* Bits set for the initially allocated keys */
|
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 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 */
|
* Even if we allocate keys with sys_pkey_alloc(), we need to make sure
|
||||||
static u64 pkey_iamr_mask; /* Bits in AMR not to be touched */
|
* other thread still find the access denied using the same keys.
|
||||||
static u64 pkey_uamor_mask; /* Bits in UMOR not to be touched */
|
*/
|
||||||
|
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;
|
static int execute_only_key = 2;
|
||||||
|
|
||||||
|
|
||||||
#define AMR_BITS_PER_PKEY 2
|
#define AMR_BITS_PER_PKEY 2
|
||||||
#define AMR_RD_BIT 0x1UL
|
#define AMR_RD_BIT 0x1UL
|
||||||
#define AMR_WR_BIT 0x2UL
|
#define AMR_WR_BIT 0x2UL
|
||||||
#define IAMR_EX_BIT 0x1UL
|
#define IAMR_EX_BIT 0x1UL
|
||||||
#define PKEY_REG_BITS (sizeof(u64)*8)
|
#define PKEY_REG_BITS (sizeof(u64) * 8)
|
||||||
#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY))
|
#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];
|
u32 vals[2];
|
||||||
|
int pkeys_total = 0;
|
||||||
struct device_node *cpu;
|
struct device_node *cpu;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pkey is not supported with Radix translation.
|
||||||
|
*/
|
||||||
|
if (radix_enabled())
|
||||||
|
return 0;
|
||||||
|
|
||||||
cpu = of_find_node_by_type(NULL, "cpu");
|
cpu = of_find_node_by_type(NULL, "cpu");
|
||||||
if (!cpu)
|
if (!cpu)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if (of_property_read_u32_array(cpu,
|
if (of_property_read_u32_array(cpu,
|
||||||
"ibm,processor-storage-keys", vals, 2))
|
"ibm,processor-storage-keys", vals, 2) == 0) {
|
||||||
return;
|
/*
|
||||||
|
* 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];
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since any pkey can be used for data or execute, we will just treat
|
* Adjust the upper limit, based on the number of bits supported by
|
||||||
* all keys as equal and track them as one entity.
|
* arch-neutral code.
|
||||||
*/
|
*/
|
||||||
pkeys_total = vals[0];
|
pkeys_total = min_t(int, pkeys_total,
|
||||||
pkeys_devtree_defined = true;
|
((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1));
|
||||||
}
|
return pkeys_total;
|
||||||
|
|
||||||
static inline bool pkey_mmu_enabled(void)
|
|
||||||
{
|
|
||||||
if (firmware_has_feature(FW_FEATURE_LPAR))
|
|
||||||
return pkeys_total;
|
|
||||||
else
|
|
||||||
return cpu_has_feature(CPU_FTR_PKEY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pkey_initialize(void)
|
static int pkey_initialize(void)
|
||||||
@ -80,35 +112,13 @@ static int pkey_initialize(void)
|
|||||||
!= (sizeof(u64) * BITS_PER_BYTE));
|
!= (sizeof(u64) * BITS_PER_BYTE));
|
||||||
|
|
||||||
/* scan the device tree for pkey feature */
|
/* scan the device tree for pkey feature */
|
||||||
scan_pkey_feature();
|
pkeys_total = scan_pkey_feature();
|
||||||
|
if (pkeys_total)
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
static_branch_disable(&pkey_disabled);
|
static_branch_disable(&pkey_disabled);
|
||||||
|
else {
|
||||||
if (static_branch_likely(&pkey_disabled))
|
static_branch_enable(&pkey_disabled);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The device tree cannot be relied to indicate support for
|
* The device tree cannot be relied to indicate support for
|
||||||
@ -122,48 +132,71 @@ static int pkey_initialize(void)
|
|||||||
#ifdef CONFIG_PPC_4K_PAGES
|
#ifdef CONFIG_PPC_4K_PAGES
|
||||||
/*
|
/*
|
||||||
* The OS can manage only 8 pkeys due to its inability to represent them
|
* 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;
|
os_reserved = pkeys_total - 8;
|
||||||
#else
|
#else
|
||||||
os_reserved = 0;
|
os_reserved = 0;
|
||||||
#endif
|
#endif
|
||||||
/*
|
|
||||||
* key 1 is recommended not to be used. PowerISA(3.0) page 1015,
|
|
||||||
* programming note.
|
|
||||||
*/
|
|
||||||
reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key);
|
|
||||||
|
|
||||||
/* register mask is in BE format */
|
|
||||||
pkey_amr_mask = ~0x0ul;
|
|
||||||
pkey_amr_mask &= ~(0x3ul << pkeyshift(0));
|
|
||||||
|
|
||||||
pkey_iamr_mask = ~0x0ul;
|
|
||||||
pkey_iamr_mask &= ~(0x3ul << pkeyshift(0));
|
|
||||||
pkey_iamr_mask &= ~(0x3ul << pkeyshift(execute_only_key));
|
|
||||||
|
|
||||||
pkey_uamor_mask = ~0x0ul;
|
|
||||||
pkey_uamor_mask &= ~(0x3ul << pkeyshift(0));
|
|
||||||
pkey_uamor_mask &= ~(0x3ul << pkeyshift(execute_only_key));
|
|
||||||
|
|
||||||
/* mark the rest of the keys as reserved and hence unavailable */
|
|
||||||
for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) {
|
|
||||||
reserved_allocation_mask |= (0x1 << i);
|
|
||||||
pkey_uamor_mask &= ~(0x3ul << pkeyshift(i));
|
|
||||||
}
|
|
||||||
initial_allocation_mask = reserved_allocation_mask | (0x1 << 0);
|
|
||||||
|
|
||||||
if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) {
|
if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) {
|
||||||
/*
|
/*
|
||||||
* Insufficient number of keys to support
|
* Insufficient number of keys to support
|
||||||
* execute only key. Mark it unavailable.
|
* execute only key. Mark it unavailable.
|
||||||
* Any AMR, UAMOR, IAMR bit set for
|
|
||||||
* this key is irrelevant since this key
|
|
||||||
* can never be allocated.
|
|
||||||
*/
|
*/
|
||||||
execute_only_key = -1;
|
execute_only_key = -1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Mark the execute_only_pkey as not available for
|
||||||
|
* user allocation via pkey_alloc.
|
||||||
|
*/
|
||||||
|
reserved_allocation_mask |= (0x1 << execute_only_key);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the uamor bits for this key.
|
||||||
|
*/
|
||||||
|
default_uamor &= ~(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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
default_uamor &= ~(0x3ul << pkeyshift(i));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Prevent the allocation of reserved keys too.
|
||||||
|
*/
|
||||||
|
initial_allocation_mask |= reserved_allocation_mask;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,13 +338,13 @@ void thread_pkey_regs_init(struct thread_struct *thread)
|
|||||||
if (static_branch_likely(&pkey_disabled))
|
if (static_branch_likely(&pkey_disabled))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
thread->amr = pkey_amr_mask;
|
thread->amr = default_amr;
|
||||||
thread->iamr = pkey_iamr_mask;
|
thread->iamr = default_iamr;
|
||||||
thread->uamor = pkey_uamor_mask;
|
thread->uamor = default_uamor;
|
||||||
|
|
||||||
write_uamor(pkey_uamor_mask);
|
write_amr(default_amr);
|
||||||
write_amr(pkey_amr_mask);
|
write_iamr(default_iamr);
|
||||||
write_iamr(pkey_iamr_mask);
|
write_uamor(default_uamor);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __execute_only_pkey(struct mm_struct *mm)
|
int __execute_only_pkey(struct mm_struct *mm)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user