1/* 2 * iSeries hashtable management. 3 * Derived from pSeries_htab.c 4 * 5 * SMP scalability work: 6 * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 11 * 2 of the License, or (at your option) any later version. 12 */ 13#include <asm/machdep.h> 14#include <asm/pgtable.h> 15#include <asm/mmu.h> 16#include <asm/mmu_context.h> 17#include <asm/abs_addr.h> 18#include <linux/spinlock.h> 19 20#include "call_hpt.h" 21 22static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp; 23 24/* 25 * Very primitive algorithm for picking up a lock 26 */ 27static inline void iSeries_hlock(unsigned long slot) 28{ 29 if (slot & 0x8) 30 slot = ~slot; 31 spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]); 32} 33 34static inline void iSeries_hunlock(unsigned long slot) 35{ 36 if (slot & 0x8) 37 slot = ~slot; 38 spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]); 39} 40 41static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, 42 unsigned long pa, unsigned long rflags, 43 unsigned long vflags, int psize, int ssize) 44{ 45 long slot; 46 struct hash_pte lhpte; 47 int secondary = 0; 48 49 BUG_ON(psize != MMU_PAGE_4K); 50 51 /* 52 * The hypervisor tries both primary and secondary. 53 * If we are being called to insert in the secondary, 54 * it means we have already tried both primary and secondary, 55 * so we return failure immediately. 56 */ 57 if (vflags & HPTE_V_SECONDARY) 58 return -1; 59 60 iSeries_hlock(hpte_group); 61 62 slot = HvCallHpt_findValid(&lhpte, va >> HW_PAGE_SHIFT); 63 if (unlikely(lhpte.v & HPTE_V_VALID)) { 64 if (vflags & HPTE_V_BOLTED) { 65 HvCallHpt_setSwBits(slot, 0x10, 0); 66 HvCallHpt_setPp(slot, PP_RWXX); 67 iSeries_hunlock(hpte_group); 68 if (slot < 0) 69 return 0x8 | (slot & 7); 70 else 71 return slot & 7; 72 } 73 BUG(); 74 } 75 76 if (slot == -1) { /* No available entry found in either group */ 77 iSeries_hunlock(hpte_group); 78 return -1; 79 } 80 81 if (slot < 0) { /* MSB set means secondary group */ 82 vflags |= HPTE_V_SECONDARY; 83 secondary = 1; 84 slot &= 0x7fffffffffffffff; 85 } 86 87 88 lhpte.v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M) | 89 vflags | HPTE_V_VALID; 90 lhpte.r = hpte_encode_r(phys_to_abs(pa), MMU_PAGE_4K) | rflags; 91 92 /* Now fill in the actual HPTE */ 93 HvCallHpt_addValidate(slot, secondary, &lhpte); 94 95 iSeries_hunlock(hpte_group); 96 97 return (secondary << 3) | (slot & 7); 98} 99 100static unsigned long iSeries_hpte_getword0(unsigned long slot) 101{ 102 struct hash_pte hpte; 103 104 HvCallHpt_get(&hpte, slot); 105 return hpte.v; 106} 107 108static long iSeries_hpte_remove(unsigned long hpte_group) 109{ 110 unsigned long slot_offset; 111 int i; 112 unsigned long hpte_v; 113 114 /* Pick a random slot to start at */ 115 slot_offset = mftb() & 0x7; 116 117 iSeries_hlock(hpte_group); 118 119 for (i = 0; i < HPTES_PER_GROUP; i++) { 120 hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset); 121 122 if (! (hpte_v & HPTE_V_BOLTED)) { 123 HvCallHpt_invalidateSetSwBitsGet(hpte_group + 124 slot_offset, 0, 0); 125 iSeries_hunlock(hpte_group); 126 return i; 127 } 128 129 slot_offset++; 130 slot_offset &= 0x7; 131 } 132 133 iSeries_hunlock(hpte_group); 134 135 return -1; 136} 137 138/* 139 * The HyperVisor expects the "flags" argument in this form: 140 * bits 0..59 : reserved 141 * bit 60 : N 142 * bits 61..63 : PP2,PP1,PP0 143 */ 144static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, 145 unsigned long va, int psize, int ssize, int local) 146{ 147 struct hash_pte hpte; 148 unsigned long want_v; 149 150 iSeries_hlock(slot); 151 152 HvCallHpt_get(&hpte, slot); 153 want_v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M); 154 155 if (HPTE_V_COMPARE(hpte.v, want_v) && (hpte.v & HPTE_V_VALID)) { 156 /* 157 * Hypervisor expects bits as NPPP, which is 158 * different from how they are mapped in our PP. 159 */ 160 HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1)); 161 iSeries_hunlock(slot); 162 return 0; 163 } 164 iSeries_hunlock(slot); 165 166 return -1; 167} 168 169/* 170 * Functions used to find the PTE for a particular virtual address. 171 * Only used during boot when bolting pages. 172 * 173 * Input : vpn : virtual page number 174 * Output: PTE index within the page table of the entry 175 * -1 on failure 176 */ 177static long iSeries_hpte_find(unsigned long vpn) 178{ 179 struct hash_pte hpte; 180 long slot; 181 182 /* 183 * The HvCallHpt_findValid interface is as follows: 184 * 0xffffffffffffffff : No entry found. 185 * 0x00000000xxxxxxxx : Entry found in primary group, slot x 186 * 0x80000000xxxxxxxx : Entry found in secondary group, slot x 187 */ 188 slot = HvCallHpt_findValid(&hpte, vpn); 189 if (hpte.v & HPTE_V_VALID) { 190 if (slot < 0) { 191 slot &= 0x7fffffffffffffff; 192 slot = -slot; 193 } 194 } else 195 slot = -1; 196 return slot; 197} 198 199/* 200 * Update the page protection bits. Intended to be used to create 201 * guard pages for kernel data structures on pages which are bolted 202 * in the HPT. Assumes pages being operated on will not be stolen. 203 * Does not work on large pages. 204 * 205 * No need to lock here because we should be the only user. 206 */ 207static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, 208 int psize, int ssize) 209{ 210 unsigned long vsid,va,vpn; 211 long slot; 212 213 BUG_ON(psize != MMU_PAGE_4K); 214 215 vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M); 216 va = (vsid << 28) | (ea & 0x0fffffff); 217 vpn = va >> HW_PAGE_SHIFT; 218 slot = iSeries_hpte_find(vpn); 219 if (slot == -1) 220 panic("updateboltedpp: Could not find page to bolt\n"); 221 HvCallHpt_setPp(slot, newpp); 222} 223 224static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va, 225 int psize, int ssize, int local) 226{ 227 unsigned long hpte_v; 228 unsigned long avpn = va >> 23; 229 unsigned long flags; 230 231 local_irq_save(flags); 232 233 iSeries_hlock(slot); 234 235 hpte_v = iSeries_hpte_getword0(slot); 236 237 if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID)) 238 HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0); 239 240 iSeries_hunlock(slot); 241 242 local_irq_restore(flags); 243} 244 245void __init hpte_init_iSeries(void) 246{ 247 int i; 248 249 for (i = 0; i < ARRAY_SIZE(iSeries_hlocks); i++) 250 spin_lock_init(&iSeries_hlocks[i]); 251 252 ppc_md.hpte_invalidate = iSeries_hpte_invalidate; 253 ppc_md.hpte_updatepp = iSeries_hpte_updatepp; 254 ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp; 255 ppc_md.hpte_insert = iSeries_hpte_insert; 256 ppc_md.hpte_remove = iSeries_hpte_remove; 257} 258