1 /* 2 * PS3 pagetable management routines. 3 * 4 * Copyright (C) 2006 Sony Computer Entertainment Inc. 5 * Copyright 2006, 2007 Sony Corporation 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; version 2 of the License. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 #include <linux/kernel.h> 22 #include <linux/memblock.h> 23 24 #include <asm/machdep.h> 25 #include <asm/prom.h> 26 #include <asm/udbg.h> 27 #include <asm/lv1call.h> 28 #include <asm/ps3fb.h> 29 30 #define PS3_VERBOSE_RESULT 31 #include "platform.h" 32 33 /** 34 * enum lpar_vas_id - id of LPAR virtual address space. 35 * @lpar_vas_id_current: Current selected virtual address space 36 * 37 * Identify the target LPAR address space. 38 */ 39 40 enum ps3_lpar_vas_id { 41 PS3_LPAR_VAS_ID_CURRENT = 0, 42 }; 43 44 45 static DEFINE_SPINLOCK(ps3_htab_lock); 46 47 static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn, 48 unsigned long pa, unsigned long rflags, unsigned long vflags, 49 int psize, int apsize, int ssize) 50 { 51 int result; 52 u64 hpte_v, hpte_r; 53 u64 inserted_index; 54 u64 evicted_v, evicted_r; 55 u64 hpte_v_array[4], hpte_rs; 56 unsigned long flags; 57 long ret = -1; 58 59 /* 60 * lv1_insert_htab_entry() will search for victim 61 * entry in both primary and secondary pte group 62 */ 63 vflags &= ~HPTE_V_SECONDARY; 64 65 hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID; 66 hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags; 67 68 spin_lock_irqsave(&ps3_htab_lock, flags); 69 70 /* talk hvc to replace entries BOLTED == 0 */ 71 result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group, 72 hpte_v, hpte_r, 73 HPTE_V_BOLTED, 0, 74 &inserted_index, 75 &evicted_v, &evicted_r); 76 77 if (result) { 78 /* all entries bolted !*/ 79 pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n", 80 __func__, ps3_result(result), vpn, pa, hpte_group, 81 hpte_v, hpte_r); 82 BUG(); 83 } 84 85 /* 86 * see if the entry is inserted into secondary pteg 87 */ 88 result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, 89 inserted_index & ~0x3UL, 90 &hpte_v_array[0], &hpte_v_array[1], 91 &hpte_v_array[2], &hpte_v_array[3], 92 &hpte_rs); 93 BUG_ON(result); 94 95 if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY) 96 ret = (inserted_index & 7) | (1 << 3); 97 else 98 ret = inserted_index & 7; 99 100 spin_unlock_irqrestore(&ps3_htab_lock, flags); 101 102 return ret; 103 } 104 105 static long ps3_hpte_remove(unsigned long hpte_group) 106 { 107 panic("ps3_hpte_remove() not implemented"); 108 return 0; 109 } 110 111 static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, 112 unsigned long vpn, int psize, int apsize, 113 int ssize, int local) 114 { 115 int result; 116 u64 hpte_v, want_v, hpte_rs; 117 u64 hpte_v_array[4]; 118 unsigned long flags; 119 long ret; 120 121 want_v = hpte_encode_avpn(vpn, psize, ssize); 122 123 spin_lock_irqsave(&ps3_htab_lock, flags); 124 125 result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL, 126 &hpte_v_array[0], &hpte_v_array[1], 127 &hpte_v_array[2], &hpte_v_array[3], 128 &hpte_rs); 129 130 if (result) { 131 pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n", 132 __func__, ps3_result(result), vpn, slot, psize); 133 BUG(); 134 } 135 136 hpte_v = hpte_v_array[slot % 4]; 137 138 /* 139 * As lv1_read_htab_entries() does not give us the RPN, we can 140 * not synthesize the new hpte_r value here, and therefore can 141 * not update the hpte with lv1_insert_htab_entry(), so we 142 * instead invalidate it and ask the caller to update it via 143 * ps3_hpte_insert() by returning a -1 value. 144 */ 145 if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { 146 /* not found */ 147 ret = -1; 148 } else { 149 /* entry found, just invalidate it */ 150 result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, 151 slot, 0, 0); 152 ret = -1; 153 } 154 155 spin_unlock_irqrestore(&ps3_htab_lock, flags); 156 return ret; 157 } 158 159 static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, 160 int psize, int ssize) 161 { 162 panic("ps3_hpte_updateboltedpp() not implemented"); 163 } 164 165 static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn, 166 int psize, int apsize, int ssize, int local) 167 { 168 unsigned long flags; 169 int result; 170 171 spin_lock_irqsave(&ps3_htab_lock, flags); 172 173 result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0); 174 175 if (result) { 176 pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n", 177 __func__, ps3_result(result), vpn, slot, psize); 178 BUG(); 179 } 180 181 spin_unlock_irqrestore(&ps3_htab_lock, flags); 182 } 183 184 static void ps3_hpte_clear(void) 185 { 186 unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4; 187 u64 i; 188 189 for (i = 0; i < hpte_count; i++) 190 lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0); 191 192 ps3_mm_shutdown(); 193 ps3_mm_vas_destroy(); 194 } 195 196 void __init ps3_hpte_init(unsigned long htab_size) 197 { 198 ppc_md.hpte_invalidate = ps3_hpte_invalidate; 199 ppc_md.hpte_updatepp = ps3_hpte_updatepp; 200 ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; 201 ppc_md.hpte_insert = ps3_hpte_insert; 202 ppc_md.hpte_remove = ps3_hpte_remove; 203 ppc_md.hpte_clear_all = ps3_hpte_clear; 204 205 ppc64_pft_size = __ilog2(htab_size); 206 } 207 208