1 /* 2 * Based on linux/arch/arm/pmsa-v7.c 3 * 4 * ARM PMSAv8 supporting functions. 5 */ 6 7 #include <linux/memblock.h> 8 #include <linux/range.h> 9 10 #include <asm/cp15.h> 11 #include <asm/cputype.h> 12 #include <asm/mpu.h> 13 14 #include <asm/page.h> 15 #include <asm/sections.h> 16 17 #include "mm.h" 18 19 #ifndef CONFIG_CPU_V7M 20 21 #define PRSEL __ACCESS_CP15(c6, 0, c2, 1) 22 #define PRBAR __ACCESS_CP15(c6, 0, c3, 0) 23 #define PRLAR __ACCESS_CP15(c6, 0, c3, 1) 24 25 static inline u32 prlar_read(void) 26 { 27 return read_sysreg(PRLAR); 28 } 29 30 static inline u32 prbar_read(void) 31 { 32 return read_sysreg(PRBAR); 33 } 34 35 static inline void prsel_write(u32 v) 36 { 37 write_sysreg(v, PRSEL); 38 } 39 40 static inline void prbar_write(u32 v) 41 { 42 write_sysreg(v, PRBAR); 43 } 44 45 static inline void prlar_write(u32 v) 46 { 47 write_sysreg(v, PRLAR); 48 } 49 #else 50 51 static inline u32 prlar_read(void) 52 { 53 return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR); 54 } 55 56 static inline u32 prbar_read(void) 57 { 58 return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR); 59 } 60 61 static inline void prsel_write(u32 v) 62 { 63 writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR); 64 } 65 66 static inline void prbar_write(u32 v) 67 { 68 writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR); 69 } 70 71 static inline void prlar_write(u32 v) 72 { 73 writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR); 74 } 75 76 #endif 77 78 static struct range __initdata io[MPU_MAX_REGIONS]; 79 static struct range __initdata mem[MPU_MAX_REGIONS]; 80 81 static unsigned int __initdata mpu_max_regions; 82 83 static __init bool is_region_fixed(int number) 84 { 85 switch (number) { 86 case PMSAv8_XIP_REGION: 87 case PMSAv8_KERNEL_REGION: 88 return true; 89 default: 90 return false; 91 } 92 } 93 94 void __init pmsav8_adjust_lowmem_bounds(void) 95 { 96 phys_addr_t mem_end; 97 phys_addr_t reg_start, reg_end; 98 bool first = true; 99 u64 i; 100 101 for_each_mem_range(i, ®_start, ®_end) { 102 if (first) { 103 phys_addr_t phys_offset = PHYS_OFFSET; 104 105 /* 106 * Initially only use memory continuous from 107 * PHYS_OFFSET */ 108 if (reg_start != phys_offset) 109 panic("First memory bank must be contiguous from PHYS_OFFSET"); 110 mem_end = reg_end; 111 first = false; 112 } else { 113 /* 114 * memblock auto merges contiguous blocks, remove 115 * all blocks afterwards in one go (we can't remove 116 * blocks separately while iterating) 117 */ 118 pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n", 119 &mem_end, ®_start); 120 memblock_remove(reg_start, 0 - reg_start); 121 break; 122 } 123 } 124 } 125 126 static int __init __mpu_max_regions(void) 127 { 128 static int max_regions; 129 u32 mpuir; 130 131 if (max_regions) 132 return max_regions; 133 134 mpuir = read_cpuid_mputype(); 135 136 max_regions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION; 137 138 return max_regions; 139 } 140 141 static int __init __pmsav8_setup_region(unsigned int number, u32 bar, u32 lar) 142 { 143 if (number > mpu_max_regions 144 || number >= MPU_MAX_REGIONS) 145 return -ENOENT; 146 147 dsb(); 148 prsel_write(number); 149 isb(); 150 prbar_write(bar); 151 prlar_write(lar); 152 153 mpu_rgn_info.rgns[number].prbar = bar; 154 mpu_rgn_info.rgns[number].prlar = lar; 155 156 mpu_rgn_info.used++; 157 158 return 0; 159 } 160 161 static int __init pmsav8_setup_ram(unsigned int number, phys_addr_t start,phys_addr_t end) 162 { 163 u32 bar, lar; 164 165 if (is_region_fixed(number)) 166 return -EINVAL; 167 168 bar = start; 169 lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); 170 171 bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED; 172 lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; 173 174 return __pmsav8_setup_region(number, bar, lar); 175 } 176 177 static int __init pmsav8_setup_io(unsigned int number, phys_addr_t start,phys_addr_t end) 178 { 179 u32 bar, lar; 180 181 if (is_region_fixed(number)) 182 return -EINVAL; 183 184 bar = start; 185 lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); 186 187 bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED | PMSAv8_BAR_XN; 188 lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_DEVICE_nGnRnE) | PMSAv8_LAR_EN; 189 190 return __pmsav8_setup_region(number, bar, lar); 191 } 192 193 static int __init pmsav8_setup_fixed(unsigned int number, phys_addr_t start,phys_addr_t end) 194 { 195 u32 bar, lar; 196 197 if (!is_region_fixed(number)) 198 return -EINVAL; 199 200 bar = start; 201 lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); 202 203 bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED; 204 lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; 205 206 prsel_write(number); 207 isb(); 208 209 if (prbar_read() != bar || prlar_read() != lar) 210 return -EINVAL; 211 212 /* Reserved region was set up early, we just need a record for secondaries */ 213 mpu_rgn_info.rgns[number].prbar = bar; 214 mpu_rgn_info.rgns[number].prlar = lar; 215 216 mpu_rgn_info.used++; 217 218 return 0; 219 } 220 221 #ifndef CONFIG_CPU_V7M 222 static int __init pmsav8_setup_vector(unsigned int number, phys_addr_t start,phys_addr_t end) 223 { 224 u32 bar, lar; 225 226 if (number == PMSAv8_KERNEL_REGION) 227 return -EINVAL; 228 229 bar = start; 230 lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); 231 232 bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED; 233 lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; 234 235 return __pmsav8_setup_region(number, bar, lar); 236 } 237 #endif 238 239 void __init pmsav8_setup(void) 240 { 241 int i, err = 0; 242 int region = PMSAv8_KERNEL_REGION; 243 244 /* How many regions are supported ? */ 245 mpu_max_regions = __mpu_max_regions(); 246 247 /* RAM: single chunk of memory */ 248 add_range(mem, ARRAY_SIZE(mem), 0, memblock.memory.regions[0].base, 249 memblock.memory.regions[0].base + memblock.memory.regions[0].size); 250 251 /* IO: cover full 4G range */ 252 add_range(io, ARRAY_SIZE(io), 0, 0, 0xffffffff); 253 254 /* RAM and IO: exclude kernel */ 255 subtract_range(mem, ARRAY_SIZE(mem), __pa(KERNEL_START), __pa(KERNEL_END)); 256 subtract_range(io, ARRAY_SIZE(io), __pa(KERNEL_START), __pa(KERNEL_END)); 257 258 #ifdef CONFIG_XIP_KERNEL 259 /* RAM and IO: exclude xip */ 260 subtract_range(mem, ARRAY_SIZE(mem), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); 261 subtract_range(io, ARRAY_SIZE(io), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); 262 #endif 263 264 #ifndef CONFIG_CPU_V7M 265 /* RAM and IO: exclude vectors */ 266 subtract_range(mem, ARRAY_SIZE(mem), vectors_base, vectors_base + 2 * PAGE_SIZE); 267 subtract_range(io, ARRAY_SIZE(io), vectors_base, vectors_base + 2 * PAGE_SIZE); 268 #endif 269 /* IO: exclude RAM */ 270 for (i = 0; i < ARRAY_SIZE(mem); i++) 271 subtract_range(io, ARRAY_SIZE(io), mem[i].start, mem[i].end); 272 273 /* Now program MPU */ 274 275 #ifdef CONFIG_XIP_KERNEL 276 /* ROM */ 277 err |= pmsav8_setup_fixed(PMSAv8_XIP_REGION, CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); 278 #endif 279 /* Kernel */ 280 err |= pmsav8_setup_fixed(region++, __pa(KERNEL_START), __pa(KERNEL_END)); 281 282 283 /* IO */ 284 for (i = 0; i < ARRAY_SIZE(io); i++) { 285 if (!io[i].end) 286 continue; 287 288 err |= pmsav8_setup_io(region++, io[i].start, io[i].end); 289 } 290 291 /* RAM */ 292 for (i = 0; i < ARRAY_SIZE(mem); i++) { 293 if (!mem[i].end) 294 continue; 295 296 err |= pmsav8_setup_ram(region++, mem[i].start, mem[i].end); 297 } 298 299 /* Vectors */ 300 #ifndef CONFIG_CPU_V7M 301 err |= pmsav8_setup_vector(region++, vectors_base, vectors_base + 2 * PAGE_SIZE); 302 #endif 303 if (err) 304 pr_warn("MPU region initialization failure! %d", err); 305 else 306 pr_info("Using ARM PMSAv8 Compliant MPU. Used %d of %d regions\n", 307 mpu_rgn_info.used, mpu_max_regions); 308 } 309
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.