Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1668160 > unrolled thread

[RFC v2 00/12] powerpc: Memory Protection Keys

Started byRam Pai <linuxram@us.ibm.com>
First post2017-06-17 06:00 +0200
Last post2017-06-21 02:20 +0200
Articles 20 on this page of 50 — 6 participants

Back to article view | Back to linux.kernel


Contents

  [RFC v2 00/12] powerpc: Memory Protection Keys Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
    [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 09:00 +0200
        Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 02:00 +0200
          Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-21 05:20 +0200
            Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 08:20 +0200
    [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI  exception Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 10:20 +0200
        Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI  exception Ram Pai <linuxram@us.ibm.com> - 2017-06-21 01:30 +0200
      Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> - 2017-06-21 09:30 +0200
        Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI  exception Ram Pai <linuxram@us.ibm.com> - 2017-06-21 11:20 +0200
    [RFC v2 04/12] powerpc: store and restore the pkey state across context switches. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
    [RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call. "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> - 2017-06-21 09:20 +0200
    [RFC v2 11/12]Documentation: Documentation updates. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 11/12]Documentation: Documentation updates. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 08:20 +0200
        Re: [RFC v2 11/12]Documentation: Documentation updates. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 02:10 +0200
    [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 12:30 +0200
        Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 01:30 +0200
          Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-21 07:40 +0200
            Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 08:40 +0200
      Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages. "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> - 2017-06-21 08:50 +0200
        Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 11:40 +0200
      Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-22 11:10 +0200
        Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-22 18:30 +0200
    [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused the exception. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused the exception. Michael Ellerman <mpe@ellerman.id.au> - 2017-06-19 13:10 +0200
        Re: [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused  the exception. Ram Pai <linuxram@us.ibm.com> - 2017-06-19 20:00 +0200
          Re: [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused  the exception. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 08:50 +0200
            Re: [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused  the exception. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 02:00 +0200
        Re: [RFC v2 10/12] powerpc: Read AMR only if pkey-violation caused  the exception. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 02:00 +0200
    [RFC v2 03/12] powerpc: Implement sys_pkey_alloc and sys_pkey_free system call. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 03/12] powerpc: Implement sys_pkey_alloc and sys_pkey_free system call. Michael Ellerman <mpe@ellerman.id.au> - 2017-06-19 14:20 +0200
        Re: [RFC v2 03/12] powerpc: Implement sys_pkey_alloc and  sys_pkey_free system call. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 00:50 +0200
    [RFC v2 06/12] powerpc: Program HPTE key protection bits. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 06/12] powerpc: Program HPTE key protection bits. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 10:30 +0200
        Re: [RFC v2 06/12] powerpc: Program HPTE key protection bits. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 01:30 +0200
    [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-17 06:00 +0200
      Re: [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed  hpte pages. Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 13:00 +0200
        Re: [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 01:30 +0200
      Re: [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed hpte pages. "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> - 2017-06-21 09:00 +0200
        Re: [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed  hpte pages. Ram Pai <linuxram@us.ibm.com> - 2017-06-21 22:20 +0200
      Re: [RFC v2 02/12] powerpc: Free up four 64K PTE bits in 64K backed hpte pages. "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> - 2017-06-21 09:00 +0200
    Re: [RFC v2 12/12]selftest: Updated protection key selftest Michael Ellerman <mpe@ellerman.id.au> - 2017-06-19 13:10 +0200
    Re: [RFC v2 00/12] powerpc: Memory Protection Keys Balbir Singh <bsingharora@gmail.com> - 2017-06-20 07:20 +0200
      Re: [RFC v2 00/12] powerpc: Memory Protection Keys Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 08:10 +0200
      Re: [RFC v2 00/12] powerpc: Memory Protection Keys Benjamin Herrenschmidt <benh@kernel.crashing.org> - 2017-06-20 12:00 +0200
    Re: [RFC v2 12/12]selftest: Updated protection key selftest Anshuman Khandual <khandual@linux.vnet.ibm.com> - 2017-06-20 08:30 +0200
      Re: [RFC v2 12/12]selftest: Updated protection key selftest Ram Pai <linuxram@us.ibm.com> - 2017-06-21 02:20 +0200

Page 1 of 3  [1] 2 3  Next page →


#1668160 — [RFC v2 00/12] powerpc: Memory Protection Keys

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 00/12] powerpc: Memory Protection Keys
Message-ID<tTd3X-1kp-3@gated-at.bofh.it>
Memory protection keys enable applications to protect its
address space from inadvertent access or corruption from
itself.

The overall idea:

 A process allocates a   key  and associates it with
 a  address  range  within    its   address   space.
 The process  than  can  dynamically  set read/write 
 permissions on  the   key   without  involving  the 
 kernel. Any  code that  violates   the  permissions
 off the address space; as defined by its associated
 key, will receive a segmentation fault.

This patch series enables the feature on PPC64.
It is enabled on HPTE 64K-page platform.

ISA3.0 section 5.7.13 describes the detailed specifications.


Testing:
	This patch series has passed all the protection key
	tests available in  the selftests directory.
	The tests are updated to work on both x86 and powerpc.


version v2:
	(1) documentation and selftest added
 	(2) fixed a bug in 4k hpte backed 64k pte where page
	    invalidation was not done correctly, and 
	    initialization of second-part-of-the-pte was not
	    done correctly if the pte was not yet Hashed
	    with a hpte.  Reported by Aneesh.
	(3) Fixed ABI breakage caused in siginfo structure.
		Reported by Anshuman.
	
	Outstanding known issue:
	  Calls to sys_swapcontext with a made-up context will end 
	  up with a crap AMR if done by code who didn't know about
	  that register. -- Reported by Ben.

version v1: Initial version

Thanks-to: Dave Hansen, Aneesh, Paul Mackerras,
	   Michael Ellermen


Ram Pai (12):
  Free up four 64K PTE bits in 4K backed hpte pages.
  Free up four 64K PTE bits in 64K backed hpte pages.
  Implement sys_pkey_alloc and sys_pkey_free system call.
  store and restore the pkey state across context switches.
  Implementation for sys_mprotect_pkey() system call.
  Program HPTE key protection bits.
  Macro the mask used for checking DSI exception
  Handle exceptions caused by violation of pkey protection.
  Deliver SEGV signal on pkey violation.
  Read AMR only if pkey-violation caused the exception.
  Documentation updates.
  Updated protection key selftest

 Documentation/vm/protection-keys.txt          |  110 ++
 Documentation/x86/protection-keys.txt         |   85 --
 arch/powerpc/Kconfig                          |   15 +
 arch/powerpc/include/asm/book3s/64/hash-4k.h  |   20 +
 arch/powerpc/include/asm/book3s/64/hash-64k.h |   48 +-
 arch/powerpc/include/asm/book3s/64/hash.h     |   15 +-
 arch/powerpc/include/asm/book3s/64/mmu-hash.h |   10 +
 arch/powerpc/include/asm/book3s/64/mmu.h      |   10 +
 arch/powerpc/include/asm/book3s/64/pgtable.h  |   84 +-
 arch/powerpc/include/asm/mman.h               |   29 +-
 arch/powerpc/include/asm/mmu_context.h        |   12 +
 arch/powerpc/include/asm/paca.h               |    1 +
 arch/powerpc/include/asm/pkeys.h              |  159 +++
 arch/powerpc/include/asm/processor.h          |    5 +
 arch/powerpc/include/asm/reg.h                |   10 +-
 arch/powerpc/include/asm/systbl.h             |    3 +
 arch/powerpc/include/asm/unistd.h             |    6 +-
 arch/powerpc/include/uapi/asm/ptrace.h        |    3 +-
 arch/powerpc/include/uapi/asm/unistd.h        |    3 +
 arch/powerpc/kernel/asm-offsets.c             |    5 +
 arch/powerpc/kernel/exceptions-64s.S          |   18 +-
 arch/powerpc/kernel/process.c                 |   18 +
 arch/powerpc/kernel/signal_32.c               |   14 +
 arch/powerpc/kernel/signal_64.c               |   14 +
 arch/powerpc/kernel/traps.c                   |   49 +
 arch/powerpc/mm/Makefile                      |    1 +
 arch/powerpc/mm/dump_linuxpagetables.c        |    3 +-
 arch/powerpc/mm/fault.c                       |   25 +-
 arch/powerpc/mm/hash64_4k.c                   |   14 +-
 arch/powerpc/mm/hash64_64k.c                  |   93 +-
 arch/powerpc/mm/hash_utils_64.c               |   35 +-
 arch/powerpc/mm/hugetlbpage-hash64.c          |   16 +-
 arch/powerpc/mm/mmu_context_book3s64.c        |    5 +
 arch/powerpc/mm/pkeys.c                       |  267 +++++
 include/linux/mm.h                            |   32 +-
 include/uapi/asm-generic/mman-common.h        |    2 +-
 tools/testing/selftests/vm/Makefile           |    1 +
 tools/testing/selftests/vm/pkey-helpers.h     |  365 +++++++
 tools/testing/selftests/vm/protection_keys.c  | 1451 +++++++++++++++++++++++++
 tools/testing/selftests/x86/Makefile          |    2 +-
 tools/testing/selftests/x86/pkey-helpers.h    |  219 ----
 tools/testing/selftests/x86/protection_keys.c | 1395 ------------------------
 42 files changed, 2828 insertions(+), 1844 deletions(-)
 create mode 100644 Documentation/vm/protection-keys.txt
 delete mode 100644 Documentation/x86/protection-keys.txt
 create mode 100644 arch/powerpc/include/asm/pkeys.h
 create mode 100644 arch/powerpc/mm/pkeys.c
 create mode 100644 tools/testing/selftests/vm/pkey-helpers.h
 create mode 100644 tools/testing/selftests/vm/protection_keys.c
 delete mode 100644 tools/testing/selftests/x86/pkey-helpers.h
 delete mode 100644 tools/testing/selftests/x86/protection_keys.c

-- 
1.8.3.1

[toc] | [next] | [standalone]


#1668161 — [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.
Message-ID<tTd3Y-1kp-5@gated-at.bofh.it>
In reply to#1668160
The value of the AMR register at the time of exception
is made available in gp_regs[PT_AMR] of the siginfo.

This field can be used to reprogram the permission bits of
any valid pkey.

Similarly the value of the pkey, whose protection got violated,
is made available at si_pkey field of the siginfo structure.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 arch/powerpc/include/asm/paca.h        |  1 +
 arch/powerpc/include/uapi/asm/ptrace.h |  3 ++-
 arch/powerpc/kernel/asm-offsets.c      |  5 ++++
 arch/powerpc/kernel/exceptions-64s.S   |  8 ++++++
 arch/powerpc/kernel/signal_32.c        | 14 ++++++++++
 arch/powerpc/kernel/signal_64.c        | 14 ++++++++++
 arch/powerpc/kernel/traps.c            | 49 ++++++++++++++++++++++++++++++++++
 arch/powerpc/mm/fault.c                |  4 +++
 8 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 1c09f8f..a41afd3 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -92,6 +92,7 @@ struct paca_struct {
 	struct dtl_entry *dispatch_log_end;
 #endif /* CONFIG_PPC_STD_MMU_64 */
 	u64 dscr_default;		/* per-CPU default DSCR */
+	u64 paca_amr;			/* value of amr at exception */
 
 #ifdef CONFIG_PPC_STD_MMU_64
 	/*
diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
index 8036b38..7ec2428 100644
--- a/arch/powerpc/include/uapi/asm/ptrace.h
+++ b/arch/powerpc/include/uapi/asm/ptrace.h
@@ -108,8 +108,9 @@ struct pt_regs {
 #define PT_DAR	41
 #define PT_DSISR 42
 #define PT_RESULT 43
-#define PT_DSCR 44
 #define PT_REGS_COUNT 44
+#define PT_DSCR 44
+#define PT_AMR	45
 
 #define PT_FPR0	48	/* each FP reg occupies 2 slots in this space */
 
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 709e234..17f5d8a 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -241,6 +241,11 @@ int main(void)
 	OFFSET(PACAHWCPUID, paca_struct, hw_cpu_id);
 	OFFSET(PACAKEXECSTATE, paca_struct, kexec_state);
 	OFFSET(PACA_DSCR_DEFAULT, paca_struct, dscr_default);
+
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	OFFSET(PACA_AMR, paca_struct, paca_amr);
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	OFFSET(ACCOUNT_STARTTIME, paca_struct, accounting.starttime);
 	OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
 	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 3fd0528..8db9ef8 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -493,6 +493,10 @@ EXC_COMMON_BEGIN(data_access_common)
 	ld	r12,_MSR(r1)
 	ld	r3,PACA_EXGEN+EX_DAR(r13)
 	lwz	r4,PACA_EXGEN+EX_DSISR(r13)
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	mfspr	r5,SPRN_AMR
+	std	r5,PACA_AMR(r13)
+#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 	li	r5,0x300
 	std	r3,_DAR(r1)
 	std	r4,_DSISR(r1)
@@ -561,6 +565,10 @@ EXC_COMMON_BEGIN(instruction_access_common)
 	ld	r12,_MSR(r1)
 	ld	r3,_NIP(r1)
 	andis.	r4,r12,0x5820
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	mfspr	r5,SPRN_AMR
+	std	r5,PACA_AMR(r13)
+#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 	li	r5,0x400
 	std	r3,_DAR(r1)
 	std	r4,_DSISR(r1)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 97bb138..059766a 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -500,6 +500,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
 				   (unsigned long) &frame->tramp[2]);
 	}
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	if (__put_user(get_paca()->paca_amr, &frame->mc_gregs[PT_AMR]))
+		return 1;
+#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	return 0;
 }
 
@@ -661,6 +666,9 @@ static long restore_user_regs(struct pt_regs *regs,
 	long err;
 	unsigned int save_r2 = 0;
 	unsigned long msr;
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	unsigned long amr;
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 #ifdef CONFIG_VSX
 	int i;
 #endif
@@ -750,6 +758,12 @@ static long restore_user_regs(struct pt_regs *regs,
 		return 1;
 #endif /* CONFIG_SPE */
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	err |= __get_user(amr, &sr->mc_gregs[PT_AMR]);
+	if (!err && amr != get_paca()->paca_amr)
+		write_amr(amr);
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	return 0;
 }
 
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index c83c115..35df2e4 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -174,6 +174,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,
 	if (set != NULL)
 		err |=  __put_user(set->sig[0], &sc->oldmask);
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	err |= __put_user(get_paca()->paca_amr, &sc->gp_regs[PT_AMR]);
+#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	return err;
 }
 
@@ -327,6 +331,9 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
 	unsigned long save_r13 = 0;
 	unsigned long msr;
 	struct pt_regs *regs = tsk->thread.regs;
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	unsigned long amr;
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 #ifdef CONFIG_VSX
 	int i;
 #endif
@@ -406,6 +413,13 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
 			tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
 	}
 #endif
+
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	err |= __get_user(amr, &sc->gp_regs[PT_AMR]);
+	if (!err && amr != get_paca()->paca_amr)
+		write_amr(amr);
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	return err;
 }
 
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index d4e545d..cc4bde8b 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -20,6 +20,7 @@
 #include <linux/sched/debug.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/pkeys.h>
 #include <linux/stddef.h>
 #include <linux/unistd.h>
 #include <linux/ptrace.h>
@@ -247,6 +248,49 @@ void user_single_step_siginfo(struct task_struct *tsk,
 	info->si_addr = (void __user *)regs->nip;
 }
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+static void fill_sig_info_pkey(int si_code, siginfo_t *info, unsigned long addr)
+{
+	struct vm_area_struct *vma;
+
+	/* Fault not from Protection Keys: nothing to do */
+	if (si_code != SEGV_PKUERR)
+		return;
+
+	down_read(&current->mm->mmap_sem);
+	/*
+	 * we could be racing with pkey_mprotect().
+	 * If pkey_mprotect() wins the key value could
+	 * get modified...xxx
+	 */
+	vma = find_vma(current->mm, addr);
+	up_read(&current->mm->mmap_sem);
+
+	/*
+	 * force_sig_info_fault() is called from a number of
+	 * contexts, some of which have a VMA and some of which
+	 * do not.  The Pkey-fault handing happens after we have a
+	 * valid VMA, so we should never reach this without a
+	 * valid VMA.
+	 */
+	if (!vma) {
+		WARN_ONCE(1, "Pkey fault with no VMA passed in");
+		info->si_pkey = 0;
+		return;
+	}
+
+	/*
+	 * We could report the incorrect key because of the reason
+	 * explained above.
+	 *
+	 * si_pkey should be thought off as a strong hint, but not
+	 * an absolutely guarantee because of the race explained
+	 * above.
+	 */
+	info->si_pkey = vma_pkey(vma);
+}
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 {
 	siginfo_t info;
@@ -274,6 +318,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 	info.si_signo = signr;
 	info.si_code = code;
 	info.si_addr = (void __user *) addr;
+
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	fill_sig_info_pkey(code, &info, addr);
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 	force_sig_info(signr, &info, current);
 }
 
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index c31624f..dd448d2 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -453,6 +453,10 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
 	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
 					flags & FAULT_FLAG_INSTRUCTION,
 					0)) {
+
+		/* our caller may not have saved the amr. Lets save it */
+		get_paca()->paca_amr = read_amr();
+
 		code = SEGV_PKUERR;
 		goto bad_area;
 	}
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1670345 — Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.

FromAnshuman Khandual <khandual@linux.vnet.ibm.com>
Date2017-06-20 09:00 +0200
SubjectRe: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.
Message-ID<tUliP-5a4-17@gated-at.bofh.it>
In reply to#1668161
On 06/17/2017 09:22 AM, Ram Pai wrote:
> The value of the AMR register at the time of exception
> is made available in gp_regs[PT_AMR] of the siginfo.
> 
> This field can be used to reprogram the permission bits of
> any valid pkey.
> 
> Similarly the value of the pkey, whose protection got violated,
> is made available at si_pkey field of the siginfo structure.
> 
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> ---
>  arch/powerpc/include/asm/paca.h        |  1 +
>  arch/powerpc/include/uapi/asm/ptrace.h |  3 ++-
>  arch/powerpc/kernel/asm-offsets.c      |  5 ++++
>  arch/powerpc/kernel/exceptions-64s.S   |  8 ++++++
>  arch/powerpc/kernel/signal_32.c        | 14 ++++++++++
>  arch/powerpc/kernel/signal_64.c        | 14 ++++++++++
>  arch/powerpc/kernel/traps.c            | 49 ++++++++++++++++++++++++++++++++++
>  arch/powerpc/mm/fault.c                |  4 +++
>  8 files changed, 97 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> index 1c09f8f..a41afd3 100644
> --- a/arch/powerpc/include/asm/paca.h
> +++ b/arch/powerpc/include/asm/paca.h
> @@ -92,6 +92,7 @@ struct paca_struct {
>  	struct dtl_entry *dispatch_log_end;
>  #endif /* CONFIG_PPC_STD_MMU_64 */
>  	u64 dscr_default;		/* per-CPU default DSCR */
> +	u64 paca_amr;			/* value of amr at exception */
> 
>  #ifdef CONFIG_PPC_STD_MMU_64
>  	/*
> diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
> index 8036b38..7ec2428 100644
> --- a/arch/powerpc/include/uapi/asm/ptrace.h
> +++ b/arch/powerpc/include/uapi/asm/ptrace.h
> @@ -108,8 +108,9 @@ struct pt_regs {
>  #define PT_DAR	41
>  #define PT_DSISR 42
>  #define PT_RESULT 43
> -#define PT_DSCR 44
>  #define PT_REGS_COUNT 44
> +#define PT_DSCR 44
> +#define PT_AMR	45

PT_REGS_COUNT is not getting incremented even after adding
one more element into the pack ?

> 
>  #define PT_FPR0	48	/* each FP reg occupies 2 slots in this space */
> 
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> index 709e234..17f5d8a 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -241,6 +241,11 @@ int main(void)
>  	OFFSET(PACAHWCPUID, paca_struct, hw_cpu_id);
>  	OFFSET(PACAKEXECSTATE, paca_struct, kexec_state);
>  	OFFSET(PACA_DSCR_DEFAULT, paca_struct, dscr_default);
> +
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	OFFSET(PACA_AMR, paca_struct, paca_amr);
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +

So we now have a place in PACA for AMR.

>  	OFFSET(ACCOUNT_STARTTIME, paca_struct, accounting.starttime);
>  	OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
>  	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
> diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> index 3fd0528..8db9ef8 100644
> --- a/arch/powerpc/kernel/exceptions-64s.S
> +++ b/arch/powerpc/kernel/exceptions-64s.S
> @@ -493,6 +493,10 @@ EXC_COMMON_BEGIN(data_access_common)
>  	ld	r12,_MSR(r1)
>  	ld	r3,PACA_EXGEN+EX_DAR(r13)
>  	lwz	r4,PACA_EXGEN+EX_DSISR(r13)
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	mfspr	r5,SPRN_AMR
> +	std	r5,PACA_AMR(r13)
> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>  	li	r5,0x300
>  	std	r3,_DAR(r1)
>  	std	r4,_DSISR(r1)
> @@ -561,6 +565,10 @@ EXC_COMMON_BEGIN(instruction_access_common)
>  	ld	r12,_MSR(r1)
>  	ld	r3,_NIP(r1)
>  	andis.	r4,r12,0x5820
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	mfspr	r5,SPRN_AMR
> +	std	r5,PACA_AMR(r13)
> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */

Saving the AMR context on page faults, this seems to be
changing in the next patch again based on whether any
key was active at that point and fault happened for the
key enforcement ?

>  	li	r5,0x400
>  	std	r3,_DAR(r1)
>  	std	r4,_DSISR(r1)
> diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
> index 97bb138..059766a 100644
> --- a/arch/powerpc/kernel/signal_32.c
> +++ b/arch/powerpc/kernel/signal_32.c
> @@ -500,6 +500,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
>  				   (unsigned long) &frame->tramp[2]);
>  	}
> 
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	if (__put_user(get_paca()->paca_amr, &frame->mc_gregs[PT_AMR]))
> +		return 1;
> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  	return 0;
>  }
> 
> @@ -661,6 +666,9 @@ static long restore_user_regs(struct pt_regs *regs,
>  	long err;
>  	unsigned int save_r2 = 0;
>  	unsigned long msr;
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	unsigned long amr;
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>  #ifdef CONFIG_VSX
>  	int i;
>  #endif
> @@ -750,6 +758,12 @@ static long restore_user_regs(struct pt_regs *regs,
>  		return 1;
>  #endif /* CONFIG_SPE */
> 
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	err |= __get_user(amr, &sr->mc_gregs[PT_AMR]);
> +	if (!err && amr != get_paca()->paca_amr)
> +		write_amr(amr);
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  	return 0;
>  }
> 
> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> index c83c115..35df2e4 100644
> --- a/arch/powerpc/kernel/signal_64.c
> +++ b/arch/powerpc/kernel/signal_64.c
> @@ -174,6 +174,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,
>  	if (set != NULL)
>  		err |=  __put_user(set->sig[0], &sc->oldmask);
> 
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	err |= __put_user(get_paca()->paca_amr, &sc->gp_regs[PT_AMR]);
> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  	return err;
>  }
> 
> @@ -327,6 +331,9 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
>  	unsigned long save_r13 = 0;
>  	unsigned long msr;
>  	struct pt_regs *regs = tsk->thread.regs;
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	unsigned long amr;
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>  #ifdef CONFIG_VSX
>  	int i;
>  #endif
> @@ -406,6 +413,13 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
>  			tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
>  	}
>  #endif
> +
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	err |= __get_user(amr, &sc->gp_regs[PT_AMR]);
> +	if (!err && amr != get_paca()->paca_amr)
> +		write_amr(amr);
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  	return err;
>  }
> 
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index d4e545d..cc4bde8b 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -20,6 +20,7 @@
>  #include <linux/sched/debug.h>
>  #include <linux/kernel.h>
>  #include <linux/mm.h>
> +#include <linux/pkeys.h>
>  #include <linux/stddef.h>
>  #include <linux/unistd.h>
>  #include <linux/ptrace.h>
> @@ -247,6 +248,49 @@ void user_single_step_siginfo(struct task_struct *tsk,
>  	info->si_addr = (void __user *)regs->nip;
>  }
> 
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +static void fill_sig_info_pkey(int si_code, siginfo_t *info, unsigned long addr)
> +{
> +	struct vm_area_struct *vma;
> +
> +	/* Fault not from Protection Keys: nothing to do */
> +	if (si_code != SEGV_PKUERR)
> +		return;

Should have checked this in the caller ?

> +
> +	down_read(&current->mm->mmap_sem);
> +	/*
> +	 * we could be racing with pkey_mprotect().
> +	 * If pkey_mprotect() wins the key value could
> +	 * get modified...xxx
> +	 */
> +	vma = find_vma(current->mm, addr);
> +	up_read(&current->mm->mmap_sem);
> +
> +	/*
> +	 * force_sig_info_fault() is called from a number of
> +	 * contexts, some of which have a VMA and some of which
> +	 * do not.  The Pkey-fault handing happens after we have a
> +	 * valid VMA, so we should never reach this without a
> +	 * valid VMA.
> +	 */

Also because pkey can only be used from user space when we will
definitely have a VMA associated with it.

> +	if (!vma) {
> +		WARN_ONCE(1, "Pkey fault with no VMA passed in");
> +		info->si_pkey = 0;
> +		return;
> +	}
> +
> +	/*
> +	 * We could report the incorrect key because of the reason
> +	 * explained above.

What if we hold mm->mmap_sem for some more time till we update
info->si_pkey ? Is there still a chance that pkey would have
changed by the time siginfo returns to user space ? I am still
wondering is there way to hold up VMA changes to be on safer
side. Is the race conditions exists on x86 as well ?

> +	 *
> +	 * si_pkey should be thought off as a strong hint, but not
> +	 * an absolutely guarantee because of the race explained
> +	 * above.
> +	 */
> +	info->si_pkey = vma_pkey(vma);
> +}
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
>  {
>  	siginfo_t info;
> @@ -274,6 +318,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
>  	info.si_signo = signr;
>  	info.si_code = code;
>  	info.si_addr = (void __user *) addr;
> +
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +	fill_sig_info_pkey(code, &info, addr);
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
>  	force_sig_info(signr, &info, current);
>  }
> 
> diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> index c31624f..dd448d2 100644
> --- a/arch/powerpc/mm/fault.c
> +++ b/arch/powerpc/mm/fault.c
> @@ -453,6 +453,10 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
>  	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
>  					flags & FAULT_FLAG_INSTRUCTION,
>  					0)) {
> +
> +		/* our caller may not have saved the amr. Lets save it */
> +		get_paca()->paca_amr = read_amr();
> +

Something is not right here. PACA save should have happened before we
come here. Why say the caller might not have saved the AMR ? Is there
a path when its possible ?

[toc] | [prev] | [next] | [standalone]


#1671247 — Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 02:00 +0200
SubjectRe: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.
Message-ID<tUBdT-6Oo-7@gated-at.bofh.it>
In reply to#1670345
On Tue, Jun 20, 2017 at 12:24:53PM +0530, Anshuman Khandual wrote:
> On 06/17/2017 09:22 AM, Ram Pai wrote:
> > The value of the AMR register at the time of exception
> > is made available in gp_regs[PT_AMR] of the siginfo.
> > 
> > This field can be used to reprogram the permission bits of
> > any valid pkey.
> > 
> > Similarly the value of the pkey, whose protection got violated,
> > is made available at si_pkey field of the siginfo structure.
> > 
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > ---
> >  arch/powerpc/include/asm/paca.h        |  1 +
> >  arch/powerpc/include/uapi/asm/ptrace.h |  3 ++-
> >  arch/powerpc/kernel/asm-offsets.c      |  5 ++++
> >  arch/powerpc/kernel/exceptions-64s.S   |  8 ++++++
> >  arch/powerpc/kernel/signal_32.c        | 14 ++++++++++
> >  arch/powerpc/kernel/signal_64.c        | 14 ++++++++++
> >  arch/powerpc/kernel/traps.c            | 49 ++++++++++++++++++++++++++++++++++
> >  arch/powerpc/mm/fault.c                |  4 +++
> >  8 files changed, 97 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> > index 1c09f8f..a41afd3 100644
> > --- a/arch/powerpc/include/asm/paca.h
> > +++ b/arch/powerpc/include/asm/paca.h
> > @@ -92,6 +92,7 @@ struct paca_struct {
> >  	struct dtl_entry *dispatch_log_end;
> >  #endif /* CONFIG_PPC_STD_MMU_64 */
> >  	u64 dscr_default;		/* per-CPU default DSCR */
> > +	u64 paca_amr;			/* value of amr at exception */
> > 
> >  #ifdef CONFIG_PPC_STD_MMU_64
> >  	/*
> > diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
> > index 8036b38..7ec2428 100644
> > --- a/arch/powerpc/include/uapi/asm/ptrace.h
> > +++ b/arch/powerpc/include/uapi/asm/ptrace.h
> > @@ -108,8 +108,9 @@ struct pt_regs {
> >  #define PT_DAR	41
> >  #define PT_DSISR 42
> >  #define PT_RESULT 43
> > -#define PT_DSCR 44
> >  #define PT_REGS_COUNT 44
> > +#define PT_DSCR 44
> > +#define PT_AMR	45
> 
> PT_REGS_COUNT is not getting incremented even after adding
> one more element into the pack ?

Correct. there are 48 entires in gp_regs table AFAICT, only the first 45
are exposed through pt_regs and through gp_regs. the remaining
are exposed through gp_regs only.

> 
> > 
> >  #define PT_FPR0	48	/* each FP reg occupies 2 slots in this space */
> > 
> > diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> > index 709e234..17f5d8a 100644
> > --- a/arch/powerpc/kernel/asm-offsets.c
> > +++ b/arch/powerpc/kernel/asm-offsets.c
> > @@ -241,6 +241,11 @@ int main(void)
> >  	OFFSET(PACAHWCPUID, paca_struct, hw_cpu_id);
> >  	OFFSET(PACAKEXECSTATE, paca_struct, kexec_state);
> >  	OFFSET(PACA_DSCR_DEFAULT, paca_struct, dscr_default);
> > +
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	OFFSET(PACA_AMR, paca_struct, paca_amr);
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> 
> So we now have a place in PACA for AMR.

yes.

> 
> >  	OFFSET(ACCOUNT_STARTTIME, paca_struct, accounting.starttime);
> >  	OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
> >  	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
> > diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> > index 3fd0528..8db9ef8 100644
> > --- a/arch/powerpc/kernel/exceptions-64s.S
> > +++ b/arch/powerpc/kernel/exceptions-64s.S
> > @@ -493,6 +493,10 @@ EXC_COMMON_BEGIN(data_access_common)
> >  	ld	r12,_MSR(r1)
> >  	ld	r3,PACA_EXGEN+EX_DAR(r13)
> >  	lwz	r4,PACA_EXGEN+EX_DSISR(r13)
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	mfspr	r5,SPRN_AMR
> > +	std	r5,PACA_AMR(r13)
> > +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >  	li	r5,0x300
> >  	std	r3,_DAR(r1)
> >  	std	r4,_DSISR(r1)
> > @@ -561,6 +565,10 @@ EXC_COMMON_BEGIN(instruction_access_common)
> >  	ld	r12,_MSR(r1)
> >  	ld	r3,_NIP(r1)
> >  	andis.	r4,r12,0x5820
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	mfspr	r5,SPRN_AMR
> > +	std	r5,PACA_AMR(r13)
> > +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> 
> Saving the AMR context on page faults, this seems to be
> changing in the next patch again based on whether any
> key was active at that point and fault happened for the
> key enforcement ?

yes. i am going to merge the next patch with this patch.


> 
> >  	li	r5,0x400
> >  	std	r3,_DAR(r1)
> >  	std	r4,_DSISR(r1)
> > diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
> > index 97bb138..059766a 100644
> > --- a/arch/powerpc/kernel/signal_32.c
> > +++ b/arch/powerpc/kernel/signal_32.c
> > @@ -500,6 +500,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
> >  				   (unsigned long) &frame->tramp[2]);
> >  	}
> > 
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	if (__put_user(get_paca()->paca_amr, &frame->mc_gregs[PT_AMR]))
> > +		return 1;
> > +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  	return 0;
> >  }
> > 
> > @@ -661,6 +666,9 @@ static long restore_user_regs(struct pt_regs *regs,
> >  	long err;
> >  	unsigned int save_r2 = 0;
> >  	unsigned long msr;
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	unsigned long amr;
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >  #ifdef CONFIG_VSX
> >  	int i;
> >  #endif
> > @@ -750,6 +758,12 @@ static long restore_user_regs(struct pt_regs *regs,
> >  		return 1;
> >  #endif /* CONFIG_SPE */
> > 
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	err |= __get_user(amr, &sr->mc_gregs[PT_AMR]);
> > +	if (!err && amr != get_paca()->paca_amr)
> > +		write_amr(amr);
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  	return 0;
> >  }
> > 
> > diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> > index c83c115..35df2e4 100644
> > --- a/arch/powerpc/kernel/signal_64.c
> > +++ b/arch/powerpc/kernel/signal_64.c
> > @@ -174,6 +174,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,
> >  	if (set != NULL)
> >  		err |=  __put_user(set->sig[0], &sc->oldmask);
> > 
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	err |= __put_user(get_paca()->paca_amr, &sc->gp_regs[PT_AMR]);
> > +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  	return err;
> >  }
> > 
> > @@ -327,6 +331,9 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> >  	unsigned long save_r13 = 0;
> >  	unsigned long msr;
> >  	struct pt_regs *regs = tsk->thread.regs;
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	unsigned long amr;
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >  #ifdef CONFIG_VSX
> >  	int i;
> >  #endif
> > @@ -406,6 +413,13 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> >  			tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
> >  	}
> >  #endif
> > +
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	err |= __get_user(amr, &sc->gp_regs[PT_AMR]);
> > +	if (!err && amr != get_paca()->paca_amr)
> > +		write_amr(amr);
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  	return err;
> >  }
> > 
> > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> > index d4e545d..cc4bde8b 100644
> > --- a/arch/powerpc/kernel/traps.c
> > +++ b/arch/powerpc/kernel/traps.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/sched/debug.h>
> >  #include <linux/kernel.h>
> >  #include <linux/mm.h>
> > +#include <linux/pkeys.h>
> >  #include <linux/stddef.h>
> >  #include <linux/unistd.h>
> >  #include <linux/ptrace.h>
> > @@ -247,6 +248,49 @@ void user_single_step_siginfo(struct task_struct *tsk,
> >  	info->si_addr = (void __user *)regs->nip;
> >  }
> > 
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +static void fill_sig_info_pkey(int si_code, siginfo_t *info, unsigned long addr)
> > +{
> > +	struct vm_area_struct *vma;
> > +
> > +	/* Fault not from Protection Keys: nothing to do */
> > +	if (si_code != SEGV_PKUERR)
> > +		return;
> 
> Should have checked this in the caller ?

maybe. currently there is only one caller to this function. so either
way is ok. But if more than one callers show up later having the check 
here reduces the burden on the caller.


> 
> > +
> > +	down_read(&current->mm->mmap_sem);
> > +	/*
> > +	 * we could be racing with pkey_mprotect().
> > +	 * If pkey_mprotect() wins the key value could
> > +	 * get modified...xxx
> > +	 */
> > +	vma = find_vma(current->mm, addr);
> > +	up_read(&current->mm->mmap_sem);
> > +
> > +	/*
> > +	 * force_sig_info_fault() is called from a number of
> > +	 * contexts, some of which have a VMA and some of which
> > +	 * do not.  The Pkey-fault handing happens after we have a
> > +	 * valid VMA, so we should never reach this without a
> > +	 * valid VMA.
> > +	 */
> 
> Also because pkey can only be used from user space when we will
> definitely have a VMA associated with it.
> 
> > +	if (!vma) {
> > +		WARN_ONCE(1, "Pkey fault with no VMA passed in");
> > +		info->si_pkey = 0;
> > +		return;
> > +	}
> > +
> > +	/*
> > +	 * We could report the incorrect key because of the reason
> > +	 * explained above.
> 
> What if we hold mm->mmap_sem for some more time till we update
> info->si_pkey ? Is there still a chance that pkey would have
> changed by the time siginfo returns to user space ? I am still
> wondering is there way to hold up VMA changes to be on safer
> side. Is the race conditions exists on x86 as well ?
> 
> > +	 *
> > +	 * si_pkey should be thought off as a strong hint, but not
> > +	 * an absolutely guarantee because of the race explained
> > +	 * above.
> > +	 */
> > +	info->si_pkey = vma_pkey(vma);
> > +}
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
> >  {
> >  	siginfo_t info;
> > @@ -274,6 +318,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
> >  	info.si_signo = signr;
> >  	info.si_code = code;
> >  	info.si_addr = (void __user *) addr;
> > +
> > +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> > +	fill_sig_info_pkey(code, &info, addr);
> > +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> > +
> >  	force_sig_info(signr, &info, current);
> >  }
> > 
> > diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> > index c31624f..dd448d2 100644
> > --- a/arch/powerpc/mm/fault.c
> > +++ b/arch/powerpc/mm/fault.c
> > @@ -453,6 +453,10 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
> >  	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
> >  					flags & FAULT_FLAG_INSTRUCTION,
> >  					0)) {
> > +
> > +		/* our caller may not have saved the amr. Lets save it */
> > +		get_paca()->paca_amr = read_amr();
> > +
> 
> Something is not right here. PACA save should have happened before we
> come here. Why say the caller might not have saved the AMR ? Is there
> a path when its possible ?

This is a case, where the fault is cause because of page not yet being
hashed, but at the same time could have violated a protection key.
Since the page is not hashed yet, there is no protection-key fault
and hence the caller would have not saved the AMR. We do it here to 
catch that case.


RP
-- 
Ram Pai

[toc] | [prev] | [next] | [standalone]


#1671313 — Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.

FromAnshuman Khandual <khandual@linux.vnet.ibm.com>
Date2017-06-21 05:20 +0200
SubjectRe: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.
Message-ID<tUElr-C1-3@gated-at.bofh.it>
In reply to#1671247
On 06/21/2017 05:26 AM, Ram Pai wrote:
> On Tue, Jun 20, 2017 at 12:24:53PM +0530, Anshuman Khandual wrote:
>> On 06/17/2017 09:22 AM, Ram Pai wrote:
>>> The value of the AMR register at the time of exception
>>> is made available in gp_regs[PT_AMR] of the siginfo.
>>>
>>> This field can be used to reprogram the permission bits of
>>> any valid pkey.
>>>
>>> Similarly the value of the pkey, whose protection got violated,
>>> is made available at si_pkey field of the siginfo structure.
>>>
>>> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
>>> ---
>>>  arch/powerpc/include/asm/paca.h        |  1 +
>>>  arch/powerpc/include/uapi/asm/ptrace.h |  3 ++-
>>>  arch/powerpc/kernel/asm-offsets.c      |  5 ++++
>>>  arch/powerpc/kernel/exceptions-64s.S   |  8 ++++++
>>>  arch/powerpc/kernel/signal_32.c        | 14 ++++++++++
>>>  arch/powerpc/kernel/signal_64.c        | 14 ++++++++++
>>>  arch/powerpc/kernel/traps.c            | 49 ++++++++++++++++++++++++++++++++++
>>>  arch/powerpc/mm/fault.c                |  4 +++
>>>  8 files changed, 97 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
>>> index 1c09f8f..a41afd3 100644
>>> --- a/arch/powerpc/include/asm/paca.h
>>> +++ b/arch/powerpc/include/asm/paca.h
>>> @@ -92,6 +92,7 @@ struct paca_struct {
>>>  	struct dtl_entry *dispatch_log_end;
>>>  #endif /* CONFIG_PPC_STD_MMU_64 */
>>>  	u64 dscr_default;		/* per-CPU default DSCR */
>>> +	u64 paca_amr;			/* value of amr at exception */
>>>
>>>  #ifdef CONFIG_PPC_STD_MMU_64
>>>  	/*
>>> diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
>>> index 8036b38..7ec2428 100644
>>> --- a/arch/powerpc/include/uapi/asm/ptrace.h
>>> +++ b/arch/powerpc/include/uapi/asm/ptrace.h
>>> @@ -108,8 +108,9 @@ struct pt_regs {
>>>  #define PT_DAR	41
>>>  #define PT_DSISR 42
>>>  #define PT_RESULT 43
>>> -#define PT_DSCR 44
>>>  #define PT_REGS_COUNT 44
>>> +#define PT_DSCR 44
>>> +#define PT_AMR	45
>>
>> PT_REGS_COUNT is not getting incremented even after adding
>> one more element into the pack ?
> 
> Correct. there are 48 entires in gp_regs table AFAICT, only the first 45
> are exposed through pt_regs and through gp_regs. the remaining
> are exposed through gp_regs only.
> 
>>
>>>
>>>  #define PT_FPR0	48	/* each FP reg occupies 2 slots in this space */
>>>
>>> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
>>> index 709e234..17f5d8a 100644
>>> --- a/arch/powerpc/kernel/asm-offsets.c
>>> +++ b/arch/powerpc/kernel/asm-offsets.c
>>> @@ -241,6 +241,11 @@ int main(void)
>>>  	OFFSET(PACAHWCPUID, paca_struct, hw_cpu_id);
>>>  	OFFSET(PACAKEXECSTATE, paca_struct, kexec_state);
>>>  	OFFSET(PACA_DSCR_DEFAULT, paca_struct, dscr_default);
>>> +
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	OFFSET(PACA_AMR, paca_struct, paca_amr);
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>
>> So we now have a place in PACA for AMR.
> 
> yes.
> 
>>
>>>  	OFFSET(ACCOUNT_STARTTIME, paca_struct, accounting.starttime);
>>>  	OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
>>>  	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
>>> diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
>>> index 3fd0528..8db9ef8 100644
>>> --- a/arch/powerpc/kernel/exceptions-64s.S
>>> +++ b/arch/powerpc/kernel/exceptions-64s.S
>>> @@ -493,6 +493,10 @@ EXC_COMMON_BEGIN(data_access_common)
>>>  	ld	r12,_MSR(r1)
>>>  	ld	r3,PACA_EXGEN+EX_DAR(r13)
>>>  	lwz	r4,PACA_EXGEN+EX_DSISR(r13)
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	mfspr	r5,SPRN_AMR
>>> +	std	r5,PACA_AMR(r13)
>>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>>  	li	r5,0x300
>>>  	std	r3,_DAR(r1)
>>>  	std	r4,_DSISR(r1)
>>> @@ -561,6 +565,10 @@ EXC_COMMON_BEGIN(instruction_access_common)
>>>  	ld	r12,_MSR(r1)
>>>  	ld	r3,_NIP(r1)
>>>  	andis.	r4,r12,0x5820
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	mfspr	r5,SPRN_AMR
>>> +	std	r5,PACA_AMR(r13)
>>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>
>> Saving the AMR context on page faults, this seems to be
>> changing in the next patch again based on whether any
>> key was active at that point and fault happened for the
>> key enforcement ?
> 
> yes. i am going to merge the next patch with this patch.
> 
> 
>>
>>>  	li	r5,0x400
>>>  	std	r3,_DAR(r1)
>>>  	std	r4,_DSISR(r1)
>>> diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
>>> index 97bb138..059766a 100644
>>> --- a/arch/powerpc/kernel/signal_32.c
>>> +++ b/arch/powerpc/kernel/signal_32.c
>>> @@ -500,6 +500,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
>>>  				   (unsigned long) &frame->tramp[2]);
>>>  	}
>>>
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	if (__put_user(get_paca()->paca_amr, &frame->mc_gregs[PT_AMR]))
>>> +		return 1;
>>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  	return 0;
>>>  }
>>>
>>> @@ -661,6 +666,9 @@ static long restore_user_regs(struct pt_regs *regs,
>>>  	long err;
>>>  	unsigned int save_r2 = 0;
>>>  	unsigned long msr;
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	unsigned long amr;
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>>  #ifdef CONFIG_VSX
>>>  	int i;
>>>  #endif
>>> @@ -750,6 +758,12 @@ static long restore_user_regs(struct pt_regs *regs,
>>>  		return 1;
>>>  #endif /* CONFIG_SPE */
>>>
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	err |= __get_user(amr, &sr->mc_gregs[PT_AMR]);
>>> +	if (!err && amr != get_paca()->paca_amr)
>>> +		write_amr(amr);
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  	return 0;
>>>  }
>>>
>>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
>>> index c83c115..35df2e4 100644
>>> --- a/arch/powerpc/kernel/signal_64.c
>>> +++ b/arch/powerpc/kernel/signal_64.c
>>> @@ -174,6 +174,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,
>>>  	if (set != NULL)
>>>  		err |=  __put_user(set->sig[0], &sc->oldmask);
>>>
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	err |= __put_user(get_paca()->paca_amr, &sc->gp_regs[PT_AMR]);
>>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  	return err;
>>>  }
>>>
>>> @@ -327,6 +331,9 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
>>>  	unsigned long save_r13 = 0;
>>>  	unsigned long msr;
>>>  	struct pt_regs *regs = tsk->thread.regs;
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	unsigned long amr;
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>>  #ifdef CONFIG_VSX
>>>  	int i;
>>>  #endif
>>> @@ -406,6 +413,13 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
>>>  			tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
>>>  	}
>>>  #endif
>>> +
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	err |= __get_user(amr, &sc->gp_regs[PT_AMR]);
>>> +	if (!err && amr != get_paca()->paca_amr)
>>> +		write_amr(amr);
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  	return err;
>>>  }
>>>
>>> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
>>> index d4e545d..cc4bde8b 100644
>>> --- a/arch/powerpc/kernel/traps.c
>>> +++ b/arch/powerpc/kernel/traps.c
>>> @@ -20,6 +20,7 @@
>>>  #include <linux/sched/debug.h>
>>>  #include <linux/kernel.h>
>>>  #include <linux/mm.h>
>>> +#include <linux/pkeys.h>
>>>  #include <linux/stddef.h>
>>>  #include <linux/unistd.h>
>>>  #include <linux/ptrace.h>
>>> @@ -247,6 +248,49 @@ void user_single_step_siginfo(struct task_struct *tsk,
>>>  	info->si_addr = (void __user *)regs->nip;
>>>  }
>>>
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +static void fill_sig_info_pkey(int si_code, siginfo_t *info, unsigned long addr)
>>> +{
>>> +	struct vm_area_struct *vma;
>>> +
>>> +	/* Fault not from Protection Keys: nothing to do */
>>> +	if (si_code != SEGV_PKUERR)
>>> +		return;
>>
>> Should have checked this in the caller ?
> 
> maybe. currently there is only one caller to this function. so either
> way is ok. But if more than one callers show up later having the check 
> here reduces the burden on the caller.
> 
> 
>>
>>> +
>>> +	down_read(&current->mm->mmap_sem);
>>> +	/*
>>> +	 * we could be racing with pkey_mprotect().
>>> +	 * If pkey_mprotect() wins the key value could
>>> +	 * get modified...xxx
>>> +	 */
>>> +	vma = find_vma(current->mm, addr);
>>> +	up_read(&current->mm->mmap_sem);
>>> +
>>> +	/*
>>> +	 * force_sig_info_fault() is called from a number of
>>> +	 * contexts, some of which have a VMA and some of which
>>> +	 * do not.  The Pkey-fault handing happens after we have a
>>> +	 * valid VMA, so we should never reach this without a
>>> +	 * valid VMA.
>>> +	 */
>>
>> Also because pkey can only be used from user space when we will
>> definitely have a VMA associated with it.
>>
>>> +	if (!vma) {
>>> +		WARN_ONCE(1, "Pkey fault with no VMA passed in");
>>> +		info->si_pkey = 0;
>>> +		return;
>>> +	}
>>> +
>>> +	/*
>>> +	 * We could report the incorrect key because of the reason
>>> +	 * explained above.
>>
>> What if we hold mm->mmap_sem for some more time till we update
>> info->si_pkey ? Is there still a chance that pkey would have
>> changed by the time siginfo returns to user space ? I am still
>> wondering is there way to hold up VMA changes to be on safer
>> side. Is the race conditions exists on x86 as well ?

Is the race condition exists on x86 system as well ? Can we hold
up little more the mmap_sem to improve our chances ?

>>
>>> +	 *
>>> +	 * si_pkey should be thought off as trong hint, but not
>>> +	 * an absolutely guarantee because of the race explained
>>> +	 * above.
>>> +	 */
>>> +	info->si_pkey = vma_pkey(vma);
>>> +}
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
>>>  {
>>>  	siginfo_t info;
>>> @@ -274,6 +318,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
>>>  	info.si_signo = signr;
>>>  	info.si_code = code;
>>>  	info.si_addr = (void __user *) addr;
>>> +
>>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
>>> +	fill_sig_info_pkey(code, &info, addr);
>>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>>> +
>>>  	force_sig_info(signr, &info, current);
>>>  }
>>>
>>> diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
>>> index c31624f..dd448d2 100644
>>> --- a/arch/powerpc/mm/fault.c
>>> +++ b/arch/powerpc/mm/fault.c
>>> @@ -453,6 +453,10 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
>>>  	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
>>>  					flags & FAULT_FLAG_INSTRUCTION,
>>>  					0)) {
>>> +
>>> +		/* our caller may not have saved the amr. Lets save it */
>>> +		get_paca()->paca_amr = read_amr();
>>> +
>>
>> Something is not right here. PACA save should have happened before we
>> come here. Why say the caller might not have saved the AMR ? Is there
>> a path when its possible ?
> 
> This is a case, where the fault is cause because of page not yet being
> hashed, but at the same time could have violated a protection key.
> Since the page is not hashed yet, there is no protection-key fault
> and hence the caller would have not saved the AMR. We do it here to 
> catch that case.

Is that because of the above optimization you have added in the page
fault path ?

[toc] | [prev] | [next] | [standalone]


#1671382 — Re: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 08:20 +0200
SubjectRe: [RFC v2 09/12] powerpc: Deliver SEGV signal on pkey violation.
Message-ID<tUH9D-2pq-5@gated-at.bofh.it>
In reply to#1671313
On Wed, Jun 21, 2017 at 08:48:20AM +0530, Anshuman Khandual wrote:
> On 06/21/2017 05:26 AM, Ram Pai wrote:
> > On Tue, Jun 20, 2017 at 12:24:53PM +0530, Anshuman Khandual wrote:
> >> On 06/17/2017 09:22 AM, Ram Pai wrote:
> >>> The value of the AMR register at the time of exception
> >>> is made available in gp_regs[PT_AMR] of the siginfo.
> >>>
> >>> This field can be used to reprogram the permission bits of
> >>> any valid pkey.
> >>>
> >>> Similarly the value of the pkey, whose protection got violated,
> >>> is made available at si_pkey field of the siginfo structure.
> >>>
> >>> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> >>> ---
> >>>  arch/powerpc/include/asm/paca.h        |  1 +
> >>>  arch/powerpc/include/uapi/asm/ptrace.h |  3 ++-
> >>>  arch/powerpc/kernel/asm-offsets.c      |  5 ++++
> >>>  arch/powerpc/kernel/exceptions-64s.S   |  8 ++++++
> >>>  arch/powerpc/kernel/signal_32.c        | 14 ++++++++++
> >>>  arch/powerpc/kernel/signal_64.c        | 14 ++++++++++
> >>>  arch/powerpc/kernel/traps.c            | 49 ++++++++++++++++++++++++++++++++++
> >>>  arch/powerpc/mm/fault.c                |  4 +++
> >>>  8 files changed, 97 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> >>> index 1c09f8f..a41afd3 100644
> >>> --- a/arch/powerpc/include/asm/paca.h
> >>> +++ b/arch/powerpc/include/asm/paca.h
> >>> @@ -92,6 +92,7 @@ struct paca_struct {
> >>>  	struct dtl_entry *dispatch_log_end;
> >>>  #endif /* CONFIG_PPC_STD_MMU_64 */
> >>>  	u64 dscr_default;		/* per-CPU default DSCR */
> >>> +	u64 paca_amr;			/* value of amr at exception */
> >>>
> >>>  #ifdef CONFIG_PPC_STD_MMU_64
> >>>  	/*
> >>> diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h
> >>> index 8036b38..7ec2428 100644
> >>> --- a/arch/powerpc/include/uapi/asm/ptrace.h
> >>> +++ b/arch/powerpc/include/uapi/asm/ptrace.h
> >>> @@ -108,8 +108,9 @@ struct pt_regs {
> >>>  #define PT_DAR	41
> >>>  #define PT_DSISR 42
> >>>  #define PT_RESULT 43
> >>> -#define PT_DSCR 44
> >>>  #define PT_REGS_COUNT 44
> >>> +#define PT_DSCR 44
> >>> +#define PT_AMR	45
> >>
> >> PT_REGS_COUNT is not getting incremented even after adding
> >> one more element into the pack ?
> > 
> > Correct. there are 48 entires in gp_regs table AFAICT, only the first 45
> > are exposed through pt_regs and through gp_regs. the remaining
> > are exposed through gp_regs only.
> > 
> >>
> >>>
> >>>  #define PT_FPR0	48	/* each FP reg occupies 2 slots in this space */
> >>>
> >>> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> >>> index 709e234..17f5d8a 100644
> >>> --- a/arch/powerpc/kernel/asm-offsets.c
> >>> +++ b/arch/powerpc/kernel/asm-offsets.c
> >>> @@ -241,6 +241,11 @@ int main(void)
> >>>  	OFFSET(PACAHWCPUID, paca_struct, hw_cpu_id);
> >>>  	OFFSET(PACAKEXECSTATE, paca_struct, kexec_state);
> >>>  	OFFSET(PACA_DSCR_DEFAULT, paca_struct, dscr_default);
> >>> +
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	OFFSET(PACA_AMR, paca_struct, paca_amr);
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>
> >> So we now have a place in PACA for AMR.
> > 
> > yes.
> > 
> >>
> >>>  	OFFSET(ACCOUNT_STARTTIME, paca_struct, accounting.starttime);
> >>>  	OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
> >>>  	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
> >>> diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> >>> index 3fd0528..8db9ef8 100644
> >>> --- a/arch/powerpc/kernel/exceptions-64s.S
> >>> +++ b/arch/powerpc/kernel/exceptions-64s.S
> >>> @@ -493,6 +493,10 @@ EXC_COMMON_BEGIN(data_access_common)
> >>>  	ld	r12,_MSR(r1)
> >>>  	ld	r3,PACA_EXGEN+EX_DAR(r13)
> >>>  	lwz	r4,PACA_EXGEN+EX_DSISR(r13)
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	mfspr	r5,SPRN_AMR
> >>> +	std	r5,PACA_AMR(r13)
> >>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>>  	li	r5,0x300
> >>>  	std	r3,_DAR(r1)
> >>>  	std	r4,_DSISR(r1)
> >>> @@ -561,6 +565,10 @@ EXC_COMMON_BEGIN(instruction_access_common)
> >>>  	ld	r12,_MSR(r1)
> >>>  	ld	r3,_NIP(r1)
> >>>  	andis.	r4,r12,0x5820
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	mfspr	r5,SPRN_AMR
> >>> +	std	r5,PACA_AMR(r13)
> >>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>
> >> Saving the AMR context on page faults, this seems to be
> >> changing in the next patch again based on whether any
> >> key was active at that point and fault happened for the
> >> key enforcement ?
> > 
> > yes. i am going to merge the next patch with this patch.
> > 
> > 
> >>
> >>>  	li	r5,0x400
> >>>  	std	r3,_DAR(r1)
> >>>  	std	r4,_DSISR(r1)
> >>> diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
> >>> index 97bb138..059766a 100644
> >>> --- a/arch/powerpc/kernel/signal_32.c
> >>> +++ b/arch/powerpc/kernel/signal_32.c
> >>> @@ -500,6 +500,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
> >>>  				   (unsigned long) &frame->tramp[2]);
> >>>  	}
> >>>
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	if (__put_user(get_paca()->paca_amr, &frame->mc_gregs[PT_AMR]))
> >>> +		return 1;
> >>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  	return 0;
> >>>  }
> >>>
> >>> @@ -661,6 +666,9 @@ static long restore_user_regs(struct pt_regs *regs,
> >>>  	long err;
> >>>  	unsigned int save_r2 = 0;
> >>>  	unsigned long msr;
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	unsigned long amr;
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>>  #ifdef CONFIG_VSX
> >>>  	int i;
> >>>  #endif
> >>> @@ -750,6 +758,12 @@ static long restore_user_regs(struct pt_regs *regs,
> >>>  		return 1;
> >>>  #endif /* CONFIG_SPE */
> >>>
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	err |= __get_user(amr, &sr->mc_gregs[PT_AMR]);
> >>> +	if (!err && amr != get_paca()->paca_amr)
> >>> +		write_amr(amr);
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  	return 0;
> >>>  }
> >>>
> >>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> >>> index c83c115..35df2e4 100644
> >>> --- a/arch/powerpc/kernel/signal_64.c
> >>> +++ b/arch/powerpc/kernel/signal_64.c
> >>> @@ -174,6 +174,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,
> >>>  	if (set != NULL)
> >>>  		err |=  __put_user(set->sig[0], &sc->oldmask);
> >>>
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	err |= __put_user(get_paca()->paca_amr, &sc->gp_regs[PT_AMR]);
> >>> +#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  	return err;
> >>>  }
> >>>
> >>> @@ -327,6 +331,9 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> >>>  	unsigned long save_r13 = 0;
> >>>  	unsigned long msr;
> >>>  	struct pt_regs *regs = tsk->thread.regs;
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	unsigned long amr;
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>>  #ifdef CONFIG_VSX
> >>>  	int i;
> >>>  #endif
> >>> @@ -406,6 +413,13 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> >>>  			tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
> >>>  	}
> >>>  #endif
> >>> +
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	err |= __get_user(amr, &sc->gp_regs[PT_AMR]);
> >>> +	if (!err && amr != get_paca()->paca_amr)
> >>> +		write_amr(amr);
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  	return err;
> >>>  }
> >>>
> >>> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> >>> index d4e545d..cc4bde8b 100644
> >>> --- a/arch/powerpc/kernel/traps.c
> >>> +++ b/arch/powerpc/kernel/traps.c
> >>> @@ -20,6 +20,7 @@
> >>>  #include <linux/sched/debug.h>
> >>>  #include <linux/kernel.h>
> >>>  #include <linux/mm.h>
> >>> +#include <linux/pkeys.h>
> >>>  #include <linux/stddef.h>
> >>>  #include <linux/unistd.h>
> >>>  #include <linux/ptrace.h>
> >>> @@ -247,6 +248,49 @@ void user_single_step_siginfo(struct task_struct *tsk,
> >>>  	info->si_addr = (void __user *)regs->nip;
> >>>  }
> >>>
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +static void fill_sig_info_pkey(int si_code, siginfo_t *info, unsigned long addr)
> >>> +{
> >>> +	struct vm_area_struct *vma;
> >>> +
> >>> +	/* Fault not from Protection Keys: nothing to do */
> >>> +	if (si_code != SEGV_PKUERR)
> >>> +		return;
> >>
> >> Should have checked this in the caller ?
> > 
> > maybe. currently there is only one caller to this function. so either
> > way is ok. But if more than one callers show up later having the check 
> > here reduces the burden on the caller.
> > 
> > 
> >>
> >>> +
> >>> +	down_read(&current->mm->mmap_sem);
> >>> +	/*
> >>> +	 * we could be racing with pkey_mprotect().
> >>> +	 * If pkey_mprotect() wins the key value could
> >>> +	 * get modified...xxx
> >>> +	 */
> >>> +	vma = find_vma(current->mm, addr);
> >>> +	up_read(&current->mm->mmap_sem);
> >>> +
> >>> +	/*
> >>> +	 * force_sig_info_fault() is called from a number of
> >>> +	 * contexts, some of which have a VMA and some of which
> >>> +	 * do not.  The Pkey-fault handing happens after we have a
> >>> +	 * valid VMA, so we should never reach this without a
> >>> +	 * valid VMA.
> >>> +	 */
> >>
> >> Also because pkey can only be used from user space when we will
> >> definitely have a VMA associated with it.
> >>
> >>> +	if (!vma) {
> >>> +		WARN_ONCE(1, "Pkey fault with no VMA passed in");
> >>> +		info->si_pkey = 0;
> >>> +		return;
> >>> +	}
> >>> +
> >>> +	/*
> >>> +	 * We could report the incorrect key because of the reason
> >>> +	 * explained above.
> >>
> >> What if we hold mm->mmap_sem for some more time till we update
> >> info->si_pkey ? Is there still a chance that pkey would have
> >> changed by the time siginfo returns to user space ? I am still
> >> wondering is there way to hold up VMA changes to be on safer
> >> side. Is the race conditions exists on x86 as well ?
> 
> Is the race condition exists on x86 system as well ? Can we hold
> up little more the mmap_sem to improve our chances ?
> 

Yes it exists on x86 as well. We could hold the sem longer, but that 
does not entirely solve the problem, at the same time we will slow down the
process.  The fool-proof way of solving the problem is if the hardware
told us which key got violated, instead of just telling us that there
was key violation. Depending on the value in the vma, is always going to
be racy.

> >>
> >>> +	 *
> >>> +	 * si_pkey should be thought off as trong hint, but not
> >>> +	 * an absolutely guarantee because of the race explained
> >>> +	 * above.
> >>> +	 */
> >>> +	info->si_pkey = vma_pkey(vma);
> >>> +}
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
> >>>  {
> >>>  	siginfo_t info;
> >>> @@ -274,6 +318,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
> >>>  	info.si_signo = signr;
> >>>  	info.si_code = code;
> >>>  	info.si_addr = (void __user *) addr;
> >>> +
> >>> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> >>> +	fill_sig_info_pkey(code, &info, addr);
> >>> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> >>> +
> >>>  	force_sig_info(signr, &info, current);
> >>>  }
> >>>
> >>> diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> >>> index c31624f..dd448d2 100644
> >>> --- a/arch/powerpc/mm/fault.c
> >>> +++ b/arch/powerpc/mm/fault.c
> >>> @@ -453,6 +453,10 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
> >>>  	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
> >>>  					flags & FAULT_FLAG_INSTRUCTION,
> >>>  					0)) {
> >>> +
> >>> +		/* our caller may not have saved the amr. Lets save it */
> >>> +		get_paca()->paca_amr = read_amr();
> >>> +
> >>
> >> Something is not right here. PACA save should have happened before we
> >> come here. Why say the caller might not have saved the AMR ? Is there
> >> a path when its possible ?
> > 
> > This is a case, where the fault is cause because of page not yet being
> > hashed, but at the same time could have violated a protection key.
> > Since the page is not hashed yet, there is no protection-key fault
> > and hence the caller would have not saved the AMR. We do it here to 
> > catch that case.
> 
> Is that because of the above optimization you have added in the page
> fault path ?

yes. exactly.

-- 
Ram Pai

[toc] | [prev] | [next] | [standalone]


#1668162 — [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception
Message-ID<tTd3Y-1kp-9@gated-at.bofh.it>
In reply to#1668160
Replace the magic number used to check for DSI exception
with a meaningful value.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 arch/powerpc/include/asm/reg.h       | 9 ++++++++-
 arch/powerpc/kernel/exceptions-64s.S | 2 +-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 7e50e47..2dcb8a1 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -272,16 +272,23 @@
 #define SPRN_DAR	0x013	/* Data Address Register */
 #define SPRN_DBCR	0x136	/* e300 Data Breakpoint Control Reg */
 #define SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
+#define   DSISR_BIT32		0x80000000	/* not defined */
 #define   DSISR_NOHPTE		0x40000000	/* no translation found */
+#define   DSISR_PAGEATTR_CONFLT	0x20000000	/* page attribute conflict */
+#define   DSISR_BIT35		0x10000000	/* not defined */
 #define   DSISR_PROTFAULT	0x08000000	/* protection fault */
 #define   DSISR_BADACCESS	0x04000000	/* bad access to CI or G */
 #define   DSISR_ISSTORE		0x02000000	/* access was a store */
 #define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
-#define   DSISR_NOSEGMENT	0x00200000	/* SLB miss */
 #define   DSISR_KEYFAULT	0x00200000	/* Key fault */
+#define   DSISR_BIT43		0x00100000	/* not defined */
 #define   DSISR_UNSUPP_MMU	0x00080000	/* Unsupported MMU config */
 #define   DSISR_SET_RC		0x00040000	/* Failed setting of R/C bits */
 #define   DSISR_PGDIRFAULT      0x00020000      /* Fault on page directory */
+#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
+				DSISR_PAGEATTR_CONFLT | \
+				DSISR_BADACCESS |       \
+				DSISR_BIT43)
 #define SPRN_TBRL	0x10C	/* Time Base Read Lower Register (user, R/O) */
 #define SPRN_TBRU	0x10D	/* Time Base Read Upper Register (user, R/O) */
 #define SPRN_CIR	0x11B	/* Chip Information Register (hyper, R/0) */
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index ae418b8..3fd0528 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -1411,7 +1411,7 @@ USE_TEXT_SECTION()
 	.balign	IFETCH_ALIGN_BYTES
 do_hash_page:
 #ifdef CONFIG_PPC_STD_MMU_64
