summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
diff options
context:
space:
mode:
authorRam Pai <linuxram@us.ibm.com>2018-01-18 17:50:40 -0800
committerMichael Ellerman <mpe@ellerman.id.au>2018-01-20 22:59:04 +1100
commite6c2a4797e101a25eced94aa9e1fc42c30247aec (patch)
tree97ab4186d328b99f469a0252f419a2f692524139 /arch/powerpc/mm
parent1137573acfe4c67cdd265bbfbd2d66ebe87d6325 (diff)
downloadlinux-e6c2a4797e101a25eced94aa9e1fc42c30247aec.tar.bz2
powerpc: Handle exceptions caused by pkey violation
Handle Data and Instruction exceptions caused by memory protection-key. The CPU will detect the key fault if the HPTE is already programmed with the key. However if the HPTE is not hashed, a key fault will not be detected by the hardware. The software will detect pkey violation in such a case. Signed-off-by: Ram Pai <linuxram@us.ibm.com> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r--arch/powerpc/mm/fault.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index c07896c19517..6417a659b02d 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -427,6 +427,11 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
+ if (error_code & DSISR_KEYFAULT) {
+ _exception(SIGSEGV, regs, SEGV_PKUERR, address);
+ return 0;
+ }
+
/*
* We want to do this outside mmap_sem, because reading code around nip
* can result in fault, which will cause a deadlock when called with
@@ -498,6 +503,23 @@ good_area:
* the fault.
*/
fault = handle_mm_fault(vma, address, flags);
+
+#ifdef CONFIG_PPC_MEM_KEYS
+ /*
+ * if the HPTE is not hashed, hardware will not detect
+ * a key fault. Lets check if we failed because of a
+ * software detected key fault.
+ */
+ if (unlikely(fault & VM_FAULT_SIGSEGV) &&
+ !arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
+ is_exec, 0)) {
+ int pkey = vma_pkey(vma);
+
+ if (likely(pkey))
+ return __bad_area(regs, address, SEGV_PKUERR);
+ }
+#endif /* CONFIG_PPC_MEM_KEYS */
+
major |= fault & VM_FAULT_MAJOR;
/*