#include <linux/config.h>
#include <asm/page.h>
-#include <linux/stringify.h>
-#ifndef __ASSEMBLY__
-
-/* Time to allow for more things here */
-typedef unsigned long mm_context_id_t;
-typedef struct {
- mm_context_id_t id;
-#ifdef CONFIG_HUGETLB_PAGE
- pgd_t *huge_pgdir;
- u16 htlb_segs; /* bitmask */
-#endif
-} mm_context_t;
+/*
+ * Segment table
+ */
#define STE_ESID_V 0x80
#define STE_ESID_KS 0x20
#define STE_VSID_SHIFT 12
-struct stab_entry {
- unsigned long esid_data;
- unsigned long vsid_data;
-};
+/* Location of cpu0's segment table */
+#define STAB0_PAGE 0x9
+#define STAB0_PHYS_ADDR (STAB0_PAGE<<PAGE_SHIFT)
+#define STAB0_VIRT_ADDR (KERNELBASE+STAB0_PHYS_ADDR)
-/* Hardware Page Table Entry */
+/*
+ * SLB
+ */
-#define HPTES_PER_GROUP 8
+#define SLB_NUM_BOLTED 3
+#define SLB_CACHE_ENTRIES 8
-typedef struct {
- unsigned long avpn:57; /* vsid | api == avpn */
- unsigned long : 2; /* Software use */
- unsigned long bolted: 1; /* HPTE is "bolted" */
- unsigned long lock: 1; /* lock on pSeries SMP */
- unsigned long l: 1; /* Virtual page is large (L=1) or 4 KB (L=0) */
- unsigned long h: 1; /* Hash function identifier */
- unsigned long v: 1; /* Valid (v=1) or invalid (v=0) */
-} Hpte_dword0;
+/* Bits in the SLB ESID word */
+#define SLB_ESID_V ASM_CONST(0x0000000008000000) /* valid */
-typedef struct {
- unsigned long pp0: 1; /* Page protection bit 0 */
- unsigned long ts: 1; /* Tag set bit */
- unsigned long rpn: 50; /* Real page number */
- unsigned long : 2; /* Reserved */
- unsigned long ac: 1; /* Address compare */
- unsigned long r: 1; /* Referenced */
- unsigned long c: 1; /* Changed */
- unsigned long w: 1; /* Write-thru cache mode */
- unsigned long i: 1; /* Cache inhibited */
- unsigned long m: 1; /* Memory coherence required */
- unsigned long g: 1; /* Guarded */
- unsigned long n: 1; /* No-execute */
- unsigned long pp: 2; /* Page protection bits 1:2 */
-} Hpte_dword1;
+/* Bits in the SLB VSID word */
+#define SLB_VSID_SHIFT 12
+#define SLB_VSID_KS ASM_CONST(0x0000000000000800)
+#define SLB_VSID_KP ASM_CONST(0x0000000000000400)
+#define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */
+#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage */
+#define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */
+#define SLB_VSID_LS ASM_CONST(0x0000000000000070) /* size of largepage */
+
+#define SLB_VSID_KERNEL (SLB_VSID_KP|SLB_VSID_C)
+#define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS)
-typedef struct {
- char padding[6]; /* padding */
- unsigned long : 6; /* padding */
- unsigned long flags: 10; /* HPTE flags */
-} Hpte_dword1_flags;
+/*
+ * Hash table
+ */
-typedef struct {
- union {
- unsigned long dword0;
- Hpte_dword0 dw0;
- } dw0;
-
- union {
- unsigned long dword1;
- Hpte_dword1 dw1;
- Hpte_dword1_flags flags;
- } dw1;
-} HPTE;
+#define HPTES_PER_GROUP 8
+
+#define HPTE_V_AVPN_SHIFT 7
+#define HPTE_V_AVPN ASM_CONST(0xffffffffffffff80)
+#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
+#define HPTE_V_BOLTED ASM_CONST(0x0000000000000010)
+#define HPTE_V_LOCK ASM_CONST(0x0000000000000008)
+#define HPTE_V_LARGE ASM_CONST(0x0000000000000004)
+#define HPTE_V_SECONDARY ASM_CONST(0x0000000000000002)
+#define HPTE_V_VALID ASM_CONST(0x0000000000000001)
+
+#define HPTE_R_PP0 ASM_CONST(0x8000000000000000)
+#define HPTE_R_TS ASM_CONST(0x4000000000000000)
+#define HPTE_R_RPN_SHIFT 12
+#define HPTE_R_RPN ASM_CONST(0x3ffffffffffff000)
+#define HPTE_R_FLAGS ASM_CONST(0x00000000000003ff)
+#define HPTE_R_PP ASM_CONST(0x0000000000000003)
/* Values for PP (assumes Ks=0, Kp=1) */
/* pp0 will always be 0 for linux */
#define PP_RWRW 2 /* Supervisor read/write, User read/write */
#define PP_RXRX 3 /* Supervisor read, User read */
+#ifndef __ASSEMBLY__
+
+typedef struct {
+ unsigned long v;
+ unsigned long r;
+} hpte_t;
-extern HPTE * htab_address;
-extern unsigned long htab_hash_mask;
+extern hpte_t *htab_address;
+extern unsigned long htab_hash_mask;
static inline unsigned long hpt_hash(unsigned long vpn, int large)
{
asm volatile("ptesync": : :"memory");
}
+static inline unsigned long slot2va(unsigned long hpte_v, unsigned long slot)
+{
+ unsigned long avpn = HPTE_V_AVPN_VAL(hpte_v);
+ unsigned long va;
+
+ va = avpn << 23;
+
+ if (! (hpte_v & HPTE_V_LARGE)) {
+ unsigned long vpi, pteg;
+
+ pteg = slot / HPTES_PER_GROUP;
+ if (hpte_v & HPTE_V_SECONDARY)
+ pteg = ~pteg;
+
+ vpi = ((va >> 28) ^ pteg) & htab_hash_mask;
+
+ va |= vpi << PAGE_SHIFT;
+ }
+
+ return va;
+}
+
/*
* Handle a fault by adding an HPTE. If the address can't be determined
* to be valid via Linux page tables, return 1. If handled return 0
extern void htab_finish_init(void);
-#endif /* __ASSEMBLY__ */
-
-/*
- * Location of cpu0's segment table
- */
-#define STAB0_PAGE 0x9
-#define STAB0_PHYS_ADDR (STAB0_PAGE<<PAGE_SHIFT)
-#define STAB0_VIRT_ADDR (KERNELBASE+STAB0_PHYS_ADDR)
+extern void hpte_init_native(void);
+extern void hpte_init_lpar(void);
+extern void hpte_init_iSeries(void);
-#define SLB_NUM_BOLTED 3
-#define SLB_CACHE_ENTRIES 8
+extern long pSeries_lpar_hpte_insert(unsigned long hpte_group,
+ unsigned long va, unsigned long prpn,
+ unsigned long vflags,
+ unsigned long rflags);
+extern long native_hpte_insert(unsigned long hpte_group, unsigned long va,
+ unsigned long prpn,
+ unsigned long vflags, unsigned long rflags);
-/* Bits in the SLB ESID word */
-#define SLB_ESID_V 0x0000000008000000 /* entry is valid */
+extern void stabs_alloc(void);
-/* Bits in the SLB VSID word */
-#define SLB_VSID_SHIFT 12
-#define SLB_VSID_KS 0x0000000000000800
-#define SLB_VSID_KP 0x0000000000000400
-#define SLB_VSID_N 0x0000000000000200 /* no-execute */
-#define SLB_VSID_L 0x0000000000000100 /* largepage (4M) */
-#define SLB_VSID_C 0x0000000000000080 /* class */
+#endif /* __ASSEMBLY__ */
-#define SLB_VSID_KERNEL (SLB_VSID_KP|SLB_VSID_C)
-#define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS)
+/*
+ * VSID allocation
+ *
+ * We first generate a 36-bit "proto-VSID". For kernel addresses this
+ * is equal to the ESID, for user addresses it is:
+ * (context << 15) | (esid & 0x7fff)
+ *
+ * The two forms are distinguishable because the top bit is 0 for user
+ * addresses, whereas the top two bits are 1 for kernel addresses.
+ * Proto-VSIDs with the top two bits equal to 0b10 are reserved for
+ * now.
+ *
+ * The proto-VSIDs are then scrambled into real VSIDs with the
+ * multiplicative hash:
+ *
+ * VSID = (proto-VSID * VSID_MULTIPLIER) % VSID_MODULUS
+ * where VSID_MULTIPLIER = 268435399 = 0xFFFFFC7
+ * VSID_MODULUS = 2^36-1 = 0xFFFFFFFFF
+ *
+ * This scramble is only well defined for proto-VSIDs below
+ * 0xFFFFFFFFF, so both proto-VSID and actual VSID 0xFFFFFFFFF are
+ * reserved. VSID_MULTIPLIER is prime, so in particular it is
+ * co-prime to VSID_MODULUS, making this a 1:1 scrambling function.
+ * Because the modulus is 2^n-1 we can compute it efficiently without
+ * a divide or extra multiply (see below).
+ *
+ * This scheme has several advantages over older methods:
+ *
+ * - We have VSIDs allocated for every kernel address
+ * (i.e. everything above 0xC000000000000000), except the very top
+ * segment, which simplifies several things.
+ *
+ * - We allow for 15 significant bits of ESID and 20 bits of
+ * context for user addresses. i.e. 8T (43 bits) of address space for
+ * up to 1M contexts (although the page table structure and context
+ * allocation will need changes to take advantage of this).
+ *
+ * - The scramble function gives robust scattering in the hash
+ * table (at least based on some initial results). The previous
+ * method was more susceptible to pathological cases giving excessive
+ * hash collisions.
+ */
+/*
+ * WARNING - If you change these you must make sure the asm
+ * implementations in slb_allocate (slb_low.S), do_stab_bolted
+ * (head.S) and ASM_VSID_SCRAMBLE (below) are changed accordingly.
+ *
+ * You'll also need to change the precomputed VSID values in head.S
+ * which are used by the iSeries firmware.
+ */
#define VSID_MULTIPLIER ASM_CONST(200730139) /* 28-bit prime */
#define VSID_BITS 36
#define VSID_MODULUS ((1UL<<VSID_BITS)-1)
-#define CONTEXT_BITS 20
-#define USER_ESID_BITS 15
+#define CONTEXT_BITS 19
+#define USER_ESID_BITS 16
+
+#define USER_VSID_RANGE (1UL << (USER_ESID_BITS + SID_SHIFT))
/*
* This macro generates asm code to compute the VSID scramble
srdi rx,rx,VSID_BITS; /* extract 2^36 bit */ \
add rt,rt,rx
+
+#ifndef __ASSEMBLY__
+
+typedef unsigned long mm_context_id_t;
+
+typedef struct {
+ mm_context_id_t id;
+#ifdef CONFIG_HUGETLB_PAGE
+ u16 htlb_segs; /* bitmask */
+#endif
+} mm_context_t;
+
+
+static inline unsigned long vsid_scramble(unsigned long protovsid)
+{
+#if 0
+ /* The code below is equivalent to this function for arguments
+ * < 2^VSID_BITS, which is all this should ever be called
+ * with. However gcc is not clever enough to compute the
+ * modulus (2^n-1) without a second multiply. */
+ return ((protovsid * VSID_MULTIPLIER) % VSID_MODULUS);
+#else /* 1 */
+ unsigned long x;
+
+ x = protovsid * VSID_MULTIPLIER;
+ x = (x >> VSID_BITS) + (x & VSID_MODULUS);
+ return (x + ((x+1) >> VSID_BITS)) & VSID_MODULUS;
+#endif /* 1 */
+}
+
+/* This is only valid for addresses >= KERNELBASE */
+static inline unsigned long get_kernel_vsid(unsigned long ea)
+{
+ return vsid_scramble(ea >> SID_SHIFT);
+}
+
+/* This is only valid for user addresses (which are below 2^41) */
+static inline unsigned long get_vsid(unsigned long context, unsigned long ea)
+{
+ return vsid_scramble((context << USER_ESID_BITS)
+ | (ea >> SID_SHIFT));
+}
+
+#define VSID_SCRAMBLE(pvsid) (((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS)
+#define KERNEL_VSID(ea) VSID_SCRAMBLE(GET_ESID(ea))
+
+#endif /* __ASSEMBLY */
+
#endif /* _PPC64_MMU_H_ */