-	andis.	r0,r4,0xa410		/* weird error? */
+	andis.	r0,r4,DSISR_PAGE_FAULT_MASK@h
 	bne-	handle_page_fault	/* if not, try to insert a HPTE */
 	andis.  r0,r4,DSISR_DABRMATCH@h
 	bne-    handle_dabr_fault
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1670406 — Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception

FromAnshuman Khandual <khandual@linux.vnet.ibm.com>
Date2017-06-20 10:20 +0200
SubjectRe: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception
Message-ID<tUmyf-67i-27@gated-at.bofh.it>
In reply to#1668162
On 06/17/2017 09:22 AM, Ram Pai wrote:
> Replace the magic number used to check for DSI exception
> with a meaningful value.
> 
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> ---
>  arch/powerpc/include/asm/reg.h       | 9 ++++++++-
>  arch/powerpc/kernel/exceptions-64s.S | 2 +-
>  2 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> index 7e50e47..2dcb8a1 100644
> --- a/arch/powerpc/include/asm/reg.h
> +++ b/arch/powerpc/include/asm/reg.h
> @@ -272,16 +272,23 @@
>  #define SPRN_DAR	0x013	/* Data Address Register */
>  #define SPRN_DBCR	0x136	/* e300 Data Breakpoint Control Reg */
>  #define SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
> +#define   DSISR_BIT32		0x80000000	/* not defined */
>  #define   DSISR_NOHPTE		0x40000000	/* no translation found */
> +#define   DSISR_PAGEATTR_CONFLT	0x20000000	/* page attribute conflict */
> +#define   DSISR_BIT35		0x10000000	/* not defined */
>  #define   DSISR_PROTFAULT	0x08000000	/* protection fault */
>  #define   DSISR_BADACCESS	0x04000000	/* bad access to CI or G */
>  #define   DSISR_ISSTORE		0x02000000	/* access was a store */
>  #define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
> -#define   DSISR_NOSEGMENT	0x00200000	/* SLB miss */
>  #define   DSISR_KEYFAULT	0x00200000	/* Key fault */
> +#define   DSISR_BIT43		0x00100000	/* not defined */
>  #define   DSISR_UNSUPP_MMU	0x00080000	/* Unsupported MMU config */
>  #define   DSISR_SET_RC		0x00040000	/* Failed setting of R/C bits */
>  #define   DSISR_PGDIRFAULT      0x00020000      /* Fault on page directory */
> +#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
> +				DSISR_PAGEATTR_CONFLT | \
> +				DSISR_BADACCESS |       \
> +				DSISR_BIT43)

Sorry missed this one. Seems like there are couple of unnecessary
line additions in the subsequent patch which adds the new PKEY
reason code.

-#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
-				DSISR_PAGEATTR_CONFLT | \
-				DSISR_BADACCESS |       \
+#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 |	\
+				DSISR_PAGEATTR_CONFLT |	\
+				DSISR_BADACCESS |	\
+				DSISR_KEYFAULT |	\
 				DSISR_BIT43)

[toc] | [prev] | [next] | [standalone]


#1671234 — Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 01:30 +0200
SubjectRe: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception
Message-ID<tUAKS-6CT-19@gated-at.bofh.it>
In reply to#1670406
On Tue, Jun 20, 2017 at 01:44:25PM +0530, Anshuman Khandual wrote:
> On 06/17/2017 09:22 AM, Ram Pai wrote:
> > Replace the magic number used to check for DSI exception
> > with a meaningful value.
> > 
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > ---
> >  arch/powerpc/include/asm/reg.h       | 9 ++++++++-
> >  arch/powerpc/kernel/exceptions-64s.S | 2 +-
> >  2 files changed, 9 insertions(+), 2 deletions(-)
> > 
> > diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> > index 7e50e47..2dcb8a1 100644
> > --- a/arch/powerpc/include/asm/reg.h
> > +++ b/arch/powerpc/include/asm/reg.h
> > @@ -272,16 +272,23 @@
> >  #define SPRN_DAR	0x013	/* Data Address Register */
> >  #define SPRN_DBCR	0x136	/* e300 Data Breakpoint Control Reg */
> >  #define SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
> > +#define   DSISR_BIT32		0x80000000	/* not defined */
> >  #define   DSISR_NOHPTE		0x40000000	/* no translation found */
> > +#define   DSISR_PAGEATTR_CONFLT	0x20000000	/* page attribute conflict */
> > +#define   DSISR_BIT35		0x10000000	/* not defined */
> >  #define   DSISR_PROTFAULT	0x08000000	/* protection fault */
> >  #define   DSISR_BADACCESS	0x04000000	/* bad access to CI or G */
> >  #define   DSISR_ISSTORE		0x02000000	/* access was a store */
> >  #define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
> > -#define   DSISR_NOSEGMENT	0x00200000	/* SLB miss */
> >  #define   DSISR_KEYFAULT	0x00200000	/* Key fault */
> > +#define   DSISR_BIT43		0x00100000	/* not defined */
> >  #define   DSISR_UNSUPP_MMU	0x00080000	/* Unsupported MMU config */
> >  #define   DSISR_SET_RC		0x00040000	/* Failed setting of R/C bits */
> >  #define   DSISR_PGDIRFAULT      0x00020000      /* Fault on page directory */
> > +#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
> > +				DSISR_PAGEATTR_CONFLT | \
> > +				DSISR_BADACCESS |       \
> > +				DSISR_BIT43)
> 
> Sorry missed this one. Seems like there are couple of unnecessary
> line additions in the subsequent patch which adds the new PKEY
> reason code.
> 
> -#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
> -				DSISR_PAGEATTR_CONFLT | \
> -				DSISR_BADACCESS |       \
> +#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 |	\
> +				DSISR_PAGEATTR_CONFLT |	\
> +				DSISR_BADACCESS |	\
> +				DSISR_KEYFAULT |	\
>  				DSISR_BIT43)

i like to see them separately, one per line. But than you are right.
that is not the convention in this file. So will change it accordingly.

thanks,
RP
> 
> 

-- 
Ram Pai

[toc] | [prev] | [next] | [standalone]


#1671433 — Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception

From"Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Date2017-06-21 09:30 +0200
SubjectRe: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception
Message-ID<tUIfn-34h-1@gated-at.bofh.it>
In reply to#1668162
Ram Pai <linuxram@us.ibm.com> writes:

> Replace the magic number used to check for DSI exception
> with a meaningful value.
>
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> ---
>  arch/powerpc/include/asm/reg.h       | 9 ++++++++-
>  arch/powerpc/kernel/exceptions-64s.S | 2 +-
>  2 files changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> index 7e50e47..2dcb8a1 100644
> --- a/arch/powerpc/include/asm/reg.h
> +++ b/arch/powerpc/include/asm/reg.h
> @@ -272,16 +272,23 @@
>  #define SPRN_DAR	0x013	/* Data Address Register */
>  #define SPRN_DBCR	0x136	/* e300 Data Breakpoint Control Reg */
>  #define SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
> +#define   DSISR_BIT32		0x80000000	/* not defined */
>  #define   DSISR_NOHPTE		0x40000000	/* no translation found */
> +#define   DSISR_PAGEATTR_CONFLT	0x20000000	/* page attribute conflict */
> +#define   DSISR_BIT35		0x10000000	/* not defined */
>  #define   DSISR_PROTFAULT	0x08000000	/* protection fault */
>  #define   DSISR_BADACCESS	0x04000000	/* bad access to CI or G */
>  #define   DSISR_ISSTORE		0x02000000	/* access was a store */
>  #define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
> -#define   DSISR_NOSEGMENT	0x00200000	/* SLB miss */
>  #define   DSISR_KEYFAULT	0x00200000	/* Key fault */
> +#define   DSISR_BIT43		0x00100000	/* not defined */
>  #define   DSISR_UNSUPP_MMU	0x00080000	/* Unsupported MMU config */
>  #define   DSISR_SET_RC		0x00040000	/* Failed setting of R/C bits */
>  #define   DSISR_PGDIRFAULT      0x00020000      /* Fault on page directory */
> +#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
> +				DSISR_PAGEATTR_CONFLT | \
> +				DSISR_BADACCESS |       \
> +				DSISR_BIT43)
>  #define SPRN_TBRL	0x10C	/* Time Base Read Lower Register (user, R/O) */
>  #define SPRN_TBRU	0x10D	/* Time Base Read Upper Register (user, R/O) */
>  #define SPRN_CIR	0x11B	/* Chip Information Register (hyper, R/0) */
> diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> index ae418b8..3fd0528 100644
> --- a/arch/powerpc/kernel/exceptions-64s.S
> +++ b/arch/powerpc/kernel/exceptions-64s.S
> @@ -1411,7 +1411,7 @@ USE_TEXT_SECTION()
>  	.balign	IFETCH_ALIGN_BYTES
>  do_hash_page:
>  #ifdef CONFIG_PPC_STD_MMU_64
> -	andis.	r0,r4,0xa410		/* weird error? */
> +	andis.	r0,r4,DSISR_PAGE_FAULT_MASK@h
>  	bne-	handle_page_fault	/* if not, try to insert a HPTE */
>  	andis.  r0,r4,DSISR_DABRMATCH@h
>  	bne-    handle_dabr_fault


Thanks for doing this. I always wondered what that 0xa410 indicates. Now
tha it is documented, I am wondering are those the only DSISR values
that we want to check early ? You also added few bit positions that is
expected to carry value 0 ? But then excluded BIT35. Any reason ?

-aneesh

[toc] | [prev] | [next] | [standalone]


#1671525 — Re: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 11:20 +0200
SubjectRe: [RFC v2 07/12] powerpc: Macro the mask used for checking DSI exception
Message-ID<tUJXQ-4eI-9@gated-at.bofh.it>
In reply to#1671433
On Wed, Jun 21, 2017 at 12:55:42PM +0530, Aneesh Kumar K.V wrote:
> Ram Pai <linuxram@us.ibm.com> writes:
> 
> > Replace the magic number used to check for DSI exception
> > with a meaningful value.
> >
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > ---
> >  arch/powerpc/include/asm/reg.h       | 9 ++++++++-
> >  arch/powerpc/kernel/exceptions-64s.S | 2 +-
> >  2 files changed, 9 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> > index 7e50e47..2dcb8a1 100644
> > --- a/arch/powerpc/include/asm/reg.h
> > +++ b/arch/powerpc/include/asm/reg.h
> > @@ -272,16 +272,23 @@
> >  #define SPRN_DAR	0x013	/* Data Address Register */
> >  #define SPRN_DBCR	0x136	/* e300 Data Breakpoint Control Reg */
> >  #define SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
> > +#define   DSISR_BIT32		0x80000000	/* not defined */
> >  #define   DSISR_NOHPTE		0x40000000	/* no translation found */
> > +#define   DSISR_PAGEATTR_CONFLT	0x20000000	/* page attribute conflict */
> > +#define   DSISR_BIT35		0x10000000	/* not defined */
> >  #define   DSISR_PROTFAULT	0x08000000	/* protection fault */
> >  #define   DSISR_BADACCESS	0x04000000	/* bad access to CI or G */
> >  #define   DSISR_ISSTORE		0x02000000	/* access was a store */
> >  #define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
> > -#define   DSISR_NOSEGMENT	0x00200000	/* SLB miss */
> >  #define   DSISR_KEYFAULT	0x00200000	/* Key fault */
> > +#define   DSISR_BIT43		0x00100000	/* not defined */
> >  #define   DSISR_UNSUPP_MMU	0x00080000	/* Unsupported MMU config */
> >  #define   DSISR_SET_RC		0x00040000	/* Failed setting of R/C bits */
> >  #define   DSISR_PGDIRFAULT      0x00020000      /* Fault on page directory */
> > +#define   DSISR_PAGE_FAULT_MASK (DSISR_BIT32 | \
> > +				DSISR_PAGEATTR_CONFLT | \
> > +				DSISR_BADACCESS |       \
> > +				DSISR_BIT43)
> >  #define SPRN_TBRL	0x10C	/* Time Base Read Lower Register (user, R/O) */
> >  #define SPRN_TBRU	0x10D	/* Time Base Read Upper Register (user, R/O) */
> >  #define SPRN_CIR	0x11B	/* Chip Information Register (hyper, R/0) */
> > diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> > index ae418b8..3fd0528 100644
> > --- a/arch/powerpc/kernel/exceptions-64s.S
> > +++ b/arch/powerpc/kernel/exceptions-64s.S
> > @@ -1411,7 +1411,7 @@ USE_TEXT_SECTION()
> >  	.balign	IFETCH_ALIGN_BYTES
> >  do_hash_page:
> >  #ifdef CONFIG_PPC_STD_MMU_64
> > -	andis.	r0,r4,0xa410		/* weird error? */
> > +	andis.	r0,r4,DSISR_PAGE_FAULT_MASK@h
> >  	bne-	handle_page_fault	/* if not, try to insert a HPTE */
> >  	andis.  r0,r4,DSISR_DABRMATCH@h
> >  	bne-    handle_dabr_fault
> 
> 
> Thanks for doing this. I always wondered what that 0xa410 indicates. Now
> tha it is documented, I am wondering are those the only DSISR values
> that we want to check early ? You also added few bit positions that is
> expected to carry value 0 ? But then excluded BIT35. Any reason ?

I did not look deeply into why the exact number 0xa410 was used in the
past.  I built the macro DSISR_PAGE_FAULT_MASK using whatever bits make
up 0xa410.  BIT35 if added to the DSISR_PAGE_FAULT_MASK would make it
0xb410. So I did not consider it.

However the macro for BIT35 is already defined in this patch, if that is what you were
looking for.
+#define   DSISR_BIT35              0x10000000      /* not defined */

RP

[toc] | [prev] | [next] | [standalone]


#1668163 — [RFC v2 04/12] powerpc: store and restore the pkey state across context switches.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 04/12] powerpc: store and restore the pkey state across context switches.
Message-ID<tTd3Y-1kp-7@gated-at.bofh.it>
In reply to#1668160
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 arch/powerpc/include/asm/processor.h |  5 +++++
 arch/powerpc/kernel/process.c        | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index a2123f2..1f714df 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -310,6 +310,11 @@ struct thread_struct {
 	struct thread_vr_state ckvr_state; /* Checkpointed VR state */
 	unsigned long	ckvrsave; /* Checkpointed VRSAVE */
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	unsigned long	amr;
+	unsigned long	iamr;
+	unsigned long	uamor;
+#endif
 #ifdef CONFIG_KVM_BOOK3S_32_HANDLER
 	void*		kvm_shadow_vcpu; /* KVM internal data */
 #endif /* CONFIG_KVM_BOOK3S_32_HANDLER */
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index baae104..37d001a 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1096,6 +1096,11 @@ static inline void save_sprs(struct thread_struct *t)
 		t->tar = mfspr(SPRN_TAR);
 	}
 #endif
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	t->amr = mfspr(SPRN_AMR);
+	t->iamr = mfspr(SPRN_IAMR);
+	t->uamor = mfspr(SPRN_UAMOR);
+#endif
 }
 
 static inline void restore_sprs(struct thread_struct *old_thread,
@@ -1131,6 +1136,14 @@ static inline void restore_sprs(struct thread_struct *old_thread,
 			mtspr(SPRN_TAR, new_thread->tar);
 	}
 #endif
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	if (old_thread->amr != new_thread->amr)
+		mtspr(SPRN_AMR, new_thread->amr);
+	if (old_thread->iamr != new_thread->iamr)
+		mtspr(SPRN_IAMR, new_thread->iamr);
+	if (old_thread->uamor != new_thread->uamor)
+		mtspr(SPRN_UAMOR, new_thread->uamor);
+#endif
 }
 
 struct task_struct *__switch_to(struct task_struct *prev,
@@ -1686,6 +1699,11 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
 	current->thread.tm_texasr = 0;
 	current->thread.tm_tfiar = 0;
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+	current->thread.amr   = 0x0ul;
+	current->thread.iamr  = 0x0ul;
+	current->thread.uamor = 0x0ul;
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 }
 EXPORT_SYMBOL(start_thread);
 
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1668164 — [RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call.
Message-ID<tTd3Y-1kp-15@gated-at.bofh.it>
In reply to#1668160
This system call, associates the pkey with PTE of all
pages corresponding to the given address range.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 arch/powerpc/include/asm/book3s/64/pgtable.h | 22 ++++++-
 arch/powerpc/include/asm/mman.h              | 29 +++++----
 arch/powerpc/include/asm/pkeys.h             | 21 ++++++-
 arch/powerpc/include/asm/systbl.h            |  1 +
 arch/powerpc/include/asm/unistd.h            |  4 +-
 arch/powerpc/include/uapi/asm/unistd.h       |  1 +
 arch/powerpc/mm/pkeys.c                      | 93 +++++++++++++++++++++++++++-
 include/linux/mm.h                           |  1 +
 8 files changed, 154 insertions(+), 18 deletions(-)

diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h
index 87e9a89..bc845cd 100644
--- a/arch/powerpc/include/asm/book3s/64/pgtable.h
+++ b/arch/powerpc/include/asm/book3s/64/pgtable.h
@@ -37,6 +37,7 @@
 #define _RPAGE_RSV2		0x0800000000000000UL
 #define _RPAGE_RSV3		0x0400000000000000UL
 #define _RPAGE_RSV4		0x0200000000000000UL
+#define _RPAGE_RSV5		0x00040UL
 
 #define _PAGE_PTE		0x4000000000000000UL	/* distinguishes PTEs from pointers */
 #define _PAGE_PRESENT		0x8000000000000000UL	/* pte contains a translation */
@@ -56,6 +57,20 @@
 /* Max physical address bit as per radix table */
 #define _RPAGE_PA_MAX		57
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+#define H_PAGE_PKEY_BIT0	_RPAGE_RSV1
+#define H_PAGE_PKEY_BIT1	_RPAGE_RSV2
+#define H_PAGE_PKEY_BIT2	_RPAGE_RSV3
+#define H_PAGE_PKEY_BIT3	_RPAGE_RSV4
+#define H_PAGE_PKEY_BIT4	_RPAGE_RSV5
+#else /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+#define H_PAGE_PKEY_BIT0	0
+#define H_PAGE_PKEY_BIT1	0
+#define H_PAGE_PKEY_BIT2	0
+#define H_PAGE_PKEY_BIT3	0
+#define H_PAGE_PKEY_BIT4	0
+#endif /*  CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
 /*
  * Max physical address bit we will use for now.
  *
@@ -122,7 +137,12 @@
 #define PAGE_PROT_BITS  (_PAGE_SAO | _PAGE_NON_IDEMPOTENT | _PAGE_TOLERANT | \
 			 H_PAGE_4K_PFN | _PAGE_PRIVILEGED | _PAGE_ACCESSED | \
 			 _PAGE_READ | _PAGE_WRITE |  _PAGE_DIRTY | _PAGE_EXEC | \
-			 _PAGE_SOFT_DIRTY)
+			 _PAGE_SOFT_DIRTY | \
+			 H_PAGE_PKEY_BIT0 | \
+			 H_PAGE_PKEY_BIT1 | \
+			 H_PAGE_PKEY_BIT2 | \
+			 H_PAGE_PKEY_BIT3 | \
+			 H_PAGE_PKEY_BIT4)
 /*
  * We define 2 sets of base prot bits, one for basic pages (ie,
  * cacheable kernel and user pages) and one for non cacheable
diff --git a/arch/powerpc/include/asm/mman.h b/arch/powerpc/include/asm/mman.h
index 30922f6..14cc1aa 100644
--- a/arch/powerpc/include/asm/mman.h
+++ b/arch/powerpc/include/asm/mman.h
@@ -13,24 +13,31 @@
 
 #include <asm/cputable.h>
 #include <linux/mm.h>
+#include <linux/pkeys.h>
 #include <asm/cpu_has_feature.h>
 
+#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
+
 /*
  * This file is included by linux/mman.h, so we can't use cacl_vm_prot_bits()
  * here.  How important is the optimization?
  */
-static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot,
-		unsigned long pkey)
-{
-	return (prot & PROT_SAO) ? VM_SAO : 0;
-}
-#define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey)
+#define arch_calc_vm_prot_bits(prot, key) (             \
+		((prot) & PROT_SAO ? VM_SAO : 0) |	\
+			pkey_to_vmflag_bits(key))
+#define arch_vm_get_page_prot(vm_flags) __pgprot(       \
+		((vm_flags) & VM_SAO ? _PAGE_SAO : 0) |	\
+		vmflag_to_page_pkey_bits(vm_flags))
+
+#else /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
+
+#define arch_calc_vm_prot_bits(prot, key) (	\
+		((prot) & PROT_SAO ? VM_SAO : 0))
+#define arch_vm_get_page_prot(vm_flags) __pgprot(	\
+		((vm_flags) & VM_SAO ? _PAGE_SAO : 0))
+
+#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 
-static inline pgprot_t arch_vm_get_page_prot(unsigned long vm_flags)
-{
-	return (vm_flags & VM_SAO) ? __pgprot(_PAGE_SAO) : __pgprot(0);
-}
-#define arch_vm_get_page_prot(vm_flags) arch_vm_get_page_prot(vm_flags)
 
 static inline bool arch_validate_prot(unsigned long prot)
 {
diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h
index 7bc8746..0f3dca8 100644
--- a/arch/powerpc/include/asm/pkeys.h
+++ b/arch/powerpc/include/asm/pkeys.h
@@ -14,6 +14,19 @@
 			VM_PKEY_BIT3 | \
 			VM_PKEY_BIT4)
 
+#define pkey_to_vmflag_bits(key) (((key & 0x1UL) ? VM_PKEY_BIT0 : 0x0UL) | \
+			((key & 0x2UL) ? VM_PKEY_BIT1 : 0x0UL) |	\
+			((key & 0x4UL) ? VM_PKEY_BIT2 : 0x0UL) |	\
+			((key & 0x8UL) ? VM_PKEY_BIT3 : 0x0UL) |	\
+			((key & 0x10UL) ? VM_PKEY_BIT4 : 0x0UL))
+
+#define vmflag_to_page_pkey_bits(vm_flags)  \
+		(((vm_flags & VM_PKEY_BIT0) ? H_PAGE_PKEY_BIT4 : 0x0UL)|     \
+		((vm_flags & VM_PKEY_BIT1) ? H_PAGE_PKEY_BIT3 : 0x0UL) |     \
+		((vm_flags & VM_PKEY_BIT2) ? H_PAGE_PKEY_BIT2 : 0x0UL) |     \
+		((vm_flags & VM_PKEY_BIT3) ? H_PAGE_PKEY_BIT1 : 0x0UL) |     \
+		((vm_flags & VM_PKEY_BIT4) ? H_PAGE_PKEY_BIT0 : 0x0UL))
+
 /*
  * Bits are in BE format.
  * NOTE: key 31, 1, 0 are not used.
@@ -42,6 +55,12 @@
 #define mm_set_pkey_is_reserved(mm, pkey) (PKEY_INITIAL_ALLOCAION & \
 					pkeybit_mask(pkey))
 
+
+static inline int vma_pkey(struct vm_area_struct *vma)
+{
+	return (vma->vm_flags & ARCH_VM_PKEY_FLAGS) >> VM_PKEY_SHIFT;
+}
+
 static inline bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey)
 {
 	/* a reserved key is never considered as 'explicitly allocated' */
@@ -114,7 +133,7 @@ static inline int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
 	return __arch_set_user_pkey_access(tsk, pkey, init_val);
 }
 
-static inline pkey_mm_init(struct mm_struct *mm)
+static inline void pkey_mm_init(struct mm_struct *mm)
 {
 	mm_pkey_allocation_map(mm) = PKEY_INITIAL_ALLOCAION;
 	/* -1 means unallocated or invalid */
diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
index 22dd776..b33b551 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -390,3 +390,4 @@
 SYSCALL(statx)
 SYSCALL(pkey_alloc)
 SYSCALL(pkey_free)
+SYSCALL(pkey_mprotect)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index e0273bc..daf1ba9 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -12,12 +12,10 @@
 #include <uapi/asm/unistd.h>
 
 
-#define NR_syscalls		386
+#define NR_syscalls		387
 
 #define __NR__exit __NR_exit
 
-#define __IGNORE_pkey_mprotect
-
 #ifndef __ASSEMBLY__
 
 #include <linux/types.h>
diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h
index 7993a07..71ae45e 100644
--- a/arch/powerpc/include/uapi/asm/unistd.h
+++ b/arch/powerpc/include/uapi/asm/unistd.h
@@ -396,5 +396,6 @@
 #define __NR_statx		383
 #define __NR_pkey_alloc		384
 #define __NR_pkey_free		385
+#define __NR_pkey_mprotect	386
 
 #endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
index b97366e..11a32b3 100644
--- a/arch/powerpc/mm/pkeys.c
+++ b/arch/powerpc/mm/pkeys.c
@@ -15,6 +15,17 @@
 #include <linux/pkeys.h>                /* PKEY_*                       */
 #include <uapi/asm-generic/mman-common.h>
 
+#define pkeyshift(pkey) ((arch_max_pkey()-pkey-1) * AMR_BITS_PER_PKEY)
+
+static inline bool pkey_allows_readwrite(int pkey)
+{
+	int pkey_shift = pkeyshift(pkey);
+
+	if (!(read_uamor() & (0x3UL << pkey_shift)))
+		return true;
+
+	return !(read_amr() & ((AMR_AD_BIT|AMR_WD_BIT) << pkey_shift));
+}
 
 /*
  * set the access right in AMR IAMR and UAMOR register
@@ -68,7 +79,60 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
 
 int __execute_only_pkey(struct mm_struct *mm)
 {
-	return -1;
+	bool need_to_set_mm_pkey = false;
+	int execute_only_pkey = mm->context.execute_only_pkey;
+	int ret;
+
+	/* Do we need to assign a pkey for mm's execute-only maps? */
+	if (execute_only_pkey == -1) {
+		/* Go allocate one to use, which might fail */
+		execute_only_pkey = mm_pkey_alloc(mm);
+		if (execute_only_pkey < 0)
+			return -1;
+		need_to_set_mm_pkey = true;
+	}
+
+	/*
+	 * We do not want to go through the relatively costly
+	 * dance to set AMR if we do not need to.  Check it
+	 * first and assume that if the execute-only pkey is
+	 * readwrite-disabled than we do not have to set it
+	 * ourselves.
+	 */
+	if (!need_to_set_mm_pkey &&
+	    !pkey_allows_readwrite(execute_only_pkey))
+		return execute_only_pkey;
+
+	/*
+	 * Set up AMR so that it denies access for everything
+	 * other than execution.
+	 */
+	ret = __arch_set_user_pkey_access(current, execute_only_pkey,
+			(PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
+	/*
+	 * If the AMR-set operation failed somehow, just return
+	 * 0 and effectively disable execute-only support.
+	 */
+	if (ret) {
+		mm_set_pkey_free(mm, execute_only_pkey);
+		return -1;
+	}
+
+	/* We got one, store it and use it from here on out */
+	if (need_to_set_mm_pkey)
+		mm->context.execute_only_pkey = execute_only_pkey;
+	return execute_only_pkey;
+}
+
+static inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma)
+{
+	/* Do this check first since the vm_flags should be hot */
+	if ((vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)) != VM_EXEC)
+		return false;
+	if (vma_pkey(vma) != vma->vm_mm->context.execute_only_pkey)
+		return false;
+
+	return true;
 }
 
 /*
@@ -84,5 +148,30 @@ int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot,
 	if (pkey != -1)
 		return pkey;
 
-	return 0;
+	/*
+	 * Look for a protection-key-drive execute-only mapping
+	 * which is now being given permissions that are not
+	 * execute-only.  Move it back to the default pkey.
+	 */
+	if (vma_is_pkey_exec_only(vma) &&
+	    (prot & (PROT_READ|PROT_WRITE))) {
+		return 0;
+	}
+	/*
+	 * The mapping is execute-only.  Go try to get the
+	 * execute-only protection key.  If we fail to do that,
+	 * fall through as if we do not have execute-only
+	 * support.
+	 */
+	if (prot == PROT_EXEC) {
+		pkey = execute_only_pkey(vma->vm_mm);
+		if (pkey > 0)
+			return pkey;
+	}
+	/*
+	 * This is a vanilla, non-pkey mprotect (or we failed to
+	 * setup execute-only), inherit the pkey from the VMA we
+	 * are working on.
+	 */
+	return vma_pkey(vma);
 }
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 34ddac7..5399031 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -227,6 +227,7 @@ extern int overcommit_kbytes_handler(struct ctl_table *, int, void __user *,
 #define VM_PKEY_BIT3	VM_HIGH_ARCH_3
 #endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
 #elif defined(CONFIG_PPC)
+#define VM_PKEY_SHIFT	VM_HIGH_ARCH_BIT_0
 #define VM_PKEY_BIT0	VM_HIGH_ARCH_0	/* A protection key is a 5-bit value */
 #define VM_PKEY_BIT1	VM_HIGH_ARCH_1
 #define VM_PKEY_BIT2	VM_HIGH_ARCH_2
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1671431 — Re: [RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call.

From"Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Date2017-06-21 09:20 +0200
SubjectRe: [RFC v2 05/12] powerpc: Implementation for sys_mprotect_pkey() system call.
Message-ID<tUI5K-31b-43@gated-at.bofh.it>
In reply to#1668164
Ram Pai <linuxram@us.ibm.com> writes:

....

>
> +#ifdef CONFIG_PPC64_MEMORY_PROTECTION_KEYS
> +
>  /*
>   * This file is included by linux/mman.h, so we can't use cacl_vm_prot_bits()
>   * here.  How important is the optimization?
>   */
> -static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot,
> -		unsigned long pkey)
> -{
> -	return (prot & PROT_SAO) ? VM_SAO : 0;
> -}
> -#define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey)
> +#define arch_calc_vm_prot_bits(prot, key) (             \
> +		((prot) & PROT_SAO ? VM_SAO : 0) |	\
> +			pkey_to_vmflag_bits(key))
> +#define arch_vm_get_page_prot(vm_flags) __pgprot(       \
> +		((vm_flags) & VM_SAO ? _PAGE_SAO : 0) |	\
> +		vmflag_to_page_pkey_bits(vm_flags))

Can we avoid converting static inline back to macors ? They loose type checking.
> +
> +#else /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
> +
> +#define arch_calc_vm_prot_bits(prot, key) (	\
> +		((prot) & PROT_SAO ? VM_SAO : 0))
> +#define arch_vm_get_page_prot(vm_flags) __pgprot(	\
> +		((vm_flags) & VM_SAO ? _PAGE_SAO : 0))
> +
> +#endif /* CONFIG_PPC64_MEMORY_PROTECTION_KEYS */
>
> -static inline pgprot_t arch_vm_get_page_prot(unsigned long vm_flags)
> -{
> -	return (vm_flags & VM_SAO) ? __pgprot(_PAGE_SAO) : __pgprot(0);
> -}
> -#define arch_vm_get_page_prot(vm_flags) arch_vm_get_page_prot(vm_flags)
>
>  static inline bool arch_validate_prot(unsigned long prot)
>  {

-aneesh

[toc] | [prev] | [next] | [standalone]


#1668165 — [RFC v2 11/12]Documentation: Documentation updates.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 11/12]Documentation: Documentation updates.
Message-ID<tTd3Y-1kp-21@gated-at.bofh.it>
In reply to#1668160
The Documentaton file is moved from x86 into the generic area,
since this feature is now supported by more than one archs.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 Documentation/vm/protection-keys.txt  | 110 ++++++++++++++++++++++++++++++++++
 Documentation/x86/protection-keys.txt |  85 --------------------------
 2 files changed, 110 insertions(+), 85 deletions(-)
 create mode 100644 Documentation/vm/protection-keys.txt
 delete mode 100644 Documentation/x86/protection-keys.txt

diff --git a/Documentation/vm/protection-keys.txt b/Documentation/vm/protection-keys.txt
new file mode 100644
index 0000000..b49e6bb
--- /dev/null
+++ b/Documentation/vm/protection-keys.txt
@@ -0,0 +1,110 @@
+Memory Protection Keys for Userspace (PKU aka PKEYs) is a CPU feature
+found in new generation of intel CPUs on PowerPC CPUs.
+
+Memory Protection Keys provides a mechanism for enforcing page-based
+protections, but without requiring modification of the page tables
+when an application changes protection domains.
+
+
+On Intel:
+
+It works by dedicating 4 previously ignored bits in each page table
+entry to a "protection key", giving 16 possible keys.
+
+There is also a new user-accessible register (PKRU) with two separate
+bits (Access Disable and Write Disable) for each key.  Being a CPU
+register, PKRU is inherently thread-local, potentially giving each
+thread a different set of protections from every other thread.
+
+There are two new instructions (RDPKRU/WRPKRU) for reading and writing
+to the new register.  The feature is only available in 64-bit mode,
+even though there is theoretically space in the PAE PTEs.  These
+permissions are enforced on data access only and have no effect on
+instruction fetches.
+
+
+On PowerPC:
+
+It works by dedicating 5 page table entry to a "protection key",
+giving 32 possible keys.
+
+There is a user-accessible register (AMR) with two separate bits
+(Access Disable and Write Disable) for each key.  Being a CPU
+register, AMR is inherently thread-local, potentially giving each
+thread a different set of protections from every other thread.
+NOTE: Disabling read permission does not disable
+write and vice-versa.
+
+The feature is available on 64-bit HPTE mode only.
+
+'mtspr 0xd, mem' reads the AMR register
+'mfspr mem, 0xd' writes into the AMR register.
+
+Permissions are enforced on data access only and have no effect on
+instruction fetches.
+
+=========================== Syscalls ===========================
+
+There are 3 system calls which directly interact with pkeys:
+
+	int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+	int pkey_free(int pkey);
+	int pkey_mprotect(unsigned long start, size_t len,
+			  unsigned long prot, int pkey);
+
+Before a pkey can be used, it must first be allocated with
+pkey_alloc().  An application calls the WRPKRU instruction
+directly in order to change access permissions to memory covered
+with a key.  In this example WRPKRU is wrapped by a C function
+called pkey_set().
+
+	int real_prot = PROT_READ|PROT_WRITE;
+	pkey = pkey_alloc(0, PKEY_DENY_WRITE);
+	ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+	ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey);
+	... application runs here
+
+Now, if the application needs to update the data at 'ptr', it can
+gain access, do the update, then remove its write access:
+
+	pkey_set(pkey, 0); // clear PKEY_DENY_WRITE
+	*ptr = foo; // assign something
+	pkey_set(pkey, PKEY_DENY_WRITE); // set PKEY_DENY_WRITE again
+
+Now when it frees the memory, it will also free the pkey since it
+is no longer in use:
+
+	munmap(ptr, PAGE_SIZE);
+	pkey_free(pkey);
+
+(Note: pkey_set() is a wrapper for the RDPKRU and WRPKRU instructions.
+ An example implementation can be found in
+ tools/testing/selftests/x86/protection_keys.c)
+
+=========================== Behavior ===========================
+
+The kernel attempts to make protection keys consistent with the
+behavior of a plain mprotect().  For instance if you do this:
+
+	mprotect(ptr, size, PROT_NONE);
+	something(ptr);
+
+you can expect the same effects with protection keys when doing this:
+
+	pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ);
+	pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey);
+	something(ptr);
+
+That should be true whether something() is a direct access to 'ptr'
+like:
+
+	*ptr = foo;
+
+or when the kernel does the access on the application's behalf like
+with a read():
+
+	read(fd, ptr, 1);
+
+The kernel will send a SIGSEGV in both cases, but si_code will be set
+to SEGV_PKERR when violating protection keys versus SEGV_ACCERR when
+the plain mprotect() permissions are violated.
diff --git a/Documentation/x86/protection-keys.txt b/Documentation/x86/protection-keys.txt
deleted file mode 100644
index b643045..0000000
--- a/Documentation/x86/protection-keys.txt
+++ /dev/null
@@ -1,85 +0,0 @@
-Memory Protection Keys for Userspace (PKU aka PKEYs) is a CPU feature
-which will be found on future Intel CPUs.
-
-Memory Protection Keys provides a mechanism for enforcing page-based
-protections, but without requiring modification of the page tables
-when an application changes protection domains.  It works by
-dedicating 4 previously ignored bits in each page table entry to a
-"protection key", giving 16 possible keys.
-
-There is also a new user-accessible register (PKRU) with two separate
-bits (Access Disable and Write Disable) for each key.  Being a CPU
-register, PKRU is inherently thread-local, potentially giving each
-thread a different set of protections from every other thread.
-
-There are two new instructions (RDPKRU/WRPKRU) for reading and writing
-to the new register.  The feature is only available in 64-bit mode,
-even though there is theoretically space in the PAE PTEs.  These
-permissions are enforced on data access only and have no effect on
-instruction fetches.
-
-=========================== Syscalls ===========================
-
-There are 3 system calls which directly interact with pkeys:
-
-	int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
-	int pkey_free(int pkey);
-	int pkey_mprotect(unsigned long start, size_t len,
-			  unsigned long prot, int pkey);
-
-Before a pkey can be used, it must first be allocated with
-pkey_alloc().  An application calls the WRPKRU instruction
-directly in order to change access permissions to memory covered
-with a key.  In this example WRPKRU is wrapped by a C function
-called pkey_set().
-
-	int real_prot = PROT_READ|PROT_WRITE;
-	pkey = pkey_alloc(0, PKEY_DENY_WRITE);
-	ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
-	ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey);
-	... application runs here
-
-Now, if the application needs to update the data at 'ptr', it can
-gain access, do the update, then remove its write access:
-
-	pkey_set(pkey, 0); // clear PKEY_DENY_WRITE
-	*ptr = foo; // assign something
-	pkey_set(pkey, PKEY_DENY_WRITE); // set PKEY_DENY_WRITE again
-
-Now when it frees the memory, it will also free the pkey since it
-is no longer in use:
-
-	munmap(ptr, PAGE_SIZE);
-	pkey_free(pkey);
-
-(Note: pkey_set() is a wrapper for the RDPKRU and WRPKRU instructions.
- An example implementation can be found in
- tools/testing/selftests/x86/protection_keys.c)
-
-=========================== Behavior ===========================
-
-The kernel attempts to make protection keys consistent with the
-behavior of a plain mprotect().  For instance if you do this:
-
-	mprotect(ptr, size, PROT_NONE);
-	something(ptr);
-
-you can expect the same effects with protection keys when doing this:
-
-	pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ);
-	pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey);
-	something(ptr);
-
-That should be true whether something() is a direct access to 'ptr'
-like:
-
-	*ptr = foo;
-
-or when the kernel does the access on the application's behalf like
-with a read():
-
-	read(fd, ptr, 1);
-
-The kernel will send a SIGSEGV in both cases, but si_code will be set
-to SEGV_PKERR when violating protection keys versus SEGV_ACCERR when
-the plain mprotect() permissions are violated.
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1670317 — Re: [RFC v2 11/12]Documentation: Documentation updates.

FromAnshuman Khandual <khandual@linux.vnet.ibm.com>
Date2017-06-20 08:20 +0200
SubjectRe: [RFC v2 11/12]Documentation: Documentation updates.
Message-ID<tUkG6-4V1-21@gated-at.bofh.it>
In reply to#1668165
On 06/17/2017 09:22 AM, Ram Pai wrote:
> The Documentaton file is moved from x86 into the generic area,
> since this feature is now supported by more than one archs.
> 
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> ---
>  Documentation/vm/protection-keys.txt  | 110 ++++++++++++++++++++++++++++++++++
>  Documentation/x86/protection-keys.txt |  85 --------------------------

I am not sure whether this is a good idea. There might be
specifics for each architecture which need to be detailed
again in this new generic one.

>  2 files changed, 110 insertions(+), 85 deletions(-)
>  create mode 100644 Documentation/vm/protection-keys.txt
>  delete mode 100644 Documentation/x86/protection-keys.txt
> 
> diff --git a/Documentation/vm/protection-keys.txt b/Documentation/vm/protection-keys.txt
> new file mode 100644
> index 0000000..b49e6bb
> --- /dev/null
> +++ b/Documentation/vm/protection-keys.txt
> @@ -0,0 +1,110 @@
> +Memory Protection Keys for Userspace (PKU aka PKEYs) is a CPU feature
> +found in new generation of intel CPUs on PowerPC CPUs.
> +
> +Memory Protection Keys provides a mechanism for enforcing page-based
> +protections, but without requiring modification of the page tables
> +when an application changes protection domains.

Does resultant access through protection keys should be a
subset of the protection bits enabled through original PTE
PROT format ? Does the semantics exactly the same on x86
and powerpc ?

> +
> +
> +On Intel:
> +
> +It works by dedicating 4 previously ignored bits in each page table
> +entry to a "protection key", giving 16 possible keys.
> +
> +There is also a new user-accessible register (PKRU) with two separate
> +bits (Access Disable and Write Disable) for each key.  Being a CPU
> +register, PKRU is inherently thread-local, potentially giving each
> +thread a different set of protections from every other thread.
> +
> +There are two new instructions (RDPKRU/WRPKRU) for reading and writing
> +to the new register.  The feature is only available in 64-bit mode,
> +even though there is theoretically space in the PAE PTEs.  These
> +permissions are enforced on data access only and have no effect on
> +instruction fetches.
> +
> +
> +On PowerPC:
> +
> +It works by dedicating 5 page table entry to a "protection key",
> +giving 32 possible keys.
> +
> +There is a user-accessible register (AMR) with two separate bits
> +(Access Disable and Write Disable) for each key.  Being a CPU
> +register, AMR is inherently thread-local, potentially giving each
> +thread a different set of protections from every other thread.

Small nit. Space needed here.

> +NOTE: Disabling read permission does not disable
> +write and vice-versa.
> +
> +The feature is available on 64-bit HPTE mode only.
> +
> +'mtspr 0xd, mem' reads the AMR register
> +'mfspr mem, 0xd' writes into the AMR register.
> +
> +Permissions are enforced on data access only and have no effect on
> +instruction fetches.
> +
> +=========================== Syscalls ===========================
> +
> +There are 3 system calls which directly interact with pkeys:
> +
> +	int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
> +	int pkey_free(int pkey);
> +	int pkey_mprotect(unsigned long start, size_t len,
> +			  unsigned long prot, int pkey);
> +
> +Before a pkey can be used, it must first be allocated with
> +pkey_alloc().  An application calls the WRPKRU instruction
> +directly in order to change access permissions to memory covered
> +with a key.  In this example WRPKRU is wrapped by a C function
> +called pkey_set().
> +
> +	int real_prot = PROT_READ|PROT_WRITE;
> +	pkey = pkey_alloc(0, PKEY_DENY_WRITE);
> +	ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
> +	ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey);
> +	... application runs here
> +
> +Now, if the application needs to update the data at 'ptr', it can
> +gain access, do the update, then remove its write access:
> +
> +	pkey_set(pkey, 0); // clear PKEY_DENY_WRITE
> +	*ptr = foo; // assign something
> +	pkey_set(pkey, PKEY_DENY_WRITE); // set PKEY_DENY_WRITE again
> +
> +Now when it frees the memory, it will also free the pkey since it
> +is no longer in use:
> +
> +	munmap(ptr, PAGE_SIZE);
> +	pkey_free(pkey);
> +
> +(Note: pkey_set() is a wrapper for the RDPKRU and WRPKRU instructions.
> + An example implementation can be found in
> + tools/testing/selftests/x86/protection_keys.c)
> +
> +=========================== Behavior ===========================
> +
> +The kernel attempts to make protection keys consistent with the
> +behavior of a plain mprotect().  For instance if you do this:
> +
> +	mprotect(ptr, size, PROT_NONE);
> +	something(ptr);
> +
> +you can expect the same effects with protection keys when doing this:
> +
> +	pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ);
> +	pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey);
> +	something(ptr);
> +
> +That should be true whether something() is a direct access to 'ptr'
> +like:
> +
> +	*ptr = foo;
> +
> +or when the kernel does the access on the application's behalf like
> +with a read():
> +
> +	read(fd, ptr, 1);
> +
> +The kernel will send a SIGSEGV in both cases, but si_code will be set
> +to SEGV_PKERR when violating protection keys versus SEGV_ACCERR when
> +the plain mprotect() permissions are violated.

I guess the right thing would be to have three files

* Documentation/vm/protection-keys.txt

	- Generic interface, system calls
	- Signal handling, error codes
	- Semantics of programming with an example

* Documentation/x86/protection-keys.txt

	- Number of active protections keys inside an address space
	- X86 protection key instruction details
	- PTE protection bits placement details
	- Page fault handling
	- Implementation details a bit ?

* Documentation/powerpc/protection-keys.txt

	- Number of active protections keys inside an address space
	- Powerpc instructions details
	- PTE protection bits placement details
	- Page fault handling
	- Implementation details a bit ?

[toc] | [prev] | [next] | [standalone]


#1671252 — Re: [RFC v2 11/12]Documentation: Documentation updates.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 02:10 +0200
SubjectRe: [RFC v2 11/12]Documentation: Documentation updates.
Message-ID<tUBnA-77n-11@gated-at.bofh.it>
In reply to#1670317
On Tue, Jun 20, 2017 at 11:48:23AM +0530, Anshuman Khandual wrote:
> On 06/17/2017 09:22 AM, Ram Pai wrote:
> > The Documentaton file is moved from x86 into the generic area,
> > since this feature is now supported by more than one archs.
> > 
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > ---
> >  Documentation/vm/protection-keys.txt  | 110 ++++++++++++++++++++++++++++++++++
> >  Documentation/x86/protection-keys.txt |  85 --------------------------
> 
> I am not sure whether this is a good idea. There might be
> specifics for each architecture which need to be detailed
> again in this new generic one.
> 
> >  2 files changed, 110 insertions(+), 85 deletions(-)
> >  create mode 100644 Documentation/vm/protection-keys.txt
> >  delete mode 100644 Documentation/x86/protection-keys.txt
> > 
> > diff --git a/Documentation/vm/protection-keys.txt b/Documentation/vm/protection-keys.txt
> > new file mode 100644
> > index 0000000..b49e6bb
> > --- /dev/null
> > +++ b/Documentation/vm/protection-keys.txt
> > @@ -0,0 +1,110 @@
> > +Memory Protection Keys for Userspace (PKU aka PKEYs) is a CPU feature
> > +found in new generation of intel CPUs on PowerPC CPUs.
> > +
> > +Memory Protection Keys provides a mechanism for enforcing page-based
> > +protections, but without requiring modification of the page tables
> > +when an application changes protection domains.
> 
> Does resultant access through protection keys should be a
> subset of the protection bits enabled through original PTE
> PROT format ? Does the semantics exactly the same on x86
> and powerpc ?

The protection key takes precedence over protection done through
mprotect. 
Yes both on x86 and powerpc we maintain the same semantics.
> 
> > +
> > +
> > +On Intel:
> > +
> > +It works by dedicating 4 previously ignored bits in each page table
> > +entry to a "protection key", giving 16 possible keys.
> > +
> > +There is also a new user-accessible register (PKRU) with two separate
> > +bits (Access Disable and Write Disable) for each key.  Being a CPU
> > +register, PKRU is inherently thread-local, potentially giving each
> > +thread a different set of protections from every other thread.
> > +
> > +There are two new instructions (RDPKRU/WRPKRU) for reading and writing
> > +to the new register.  The feature is only available in 64-bit mode,
> > +even though there is theoretically space in the PAE PTEs.  These
> > +permissions are enforced on data access only and have no effect on
> > +instruction fetches.
> > +
> > +
> > +On PowerPC:
> > +
> > +It works by dedicating 5 page table entry to a "protection key",
> > +giving 32 possible keys.
> > +
> > +There is a user-accessible register (AMR) with two separate bits
> > +(Access Disable and Write Disable) for each key.  Being a CPU
> > +register, AMR is inherently thread-local, potentially giving each
> > +thread a different set of protections from every other thread.
> 
> Small nit. Space needed here.
> 
> > +NOTE: Disabling read permission does not disable
> > +write and vice-versa.
> > +
> > +The feature is available on 64-bit HPTE mode only.
> > +
> > +'mtspr 0xd, mem' reads the AMR register
> > +'mfspr mem, 0xd' writes into the AMR register.
> > +
> > +Permissions are enforced on data access only and have no effect on
> > +instruction fetches.
> > +
> > +=========================== Syscalls ===========================
> > +
> > +There are 3 system calls which directly interact with pkeys:
> > +
> > +	int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
> > +	int pkey_free(int pkey);
> > +	int pkey_mprotect(unsigned long start, size_t len,
> > +			  unsigned long prot, int pkey);
> > +
> > +Before a pkey can be used, it must first be allocated with
> > +pkey_alloc().  An application calls the WRPKRU instruction
> > +directly in order to change access permissions to memory covered
> > +with a key.  In this example WRPKRU is wrapped by a C function
> > +called pkey_set().
> > +
> > +	int real_prot = PROT_READ|PROT_WRITE;
> > +	pkey = pkey_alloc(0, PKEY_DENY_WRITE);
> > +	ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
> > +	ret = pkey_mprotect(ptr, PAGE_SIZE, real_prot, pkey);
> > +	... application runs here
> > +
> > +Now, if the application needs to update the data at 'ptr', it can
> > +gain access, do the update, then remove its write access:
> > +
> > +	pkey_set(pkey, 0); // clear PKEY_DENY_WRITE
> > +	*ptr = foo; // assign something
> > +	pkey_set(pkey, PKEY_DENY_WRITE); // set PKEY_DENY_WRITE again
> > +
> > +Now when it frees the memory, it will also free the pkey since it
> > +is no longer in use:
> > +
> > +	munmap(ptr, PAGE_SIZE);
> > +	pkey_free(pkey);
> > +
> > +(Note: pkey_set() is a wrapper for the RDPKRU and WRPKRU instructions.
> > + An example implementation can be found in
> > + tools/testing/selftests/x86/protection_keys.c)
> > +
> > +=========================== Behavior ===========================
> > +
> > +The kernel attempts to make protection keys consistent with the
> > +behavior of a plain mprotect().  For instance if you do this:
> > +
> > +	mprotect(ptr, size, PROT_NONE);
> > +	something(ptr);
> > +
> > +you can expect the same effects with protection keys when doing this:
> > +
> > +	pkey = pkey_alloc(0, PKEY_DISABLE_WRITE | PKEY_DISABLE_READ);
> > +	pkey_mprotect(ptr, size, PROT_READ|PROT_WRITE, pkey);
> > +	something(ptr);
> > +
> > +That should be true whether something() is a direct access to 'ptr'
> > +like:
> > +
> > +	*ptr = foo;
> > +
> > +or when the kernel does the access on the application's behalf like
> > +with a read():
> > +
> > +	read(fd, ptr, 1);
> > +
> > +The kernel will send a SIGSEGV in both cases, but si_code will be set
> > +to SEGV_PKERR when violating protection keys versus SEGV_ACCERR when
> > +the plain mprotect() permissions are violated.
> 
> I guess the right thing would be to have three files
> 
> * Documentation/vm/protection-keys.txt
> 
> 	- Generic interface, system calls
> 	- Signal handling, error codes
> 	- Semantics of programming with an example
> 
> * Documentation/x86/protection-keys.txt
> 
> 	- Number of active protections keys inside an address space
> 	- X86 protection key instruction details
> 	- PTE protection bits placement details
> 	- Page fault handling
> 	- Implementation details a bit ?
> 
> * Documentation/powerpc/protection-keys.txt
> 
> 	- Number of active protections keys inside an address space
> 	- Powerpc instructions details
> 	- PTE protection bits placement details
> 	- Page fault handling
> 	- Implementation details a bit ?

I see the value of your suggestion. This is something that will touch
atleast two architectures. Want to hear some more inputs before I do the
changes.  

Dave Hansen:  would like to hear your ideas.

RP

[toc] | [prev] | [next] | [standalone]


#1668166 — [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-17 06:00 +0200
Subject[RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.
Message-ID<tTd3Y-1kp-11@gated-at.bofh.it>
In reply to#1668160
Rearrange 64K PTE bits to  free  up  bits 3, 4, 5  and  6
in the 4K backed hpte pages. These bits continue to be used
for 64K backed hpte pages in this patch, but will be freed
up in the next patch.

The patch does the following change to the 64K PTE format

H_PAGE_BUSY moves from bit 3 to bit 9
H_PAGE_F_SECOND which occupied bit 4 moves to the second part
	of the pte.
H_PAGE_F_GIX which  occupied bit 5, 6 and 7 also moves to the
	second part of the pte.

the four  bits((H_PAGE_F_SECOND|H_PAGE_F_GIX) that represent a slot
is  initialized  to  0xF  indicating  an invalid  slot.  If  a hpte
gets cached in a 0xF  slot(i.e  7th  slot  of  secondary),  it   is
released immediately. In  other  words, even  though   0xF   is   a
valid slot we discard  and consider it as an invalid
slot;i.e hpte_soft_invalid(). This  gives  us  an opportunity to not
depend on a bit in the primary PTE in order to determine the
validity of a slot.

When  we  release  a    hpte   in the 0xF   slot we also   release a
legitimate primary   slot  and    unmap    that  entry. This  is  to
ensure  that we do get a   legimate   non-0xF  slot the next time we
retry for a slot.

Though treating 0xF slot as invalid reduces the number of available
slots  and  may  have an effect  on the performance, the probabilty
of hitting a 0xF is extermely low.

Compared  to the current scheme, the above described scheme reduces
the number of false hash table updates  significantly  and  has the
added  advantage  of  releasing  four  valuable  PTE bits for other
purpose.

This idea was jointly developed by Paul Mackerras, Aneesh, Michael
Ellermen and myself.

4K PTE format remain unchanged currently.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
---
 arch/powerpc/include/asm/book3s/64/hash-4k.h  | 20 +++++++
 arch/powerpc/include/asm/book3s/64/hash-64k.h | 32 +++++++----
 arch/powerpc/include/asm/book3s/64/hash.h     | 15 +++--
 arch/powerpc/include/asm/book3s/64/mmu-hash.h |  5 ++
 arch/powerpc/mm/dump_linuxpagetables.c        |  3 +-
 arch/powerpc/mm/hash64_4k.c                   | 14 ++---
 arch/powerpc/mm/hash64_64k.c                  | 81 ++++++++++++---------------
 arch/powerpc/mm/hash_utils_64.c               | 30 +++++++---
 8 files changed, 122 insertions(+), 78 deletions(-)

diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h
index b4b5e6b..5ef1d81 100644
--- a/arch/powerpc/include/asm/book3s/64/hash-4k.h
+++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h
@@ -16,6 +16,18 @@
 #define H_PUD_TABLE_SIZE	(sizeof(pud_t) << H_PUD_INDEX_SIZE)
 #define H_PGD_TABLE_SIZE	(sizeof(pgd_t) << H_PGD_INDEX_SIZE)
 
+
+/*
+ * Only supported by 4k linux page size
+ */
+#define H_PAGE_F_SECOND        _RPAGE_RSV2     /* HPTE is in 2ndary HPTEG */
+#define H_PAGE_F_GIX           (_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
+#define H_PAGE_F_GIX_SHIFT     56
+
+#define H_PAGE_BUSY	_RPAGE_RSV1     /* software: PTE & hash are busy */
+#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */
+
+
 /* PTE flags to conserve for HPTE identification */
 #define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | \
 			 H_PAGE_F_SECOND | H_PAGE_F_GIX)
@@ -48,6 +60,14 @@ static inline int hash__hugepd_ok(hugepd_t hpd)
 }
 #endif
 
+static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
+			unsigned int subpg_index, unsigned long slot)
+{
+	return (slot << H_PAGE_F_GIX_SHIFT) &
+		(H_PAGE_F_SECOND | H_PAGE_F_GIX);
+}
+
+
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 
 static inline char *get_hpte_slot_array(pmd_t *pmdp)
diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h
index 9732837..0eb3c89 100644
--- a/arch/powerpc/include/asm/book3s/64/hash-64k.h
+++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h
@@ -10,23 +10,25 @@
  * 64k aligned address free up few of the lower bits of RPN for us
  * We steal that here. For more deatils look at pte_pfn/pfn_pte()
  */
-#define H_PAGE_COMBO	_RPAGE_RPN0 /* this is a combo 4k page */
-#define H_PAGE_4K_PFN	_RPAGE_RPN1 /* PFN is for a single 4k page */
+#define H_PAGE_COMBO   _RPAGE_RPN0 /* this is a combo 4k page */
+#define H_PAGE_4K_PFN  _RPAGE_RPN1 /* PFN is for a single 4k page */
+#define H_PAGE_F_SECOND	_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
+#define H_PAGE_F_GIX	(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
+#define H_PAGE_F_GIX_SHIFT	56
+
+
+#define H_PAGE_BUSY	_RPAGE_RPN42     /* software: PTE & hash are busy */
+#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */
+
 /*
  * We need to differentiate between explicit huge page and THP huge
  * page, since THP huge page also need to track real subpage details
  */
 #define H_PAGE_THP_HUGE  H_PAGE_4K_PFN
 
-/*
- * Used to track subpage group valid if H_PAGE_COMBO is set
- * This overloads H_PAGE_F_GIX and H_PAGE_F_SECOND
- */
-#define H_PAGE_COMBO_VALID	(H_PAGE_F_GIX | H_PAGE_F_SECOND)
-
 /* PTE flags to conserve for HPTE identification */
-#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_F_SECOND | \
-			 H_PAGE_F_GIX | H_PAGE_HASHPTE | H_PAGE_COMBO)
+#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | H_PAGE_COMBO)
+
 /*
  * we support 16 fragments per PTE page of 64K size.
  */
@@ -74,6 +76,16 @@ static inline unsigned long __rpte_to_hidx(real_pte_t rpte, unsigned long index)
 	return (pte_val(rpte.pte) >> H_PAGE_F_GIX_SHIFT) & 0xf;
 }
 
+static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
+		unsigned int subpg_index, unsigned long slot)
+{
+	unsigned long *hidxp = (unsigned long *)(ptep + PTRS_PER_PTE);
+
+	rpte.hidx &= ~(0xfUL << (subpg_index << 2));
+	*hidxp = rpte.hidx  | (slot << (subpg_index << 2));
+	return 0x0UL;
+}
+
 #define __rpte_to_pte(r)	((r).pte)
 extern bool __rpte_sub_valid(real_pte_t rpte, unsigned long index);
 /*
diff --git a/arch/powerpc/include/asm/book3s/64/hash.h b/arch/powerpc/include/asm/book3s/64/hash.h
index 4e957b0..e7cf03a 100644
--- a/arch/powerpc/include/asm/book3s/64/hash.h
+++ b/arch/powerpc/include/asm/book3s/64/hash.h
@@ -8,11 +8,8 @@
  *
  */
 #define H_PTE_NONE_MASK		_PAGE_HPTEFLAGS
-#define H_PAGE_F_GIX_SHIFT	56
-#define H_PAGE_BUSY		_RPAGE_RSV1 /* software: PTE & hash are busy */
-#define H_PAGE_F_SECOND		_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
-#define H_PAGE_F_GIX		(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
-#define H_PAGE_HASHPTE		_RPAGE_RPN43	/* PTE has associated HPTE */
+
+#define INIT_HIDX (~0x0UL)
 
 #ifdef CONFIG_PPC_64K_PAGES
 #include <asm/book3s/64/hash-64k.h>
@@ -160,6 +157,14 @@ static inline int hash__pte_none(pte_t pte)
 	return (pte_val(pte) & ~H_PTE_NONE_MASK) == 0;
 }
 
+static inline bool hpte_soft_invalid(unsigned long slot)
+{
+	return ((slot & 0xfUL) == 0xfUL);
+}
+
+unsigned long get_hidx_gslot(unsigned long vpn, unsigned long shift,
+		int ssize, real_pte_t rpte, unsigned int subpg_index);
+
 /* This low level function performs the actual PTE insertion
  * Setting the PTE depends on the MMU type and other factors. It's
  * an horrible mess that I'm not going to try to clean up now but
diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
index 6981a52..cfb8169 100644
--- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h
+++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
@@ -435,6 +435,11 @@ extern int __hash_page_4K(unsigned long ea, unsigned long access,
 extern int __hash_page_64K(unsigned long ea, unsigned long access,
 			   unsigned long vsid, pte_t *ptep, unsigned long trap,
 			   unsigned long flags, int ssize);
+extern unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
+			unsigned int subpg_index, unsigned long slot);
+extern unsigned long get_hidx_slot(unsigned long vpn, unsigned long shift,
+			int ssize, real_pte_t rpte, unsigned int subpg_index);
+
 struct mm_struct;
 unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap);
 extern int hash_page_mm(struct mm_struct *mm, unsigned long ea,
diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c
index 44fe483..b832ed3 100644
--- a/arch/powerpc/mm/dump_linuxpagetables.c
+++ b/arch/powerpc/mm/dump_linuxpagetables.c
@@ -213,7 +213,7 @@ struct flag_info {
 		.val	= H_PAGE_4K_PFN,
 		.set	= "4K_pfn",
 	}, {
-#endif
+#else
 		.mask	= H_PAGE_F_GIX,
 		.val	= H_PAGE_F_GIX,
 		.set	= "f_gix",
@@ -224,6 +224,7 @@ struct flag_info {
 		.val	= H_PAGE_F_SECOND,
 		.set	= "f_second",
 	}, {
+#endif /* CONFIG_PPC_64K_PAGES */
 #endif
 		.mask	= _PAGE_SPECIAL,
 		.val	= _PAGE_SPECIAL,
diff --git a/arch/powerpc/mm/hash64_4k.c b/arch/powerpc/mm/hash64_4k.c
index 6fa450c..c673829 100644
--- a/arch/powerpc/mm/hash64_4k.c
+++ b/arch/powerpc/mm/hash64_4k.c
@@ -20,6 +20,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 		   pte_t *ptep, unsigned long trap, unsigned long flags,
 		   int ssize, int subpg_prot)
 {
+	real_pte_t rpte;
 	unsigned long hpte_group;
 	unsigned long rflags, pa;
 	unsigned long old_pte, new_pte;
@@ -54,6 +55,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 	 * need to add in 0x1 if it's a read-only user page
 	 */
 	rflags = htab_convert_pte_flags(new_pte);
+	rpte = __real_pte(__pte(old_pte), ptep);
 
 	if (cpu_has_feature(CPU_FTR_NOEXECUTE) &&
 	    !cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
@@ -64,13 +66,10 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 		/*
 		 * There MIGHT be an HPTE for this pte
 		 */
-		hash = hpt_hash(vpn, shift, ssize);
-		if (old_pte & H_PAGE_F_SECOND)
-			hash = ~hash;
-		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-		slot += (old_pte & H_PAGE_F_GIX) >> H_PAGE_F_GIX_SHIFT;
+		unsigned long gslot = get_hidx_gslot(vpn, shift,
+						ssize, rpte, 0);
 
-		if (mmu_hash_ops.hpte_updatepp(slot, rflags, vpn, MMU_PAGE_4K,
+		if (mmu_hash_ops.hpte_updatepp(gslot, rflags, vpn, MMU_PAGE_4K,
 					       MMU_PAGE_4K, ssize, flags) == -1)
 			old_pte &= ~_PAGE_HPTEFLAGS;
 	}
@@ -118,8 +117,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 			return -1;
 		}
 		new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | H_PAGE_HASHPTE;
-		new_pte |= (slot << H_PAGE_F_GIX_SHIFT) &
-			(H_PAGE_F_SECOND | H_PAGE_F_GIX);
+		new_pte |= set_hidx_slot(ptep, rpte, 0, slot);
 	}
 	*ptep = __pte(new_pte & ~H_PAGE_BUSY);
 	return 0;
diff --git a/arch/powerpc/mm/hash64_64k.c b/arch/powerpc/mm/hash64_64k.c
index 1a68cb1..3702a3c 100644
--- a/arch/powerpc/mm/hash64_64k.c
+++ b/arch/powerpc/mm/hash64_64k.c
@@ -15,34 +15,13 @@
 #include <linux/mm.h>
 #include <asm/machdep.h>
 #include <asm/mmu.h>
+
 /*
  * index from 0 - 15
  */
 bool __rpte_sub_valid(real_pte_t rpte, unsigned long index)
 {
-	unsigned long g_idx;
-	unsigned long ptev = pte_val(rpte.pte);
-
-	g_idx = (ptev & H_PAGE_COMBO_VALID) >> H_PAGE_F_GIX_SHIFT;
-	index = index >> 2;
-	if (g_idx & (0x1 << index))
-		return true;
-	else
-		return false;
-}
-/*
- * index from 0 - 15
- */
-static unsigned long mark_subptegroup_valid(unsigned long ptev, unsigned long index)
-{
-	unsigned long g_idx;
-
-	if (!(ptev & H_PAGE_COMBO))
-		return ptev;
-	index = index >> 2;
-	g_idx = 0x1 << index;
-
-	return ptev | (g_idx << H_PAGE_F_GIX_SHIFT);
+	return !(hpte_soft_invalid(rpte.hidx >> (index << 2)));
 }
 
 int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
@@ -50,10 +29,9 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 		   int ssize, int subpg_prot)
 {
 	real_pte_t rpte;
-	unsigned long *hidxp;
 	unsigned long hpte_group;
 	unsigned int subpg_index;
-	unsigned long rflags, pa, hidx;
+	unsigned long rflags, pa;
 	unsigned long old_pte, new_pte, subpg_pte;
 	unsigned long vpn, hash, slot;
 	unsigned long shift = mmu_psize_defs[MMU_PAGE_4K].shift;
@@ -116,28 +94,23 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 		 * On hash insert failure we use old pte value and we don't
 		 * want slot information there if we have a insert failure.
 		 */
-		old_pte &= ~(H_PAGE_HASHPTE | H_PAGE_F_GIX | H_PAGE_F_SECOND);
-		new_pte &= ~(H_PAGE_HASHPTE | H_PAGE_F_GIX | H_PAGE_F_SECOND);
+		old_pte &= ~(H_PAGE_HASHPTE);
+		new_pte &= ~(H_PAGE_HASHPTE);
 		goto htab_insert_hpte;
 	}
 	/*
 	 * Check for sub page valid and update
 	 */
 	if (__rpte_sub_valid(rpte, subpg_index)) {
-		int ret;
 
-		hash = hpt_hash(vpn, shift, ssize);
-		hidx = __rpte_to_hidx(rpte, subpg_index);
-		if (hidx & _PTEIDX_SECONDARY)
-			hash = ~hash;
-		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-		slot += hidx & _PTEIDX_GROUP_IX;
+		unsigned long gslot = get_hidx_gslot(vpn, shift,
+				ssize, rpte, subpg_index);
 
-		ret = mmu_hash_ops.hpte_updatepp(slot, rflags, vpn,
+		int ret = mmu_hash_ops.hpte_updatepp(gslot, rflags, vpn,
 						 MMU_PAGE_4K, MMU_PAGE_4K,
 						 ssize, flags);
 		/*
-		 *if we failed because typically the HPTE wasn't really here
+		 * if we failed because typically the HPTE wasn't really here
 		 * we try an insertion.
 		 */
 		if (ret == -1)
@@ -148,6 +121,15 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 	}
 
 htab_insert_hpte:
+
+	/*
+	 * initialize all hidx entries to a invalid value,
+	 * the first time the PTE is about to allocate
+	 * a 4K hpte
+	 */
+	if (!(old_pte & H_PAGE_COMBO))
+		rpte.hidx = INIT_HIDX;
+
 	/*
 	 * handle H_PAGE_4K_PFN case
 	 */
@@ -177,10 +159,20 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 						rflags, HPTE_V_SECONDARY,
 						MMU_PAGE_4K, MMU_PAGE_4K,
 						ssize);
-		if (slot == -1) {
-			if (mftb() & 0x1)
+
+		if (unlikely(hpte_soft_invalid(slot))) {
+			slot = slot & _PTEIDX_GROUP_IX;
+			mmu_hash_ops.hpte_invalidate(hpte_group+slot, vpn,
+				MMU_PAGE_4K, MMU_PAGE_4K,
+				ssize, flags);
+		}
+
+		if (unlikely(slot == -1 || hpte_soft_invalid(slot))) {
+
+			if (hpte_soft_invalid(slot) || (mftb() & 0x1))
 				hpte_group = ((hash & htab_hash_mask) *
 					      HPTES_PER_GROUP) & ~0x7UL;
+
 			mmu_hash_ops.hpte_remove(hpte_group);
 			/*
 			 * FIXME!! Should be try the group from which we removed ?
@@ -204,11 +196,9 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
 	 * Since we have H_PAGE_BUSY set on ptep, we can be sure
 	 * nobody is undating hidx.
 	 */
-	hidxp = (unsigned long *)(ptep + PTRS_PER_PTE);
-	rpte.hidx &= ~(0xfUL << (subpg_index << 2));
-	*hidxp = rpte.hidx  | (slot << (subpg_index << 2));
-	new_pte = mark_subptegroup_valid(new_pte, subpg_index);
-	new_pte |=  H_PAGE_HASHPTE;
+	new_pte |= set_hidx_slot(ptep, rpte, subpg_index, slot);
+	new_pte |= H_PAGE_HASHPTE;
+
 	/*
 	 * check __real_pte for details on matching smp_rmb()
 	 */
@@ -322,9 +312,10 @@ int __hash_page_64K(unsigned long ea, unsigned long access,
 					   MMU_PAGE_64K, MMU_PAGE_64K, old_pte);
 			return -1;
 		}
-		new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | H_PAGE_HASHPTE;
+
 		new_pte |= (slot << H_PAGE_F_GIX_SHIFT) &
-			(H_PAGE_F_SECOND | H_PAGE_F_GIX);
+				(H_PAGE_F_SECOND | H_PAGE_F_GIX);
+		new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | H_PAGE_HASHPTE;
 	}
 	*ptep = __pte(new_pte & ~H_PAGE_BUSY);
 	return 0;
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index f2095ce..c0f4b46 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -975,8 +975,9 @@ void __init hash__early_init_devtree(void)
 
 void __init hash__early_init_mmu(void)
 {
+#ifndef CONFIG_PPC_64K_PAGES
 	/*
-	 * We have code in __hash_page_64K() and elsewhere, which assumes it can
+	 * We have code in __hash_page_4K() and elsewhere, which assumes it can
 	 * do the following:
 	 *   new_pte |= (slot << H_PAGE_F_GIX_SHIFT) & (H_PAGE_F_SECOND | H_PAGE_F_GIX);
 	 *
@@ -987,6 +988,7 @@ void __init hash__early_init_mmu(void)
 	 * with a BUILD_BUG_ON().
 	 */
 	BUILD_BUG_ON(H_PAGE_F_SECOND != (1ul  << (H_PAGE_F_GIX_SHIFT + 3)));
+#endif /* CONFIG_PPC_64K_PAGES */
 
 	htab_init_page_sizes();
 
@@ -1589,29 +1591,39 @@ static inline void tm_flush_hash_page(int local)
 }
 #endif
 
+unsigned long get_hidx_gslot(unsigned long vpn, unsigned long shift,
+			int ssize, real_pte_t rpte, unsigned int subpg_index)
+{
+	unsigned long hash, slot, hidx;
+
+	hash = hpt_hash(vpn, shift, ssize);
+	hidx = __rpte_to_hidx(rpte, subpg_index);
+	if (hidx & _PTEIDX_SECONDARY)
+		hash = ~hash;
+	slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+	slot += hidx & _PTEIDX_GROUP_IX;
+	return slot;
+}
+
+
 /* WARNING: This is called from hash_low_64.S, if you change this prototype,
  *          do not forget to update the assembly call site !
  */
 void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize, int ssize,
 		     unsigned long flags)
 {
-	unsigned long hash, index, shift, hidx, slot;
+	unsigned long hash, index, shift, hidx, gslot;
 	int local = flags & HPTE_LOCAL_UPDATE;
 
 	DBG_LOW("flush_hash_page(vpn=%016lx)\n", vpn);
 	pte_iterate_hashed_subpages(pte, psize, vpn, index, shift) {
-		hash = hpt_hash(vpn, shift, ssize);
-		hidx = __rpte_to_hidx(pte, index);
-		if (hidx & _PTEIDX_SECONDARY)
-			hash = ~hash;
-		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-		slot += hidx & _PTEIDX_GROUP_IX;
+		gslot = get_hidx_gslot(vpn, shift, ssize, pte, index);
 		DBG_LOW(" sub %ld: hash=%lx, hidx=%lx\n", index, slot, hidx);
 		/*
 		 * We use same base page size and actual psize, because we don't
 		 * use these functions for hugepage
 		 */
-		mmu_hash_ops.hpte_invalidate(slot, vpn, psize, psize,
+		mmu_hash_ops.hpte_invalidate(gslot, vpn, psize, psize,
 					     ssize, local);
 	} pte_iterate_hashed_end();
 
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1670653 — Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.

FromAnshuman Khandual <khandual@linux.vnet.ibm.com>
Date2017-06-20 12:30 +0200
SubjectRe: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.
Message-ID<tUoA3-7os-37@gated-at.bofh.it>
In reply to#1668166
On 06/17/2017 09:22 AM, Ram Pai wrote:
> Rearrange 64K PTE bits to  free  up  bits 3, 4, 5  and  6
> in the 4K backed hpte pages. These bits continue to be used
> for 64K backed hpte pages in this patch, but will be freed
> up in the next patch.

The counting 3, 4, 5 and 6 are in BE format I believe, I was
initially trying to see that from right to left as we normally
do in the kernel and was getting confused. So basically these
bits (which are only applicable for 64K mapping IIUC) are going
to be freed up from the PTE format.

#define _RPAGE_RSV1		0x1000000000000000UL
#define _RPAGE_RSV2		0x0800000000000000UL
#define _RPAGE_RSV3		0x0400000000000000UL
#define _RPAGE_RSV4		0x0200000000000000UL

As you have mentioned before this feature is available for 64K
page size only and not for 4K mappings. So I assume we support
both the combinations.

* 64K mapping on 64K
* 64K mapping on 4K

These are the current users of the above bits

#define H_PAGE_BUSY		_RPAGE_RSV1 /* software: PTE & hash are busy */
#define H_PAGE_F_SECOND		_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
#define H_PAGE_F_GIX		(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
#define H_PAGE_HASHPTE		_RPAGE_RPN43	/* PTE has associated HPTE */

> 
> The patch does the following change to the 64K PTE format
> 
> H_PAGE_BUSY moves from bit 3 to bit 9

and what is in there on bit 9 now ? This ?

#define _RPAGE_SW2		0x00400

which is used as 

#define _PAGE_SPECIAL		_RPAGE_SW2 /* software: special page */

which will not be required any more ?

> H_PAGE_F_SECOND which occupied bit 4 moves to the second part
> 	of the pte.
> H_PAGE_F_GIX which  occupied bit 5, 6 and 7 also moves to the
> 	second part of the pte.
> 
> the four  bits((H_PAGE_F_SECOND|H_PAGE_F_GIX) that represent a slot
> is  initialized  to  0xF  indicating  an invalid  slot.  If  a hpte
> gets cached in a 0xF  slot(i.e  7th  slot  of  secondary),  it   is
> released immediately. In  other  words, even  though   0xF   is   a

Release immediately means we attempt again for a new hash slot ?

> valid slot we discard  and consider it as an invalid
> slot;i.e hpte_soft_invalid(). This  gives  us  an opportunity to not
> depend on a bit in the primary PTE in order to determine the
> validity of a slot.

So we have to see the slot number in the second half for each PTE to
figure out if it has got a valid slot in the hash page table.

> 
> When  we  release  a    hpte   in the 0xF   slot we also   release a
> legitimate primary   slot  and    unmap    that  entry. This  is  to
> ensure  that we do get a   legimate   non-0xF  slot the next time we
> retry for a slot.

Okay.

> 
> Though treating 0xF slot as invalid reduces the number of available
> slots  and  may  have an effect  on the performance, the probabilty
> of hitting a 0xF is extermely low.

Why you say that ? I thought every slot number has the same probability
of hit from the hash function.

> 
> Compared  to the current scheme, the above described scheme reduces
> the number of false hash table updates  significantly  and  has the

How it reduces false hash table updates ?

> added  advantage  of  releasing  four  valuable  PTE bits for other
> purpose.
> 
> This idea was jointly developed by Paul Mackerras, Aneesh, Michael
> Ellermen and myself.
> 
> 4K PTE format remain unchanged currently.
> 
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> ---
>  arch/powerpc/include/asm/book3s/64/hash-4k.h  | 20 +++++++
>  arch/powerpc/include/asm/book3s/64/hash-64k.h | 32 +++++++----
>  arch/powerpc/include/asm/book3s/64/hash.h     | 15 +++--
>  arch/powerpc/include/asm/book3s/64/mmu-hash.h |  5 ++
>  arch/powerpc/mm/dump_linuxpagetables.c        |  3 +-
>  arch/powerpc/mm/hash64_4k.c                   | 14 ++---
>  arch/powerpc/mm/hash64_64k.c                  | 81 ++++++++++++---------------
>  arch/powerpc/mm/hash_utils_64.c               | 30 +++++++---
>  8 files changed, 122 insertions(+), 78 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> index b4b5e6b..5ef1d81 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> @@ -16,6 +16,18 @@
>  #define H_PUD_TABLE_SIZE	(sizeof(pud_t) << H_PUD_INDEX_SIZE)
>  #define H_PGD_TABLE_SIZE	(sizeof(pgd_t) << H_PGD_INDEX_SIZE)
> 
> +
> +/*
> + * Only supported by 4k linux page size
> + */
> +#define H_PAGE_F_SECOND        _RPAGE_RSV2     /* HPTE is in 2ndary HPTEG */
> +#define H_PAGE_F_GIX           (_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> +#define H_PAGE_F_GIX_SHIFT     56
> +
> +#define H_PAGE_BUSY	_RPAGE_RSV1     /* software: PTE & hash are busy */
> +#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */
> +
> +

So we moved the common 64K definitions here.


>  /* PTE flags to conserve for HPTE identification */
>  #define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | \
>  			 H_PAGE_F_SECOND | H_PAGE_F_GIX)
> @@ -48,6 +60,14 @@ static inline int hash__hugepd_ok(hugepd_t hpd)
>  }
>  #endif
> 
> +static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> +			unsigned int subpg_index, unsigned long slot)
> +{
> +	return (slot << H_PAGE_F_GIX_SHIFT) &
> +		(H_PAGE_F_SECOND | H_PAGE_F_GIX);
> +}

Why we are passing the first 3 arguments of the function if we never
use it inside. Is the caller expected to take care of it ?

> +
> +
>  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
> 
>  static inline char *get_hpte_slot_array(pmd_t *pmdp)
> diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> index 9732837..0eb3c89 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> @@ -10,23 +10,25 @@
>   * 64k aligned address free up few of the lower bits of RPN for us
>   * We steal that here. For more deatils look at pte_pfn/pfn_pte()
>   */
> -#define H_PAGE_COMBO	_RPAGE_RPN0 /* this is a combo 4k page */
> -#define H_PAGE_4K_PFN	_RPAGE_RPN1 /* PFN is for a single 4k page */
> +#define H_PAGE_COMBO   _RPAGE_RPN0 /* this is a combo 4k page */
> +#define H_PAGE_4K_PFN  _RPAGE_RPN1 /* PFN is for a single 4k page */

Its the same thing, changes nothing.

> +#define H_PAGE_F_SECOND	_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
> +#define H_PAGE_F_GIX	(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> +#define H_PAGE_F_GIX_SHIFT	56
> +
> +
> +#define H_PAGE_BUSY	_RPAGE_RPN42     /* software: PTE & hash are busy */
> +#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */

H_PAGE_BUSY seems to be differently defined here.

> +
>  /*
>   * We need to differentiate between explicit huge page and THP huge
>   * page, since THP huge page also need to track real subpage details
>   */
>  #define H_PAGE_THP_HUGE  H_PAGE_4K_PFN
> 
> -/*
> - * Used to track subpage group valid if H_PAGE_COMBO is set
> - * This overloads H_PAGE_F_GIX and H_PAGE_F_SECOND
> - */
> -#define H_PAGE_COMBO_VALID	(H_PAGE_F_GIX | H_PAGE_F_SECOND)

H_PAGE_COMBO_VALID is not defined alternately ?

> -
>  /* PTE flags to conserve for HPTE identification */
> -#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_F_SECOND | \
> -			 H_PAGE_F_GIX | H_PAGE_HASHPTE | H_PAGE_COMBO)
> +#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | H_PAGE_COMBO)
> +

Slot information has moved to the second half, hence _PAGE_HPTEFLAGS
need not carry that.

>  /*
>   * we support 16 fragments per PTE page of 64K size.
>   */
> @@ -74,6 +76,16 @@ static inline unsigned long __rpte_to_hidx(real_pte_t rpte, unsigned long index)
>  	return (pte_val(rpte.pte) >> H_PAGE_F_GIX_SHIFT) & 0xf;
>  }
> 
> +static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> +		unsigned int subpg_index, unsigned long slot)
> +{
> +	unsigned long *hidxp = (unsigned long *)(ptep + PTRS_PER_PTE);
> +
> +	rpte.hidx &= ~(0xfUL << (subpg_index << 2));
> +	*hidxp = rpte.hidx  | (slot << (subpg_index << 2));
> +	return 0x0UL;
> +}

New method to insert the slot information in the second half.

> +
>  #define __rpte_to_pte(r)	((r).pte)
>  extern bool __rpte_sub_valid(real_pte_t rpte, unsigned long index);
>  /*
> diff --git a/arch/powerpc/include/asm/book3s/64/hash.h b/arch/powerpc/include/asm/book3s/64/hash.h
> index 4e957b0..e7cf03a 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash.h
> @@ -8,11 +8,8 @@
>   *
>   */
>  #define H_PTE_NONE_MASK		_PAGE_HPTEFLAGS
> -#define H_PAGE_F_GIX_SHIFT	56
> -#define H_PAGE_BUSY		_RPAGE_RSV1 /* software: PTE & hash are busy */
> -#define H_PAGE_F_SECOND		_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
> -#define H_PAGE_F_GIX		(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> -#define H_PAGE_HASHPTE		_RPAGE_RPN43	/* PTE has associated HPTE */

Removing the common definitions.

> +
> +#define INIT_HIDX (~0x0UL)
> 
>  #ifdef CONFIG_PPC_64K_PAGES
>  #include <asm/book3s/64/hash-64k.h>
> @@ -160,6 +157,14 @@ static inline int hash__pte_none(pte_t pte)
>  	return (pte_val(pte) & ~H_PTE_NONE_MASK) == 0;
>  }
> 
> +static inline bool hpte_soft_invalid(unsigned long slot)
> +{
> +	return ((slot & 0xfUL) == 0xfUL);
> +}
> +
> +unsigned long get_hidx_gslot(unsigned long vpn, unsigned long shift,
> +		int ssize, real_pte_t rpte, unsigned int subpg_index);
> +
>  /* This low level function performs the actual PTE insertion
>   * Setting the PTE depends on the MMU type and other factors. It's
>   * an horrible mess that I'm not going to try to clean up now but
> diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> index 6981a52..cfb8169 100644
> --- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> +++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> @@ -435,6 +435,11 @@ extern int __hash_page_4K(unsigned long ea, unsigned long access,
>  extern int __hash_page_64K(unsigned long ea, unsigned long access,
>  			   unsigned long vsid, pte_t *ptep, unsigned long trap,
>  			   unsigned long flags, int ssize);
> +extern unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> +			unsigned int subpg_index, unsigned long slot);
> +extern unsigned long get_hidx_slot(unsigned long vpn, unsigned long shift,
> +			int ssize, real_pte_t rpte, unsigned int subpg_index);

I wonder what purpose set_hidx_slot() defined previously, served.

> +
>  struct mm_struct;
>  unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap);
>  extern int hash_page_mm(struct mm_struct *mm, unsigned long ea,
> diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c
> index 44fe483..b832ed3 100644
> --- a/arch/powerpc/mm/dump_linuxpagetables.c
> +++ b/arch/powerpc/mm/dump_linuxpagetables.c
> @@ -213,7 +213,7 @@ struct flag_info {
>  		.val	= H_PAGE_4K_PFN,
>  		.set	= "4K_pfn",
>  	}, {
> -#endif
> +#else
>  		.mask	= H_PAGE_F_GIX,
>  		.val	= H_PAGE_F_GIX,
>  		.set	= "f_gix",
> @@ -224,6 +224,7 @@ struct flag_info {
>  		.val	= H_PAGE_F_SECOND,
>  		.set	= "f_second",
>  	}, {
> +#endif /* CONFIG_PPC_64K_PAGES */

Are we adding H_PAGE_F_GIX as an element for 4K mapping ?

>  #endif
>  		.mask	= _PAGE_SPECIAL,
>  		.val	= _PAGE_SPECIAL,
> diff --git a/arch/powerpc/mm/hash64_4k.c b/arch/powerpc/mm/hash64_4k.c
> index 6fa450c..c673829 100644
> --- a/arch/powerpc/mm/hash64_4k.c
> +++ b/arch/powerpc/mm/hash64_4k.c
> @@ -20,6 +20,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
>  		   pte_t *ptep, unsigned long trap, unsigned long flags,
>  		   int ssize, int subpg_prot)
>  {
> +	real_pte_t rpte;
>  	unsigned long hpte_group;
>  	unsigned long rflags, pa;
>  	unsigned long old_pte, new_pte;
> @@ -54,6 +55,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
>  	 * need to add in 0x1 if it's a read-only user page
>  	 */
>  	rflags = htab_convert_pte_flags(new_pte);
> +	rpte = __real_pte(__pte(old_pte), ptep);
> 
>  	if (cpu_has_feature(CPU_FTR_NOEXECUTE) &&
>  	    !cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
> @@ -64,13 +66,10 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
>  		/*
>  		 * There MIGHT be an HPTE for this pte
>  		 */
> -		hash = hpt_hash(vpn, shift, ssize);
> -		if (old_pte & H_PAGE_F_SECOND)
> -			hash = ~hash;
> -		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
> -		slot += (old_pte & H_PAGE_F_GIX) >> H_PAGE_F_GIX_SHIFT;
> +		unsigned long gslot = get_hidx_gslot(vpn, shift,
> +						ssize, rpte, 0);

I am wondering why there is a 'g' before the slot in all these
functions.

Its already too much of changes in a single patch. Being a single
logical change it needs to be inside a single change but then we
need much more description in the commit message for some one to
understand what all changed and how.

[toc] | [prev] | [next] | [standalone]


#1671231 — Re: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.

FromRam Pai <linuxram@us.ibm.com>
Date2017-06-21 01:30 +0200
SubjectRe: [RFC v2 01/12] powerpc: Free up four 64K PTE bits in 4K backed hpte pages.
Message-ID<tUAKS-6CT-9@gated-at.bofh.it>
In reply to#1670653
On Tue, Jun 20, 2017 at 03:50:25PM +0530, Anshuman Khandual wrote:
> On 06/17/2017 09:22 AM, Ram Pai wrote:
> > Rearrange 64K PTE bits to  free  up  bits 3, 4, 5  and  6
> > in the 4K backed hpte pages. These bits continue to be used
> > for 64K backed hpte pages in this patch, but will be freed
> > up in the next patch.
> 
> The counting 3, 4, 5 and 6 are in BE format I believe, I was
> initially trying to see that from right to left as we normally
> do in the kernel and was getting confused. So basically these
> bits (which are only applicable for 64K mapping IIUC) are going
> to be freed up from the PTE format.
> 
> #define _RPAGE_RSV1		0x1000000000000000UL
> #define _RPAGE_RSV2		0x0800000000000000UL
> #define _RPAGE_RSV3		0x0400000000000000UL
> #define _RPAGE_RSV4		0x0200000000000000UL
> 
> As you have mentioned before this feature is available for 64K
> page size only and not for 4K mappings. So I assume we support
> both the combinations.
> 
> * 64K mapping on 64K
> * 64K mapping on 4K

yes.

> 
> These are the current users of the above bits
> 
> #define H_PAGE_BUSY		_RPAGE_RSV1 /* software: PTE & hash are busy */
> #define H_PAGE_F_SECOND		_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
> #define H_PAGE_F_GIX		(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> #define H_PAGE_HASHPTE		_RPAGE_RPN43	/* PTE has associated HPTE */
> 
> > 
> > The patch does the following change to the 64K PTE format
> > 
> > H_PAGE_BUSY moves from bit 3 to bit 9
> 
> and what is in there on bit 9 now ? This ?
> 
> #define _RPAGE_SW2		0x00400
> 
> which is used as 
> 
> #define _PAGE_SPECIAL		_RPAGE_SW2 /* software: special page */
> 
> which will not be required any more ?

i think you are reading bit 9 from right to left. the bit 9 i refer to
is from left to right. Using the same numbering convention the ISA3.0 uses.
I know it is confusing, will make a mention in the comment of this
patch, to read it the big-endian way.

BTW: Bit 9 is not used currently. so using it in this patch. But this is
a temporary move. the H_PAGE_BUSY will move to bit 7 in the next patch.

Had to keep at bit 9, because bit 7 is not yet entirely freed up. it is
used by 64K PTE backed by 64k htpe.

> 
> > H_PAGE_F_SECOND which occupied bit 4 moves to the second part
> > 	of the pte.
> > H_PAGE_F_GIX which  occupied bit 5, 6 and 7 also moves to the
> > 	second part of the pte.
> > 
> > the four  bits((H_PAGE_F_SECOND|H_PAGE_F_GIX) that represent a slot
> > is  initialized  to  0xF  indicating  an invalid  slot.  If  a hpte
> > gets cached in a 0xF  slot(i.e  7th  slot  of  secondary),  it   is
> > released immediately. In  other  words, even  though   0xF   is   a
> 
> Release immediately means we attempt again for a new hash slot ?

yes.

> 
> > valid slot we discard  and consider it as an invalid
> > slot;i.e hpte_soft_invalid(). This  gives  us  an opportunity to not
> > depend on a bit in the primary PTE in order to determine the
> > validity of a slot.
> 
> So we have to see the slot number in the second half for each PTE to
> figure out if it has got a valid slot in the hash page table.

yes.

> 
> > 
> > When  we  release  a    hpte   in the 0xF   slot we also   release a
> > legitimate primary   slot  and    unmap    that  entry. This  is  to
> > ensure  that we do get a   legimate   non-0xF  slot the next time we
> > retry for a slot.
> 
> Okay.
> 
> > 
> > Though treating 0xF slot as invalid reduces the number of available
> > slots  and  may  have an effect  on the performance, the probabilty
> > of hitting a 0xF is extermely low.
> 
> Why you say that ? I thought every slot number has the same probability
> of hit from the hash function.

Every hash bucket has the same probability. But every slot within the
hash bucket is filled in sequentially. so it takes 15 hptes to hash to
the same bucket before we get to the 15th slot in the secondary.

> 
> > 
> > Compared  to the current scheme, the above described scheme reduces
> > the number of false hash table updates  significantly  and  has the
> 
> How it reduces false hash table updates ?

earlier, we had 1 bit allocated in the first-part-of-the 64K-PTE 
for four consecutive 4K hptes. If any one 4k hpte got hashed-in,
the bit got set. Which means anytime it faulted on the remaining
three 4k hpte, we saw the bit already set and tried to erroneously 
update that hpte. So we had a 75% update error rate. Funcationally
not bad, but bad from a performance point of view.

With the current scheme, we decide if a 4k slot is valid by looking
at its value rather than depending on a bit in the main-pte. So
there is no chance of getting mislead. And hence no chance of trying
to update a invalid hpte. Should improve performance and at the same
time give us four valuable PTE bits.


> 
> > added  advantage  of  releasing  four  valuable  PTE bits for other
> > purpose.
> > 
> > This idea was jointly developed by Paul Mackerras, Aneesh, Michael
> > Ellermen and myself.
> > 
> > 4K PTE format remain unchanged currently.
> > 
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > ---
> >  arch/powerpc/include/asm/book3s/64/hash-4k.h  | 20 +++++++
> >  arch/powerpc/include/asm/book3s/64/hash-64k.h | 32 +++++++----
> >  arch/powerpc/include/asm/book3s/64/hash.h     | 15 +++--
> >  arch/powerpc/include/asm/book3s/64/mmu-hash.h |  5 ++
> >  arch/powerpc/mm/dump_linuxpagetables.c        |  3 +-
> >  arch/powerpc/mm/hash64_4k.c                   | 14 ++---
> >  arch/powerpc/mm/hash64_64k.c                  | 81 ++++++++++++---------------
> >  arch/powerpc/mm/hash_utils_64.c               | 30 +++++++---
> >  8 files changed, 122 insertions(+), 78 deletions(-)
> > 
> > diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> > index b4b5e6b..5ef1d81 100644
> > --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h
> > +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> > @@ -16,6 +16,18 @@
> >  #define H_PUD_TABLE_SIZE	(sizeof(pud_t) << H_PUD_INDEX_SIZE)
> >  #define H_PGD_TABLE_SIZE	(sizeof(pgd_t) << H_PGD_INDEX_SIZE)
> > 
> > +
> > +/*
> > + * Only supported by 4k linux page size
> > + */
> > +#define H_PAGE_F_SECOND        _RPAGE_RSV2     /* HPTE is in 2ndary HPTEG */
> > +#define H_PAGE_F_GIX           (_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> > +#define H_PAGE_F_GIX_SHIFT     56
> > +
> > +#define H_PAGE_BUSY	_RPAGE_RSV1     /* software: PTE & hash are busy */
> > +#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */
> > +
> > +
> 
> So we moved the common 64K definitions here.

yes.
> 
> 
> >  /* PTE flags to conserve for HPTE identification */
> >  #define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | \
> >  			 H_PAGE_F_SECOND | H_PAGE_F_GIX)
> > @@ -48,6 +60,14 @@ static inline int hash__hugepd_ok(hugepd_t hpd)
> >  }
> >  #endif
> > 
> > +static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> > +			unsigned int subpg_index, unsigned long slot)
> > +{
> > +	return (slot << H_PAGE_F_GIX_SHIFT) &
> > +		(H_PAGE_F_SECOND | H_PAGE_F_GIX);
> > +}
> 
> Why we are passing the first 3 arguments of the function if we never
> use it inside. Is the caller expected to take care of it ?

trying to keep the same prototype for the 4K-pte and 64K-pte cases.
Otherwise the caller has to wonder which parameter scheme to use.

> 
> > +
> > +
> >  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
> > 
> >  static inline char *get_hpte_slot_array(pmd_t *pmdp)
> > diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> > index 9732837..0eb3c89 100644
> > --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h
> > +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> > @@ -10,23 +10,25 @@
> >   * 64k aligned address free up few of the lower bits of RPN for us
> >   * We steal that here. For more deatils look at pte_pfn/pfn_pte()
> >   */
> > -#define H_PAGE_COMBO	_RPAGE_RPN0 /* this is a combo 4k page */
> > -#define H_PAGE_4K_PFN	_RPAGE_RPN1 /* PFN is for a single 4k page */
> > +#define H_PAGE_COMBO   _RPAGE_RPN0 /* this is a combo 4k page */
> > +#define H_PAGE_4K_PFN  _RPAGE_RPN1 /* PFN is for a single 4k page */
> 
> Its the same thing, changes nothing.

it fixes some space/tab problem.

> 
> > +#define H_PAGE_F_SECOND	_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
> > +#define H_PAGE_F_GIX	(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> > +#define H_PAGE_F_GIX_SHIFT	56
> > +
> > +
> > +#define H_PAGE_BUSY	_RPAGE_RPN42     /* software: PTE & hash are busy */
> > +#define H_PAGE_HASHPTE	_RPAGE_RPN43    /* PTE has associated HPTE */
> 
> H_PAGE_BUSY seems to be differently defined here.

Yes. it is using two different bits depending on 4K hpte v/s 64k hpte
case. But in the next patch all will be same and consistent.

> 
> > +
> >  /*
> >   * We need to differentiate between explicit huge page and THP huge
> >   * page, since THP huge page also need to track real subpage details
> >   */
> >  #define H_PAGE_THP_HUGE  H_PAGE_4K_PFN
> > 
> > -/*
> > - * Used to track subpage group valid if H_PAGE_COMBO is set
> > - * This overloads H_PAGE_F_GIX and H_PAGE_F_SECOND
> > - */
> > -#define H_PAGE_COMBO_VALID	(H_PAGE_F_GIX | H_PAGE_F_SECOND)
> 
> H_PAGE_COMBO_VALID is not defined alternately ?

it is not needed anymore.

> 
> > -
> >  /* PTE flags to conserve for HPTE identification */
> > -#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_F_SECOND | \
> > -			 H_PAGE_F_GIX | H_PAGE_HASHPTE | H_PAGE_COMBO)
> > +#define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | H_PAGE_COMBO)
> > +
> 
> Slot information has moved to the second half, hence _PAGE_HPTEFLAGS
> need not carry that.

yes.

> 
> >  /*
> >   * we support 16 fragments per PTE page of 64K size.
> >   */
> > @@ -74,6 +76,16 @@ static inline unsigned long __rpte_to_hidx(real_pte_t rpte, unsigned long index)
> >  	return (pte_val(rpte.pte) >> H_PAGE_F_GIX_SHIFT) & 0xf;
> >  }
> > 
> > +static inline unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> > +		unsigned int subpg_index, unsigned long slot)
> > +{
> > +	unsigned long *hidxp = (unsigned long *)(ptep + PTRS_PER_PTE);
> > +
> > +	rpte.hidx &= ~(0xfUL << (subpg_index << 2));
> > +	*hidxp = rpte.hidx  | (slot << (subpg_index << 2));
> > +	return 0x0UL;
> > +}
> 
> New method to insert the slot information in the second half.

yes. well it basically trying to reduce code redundancy. Too many places
using exactly the same code to accomplish the same thing. Makes sense to
bring it all in one place.

> 
> > +
> >  #define __rpte_to_pte(r)	((r).pte)
> >  extern bool __rpte_sub_valid(real_pte_t rpte, unsigned long index);
> >  /*
> > diff --git a/arch/powerpc/include/asm/book3s/64/hash.h b/arch/powerpc/include/asm/book3s/64/hash.h
> > index 4e957b0..e7cf03a 100644
> > --- a/arch/powerpc/include/asm/book3s/64/hash.h
> > +++ b/arch/powerpc/include/asm/book3s/64/hash.h
> > @@ -8,11 +8,8 @@
> >   *
> >   */
> >  #define H_PTE_NONE_MASK		_PAGE_HPTEFLAGS
> > -#define H_PAGE_F_GIX_SHIFT	56
> > -#define H_PAGE_BUSY		_RPAGE_RSV1 /* software: PTE & hash are busy */
> > -#define H_PAGE_F_SECOND		_RPAGE_RSV2	/* HPTE is in 2ndary HPTEG */
> > -#define H_PAGE_F_GIX		(_RPAGE_RSV3 | _RPAGE_RSV4 | _RPAGE_RPN44)
> > -#define H_PAGE_HASHPTE		_RPAGE_RPN43	/* PTE has associated HPTE */
> 
> Removing the common definitions.
> 
> > +
> > +#define INIT_HIDX (~0x0UL)
> > 
> >  #ifdef CONFIG_PPC_64K_PAGES
> >  #include <asm/book3s/64/hash-64k.h>
> > @@ -160,6 +157,14 @@ static inline int hash__pte_none(pte_t pte)
> >  	return (pte_val(pte) & ~H_PTE_NONE_MASK) == 0;
> >  }
> > 
> > +static inline bool hpte_soft_invalid(unsigned long slot)
> > +{
> > +	return ((slot & 0xfUL) == 0xfUL);
> > +}
> > +
> > +unsigned long get_hidx_gslot(unsigned long vpn, unsigned long shift,
> > +		int ssize, real_pte_t rpte, unsigned int subpg_index);
> > +
> >  /* This low level function performs the actual PTE insertion
> >   * Setting the PTE depends on the MMU type and other factors. It's
> >   * an horrible mess that I'm not going to try to clean up now but
> > diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> > index 6981a52..cfb8169 100644
> > --- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> > +++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
> > @@ -435,6 +435,11 @@ extern int __hash_page_4K(unsigned long ea, unsigned long access,
> >  extern int __hash_page_64K(unsigned long ea, unsigned long access,
> >  			   unsigned long vsid, pte_t *ptep, unsigned long trap,
> >  			   unsigned long flags, int ssize);
> > +extern unsigned long set_hidx_slot(pte_t *ptep, real_pte_t rpte,
> > +			unsigned int subpg_index, unsigned long slot);
> > +extern unsigned long get_hidx_slot(unsigned long vpn, unsigned long shift,
> > +			int ssize, real_pte_t rpte, unsigned int subpg_index);
> 
> I wonder what purpose set_hidx_slot() defined previously, served.
> 
> > +
> >  struct mm_struct;
> >  unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap);
> >  extern int hash_page_mm(struct mm_struct *mm, unsigned long ea,
> > diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c
> > index 44fe483..b832ed3 100644
> > --- a/arch/powerpc/mm/dump_linuxpagetables.c
> > +++ b/arch/powerpc/mm/dump_linuxpagetables.c
> > @@ -213,7 +213,7 @@ struct flag_info {
> >  		.val	= H_PAGE_4K_PFN,
> >  		.set	= "4K_pfn",
> >  	}, {
> > -#endif
> > +#else
> >  		.mask	= H_PAGE_F_GIX,
> >  		.val	= H_PAGE_F_GIX,
> >  		.set	= "f_gix",
> > @@ -224,6 +224,7 @@ struct flag_info {
> >  		.val	= H_PAGE_F_SECOND,
> >  		.set	= "f_second",
> >  	}, {
> > +#endif /* CONFIG_PPC_64K_PAGES */
> 
> Are we adding H_PAGE_F_GIX as an element for 4K mapping ?

I think there is mistake here. 
In the next patch when these bits are divorsed from
64K ptes entirely, we will not need the above code for 64K ptes.
But good catch. Will fix the error in this patch.

> 
> >  #endif
> >  		.mask	= _PAGE_SPECIAL,
> >  		.val	= _PAGE_SPECIAL,
> > diff --git a/arch/powerpc/mm/hash64_4k.c b/arch/powerpc/mm/hash64_4k.c
> > index 6fa450c..c673829 100644
> > --- a/arch/powerpc/mm/hash64_4k.c
> > +++ b/arch/powerpc/mm/hash64_4k.c
> > @@ -20,6 +20,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
> >  		   pte_t *ptep, unsigned long trap, unsigned long flags,
> >  		   int ssize, int subpg_prot)
> >  {
> > +	real_pte_t rpte;
> >  	unsigned long hpte_group;
> >  	unsigned long rflags, pa;
> >  	unsigned long old_pte, new_pte;
> > @@ -54,6 +55,7 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
> >  	 * need to add in 0x1 if it's a read-only user page
> >  	 */
> >  	rflags = htab_convert_pte_flags(new_pte);
> > +	rpte = __real_pte(__pte(old_pte), ptep);
> > 
> >  	if (cpu_has_feature(CPU_FTR_NOEXECUTE) &&
> >  	    !cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
> > @@ -64,13 +66,10 @@ int __hash_page_4K(unsigned long ea, unsigned long access, unsigned long vsid,
> >  		/*
> >  		 * There MIGHT be an HPTE for this pte
> >  		 */
> > -		hash = hpt_hash(vpn, shift, ssize);
> > -		if (old_pte & H_PAGE_F_SECOND)
> > -			hash = ~hash;
> > -		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
> > -		slot += (old_pte & H_PAGE_F_GIX) >> H_PAGE_F_GIX_SHIFT;
> > +		unsigned long gslot = get_hidx_gslot(vpn, shift,
> > +						ssize, rpte, 0);
> 
> I am wondering why there is a 'g' before the slot in all these
> functions.

Right. even i was confused initially. :)

hash table slots are originized as one big table. 8 consecutive entires
in that table form a bucket.  the term slot is used to refer to the
slot within the bucket.  the term gslot is used to refer to an entry
in the table.  roughly speaking slot 2 in bucket 2, will be gslot 2*8+2=18.

> 
> Its already too much of changes in a single patch. Being a single
> logical change it needs to be inside a single change but then we
> need much more description in the commit message for some one to
> understand what all changed and how.

I have further broken down this patch, one to introduce get_hidx_gslot()
one to introduce set_hidx_slot() . Hopefully that will reduce the size
of the patch to graspable level. let me know,


Thanks for your valuable comments,
RP


-- 
Ram Pai

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | linux.kernel


csiph-web