1 /* $NetBSD: loadfile_machdep.c,v 1.10 2011/05/21 16:32:00 nakayama Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This work is based on the code contributed by Robert Drehmel to the 8 * FreeBSD project. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <lib/libsa/stand.h> 33 #include <lib/libkern/libkern.h> 34 35 #include <machine/pte.h> 36 #include <machine/cpu.h> 37 #include <machine/ctlreg.h> 38 #include <machine/vmparam.h> 39 #include <machine/promlib.h> 40 41 #include "boot.h" 42 #include "openfirm.h" 43 44 45 #define MAXSEGNUM 50 46 #define hi(val) ((uint32_t)(((val) >> 32) & (uint32_t)-1)) 47 #define lo(val) ((uint32_t)((val) & (uint32_t)-1)) 48 49 #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) 50 51 52 typedef int phandle_t; 53 54 extern void itlb_enter(vaddr_t, uint32_t, uint32_t); 55 extern void dtlb_enter(vaddr_t, uint32_t, uint32_t); 56 extern void dtlb_replace(vaddr_t, uint32_t, uint32_t); 57 extern vaddr_t itlb_va_to_pa(vaddr_t); 58 extern vaddr_t dtlb_va_to_pa(vaddr_t); 59 60 static void tlb_init(void); 61 62 static int mmu_mapin(vaddr_t, vsize_t); 63 static ssize_t mmu_read(int, void *, size_t); 64 static void* mmu_memcpy(void *, const void *, size_t); 65 static void* mmu_memset(void *, int, size_t); 66 static void mmu_freeall(void); 67 68 static int ofw_mapin(vaddr_t, vsize_t); 69 static ssize_t ofw_read(int, void *, size_t); 70 static void* ofw_memcpy(void *, const void *, size_t); 71 static void* ofw_memset(void *, int, size_t); 72 static void ofw_freeall(void); 73 74 #if 0 75 static int nop_mapin(vaddr_t, vsize_t); 76 #endif 77 static ssize_t nop_read(int, void *, size_t); 78 static void* nop_memcpy(void *, const void *, size_t); 79 static void* nop_memset(void *, int, size_t); 80 static void nop_freeall(void); 81 82 83 struct tlb_entry *dtlb_store = 0; 84 struct tlb_entry *itlb_store = 0; 85 86 int dtlb_slot; 87 int itlb_slot; 88 int dtlb_slot_max; 89 int itlb_slot_max; 90 91 static struct kvamap { 92 uint64_t start; 93 uint64_t end; 94 } kvamap[MAXSEGNUM]; 95 96 static struct memsw { 97 ssize_t (* read)(int f, void *addr, size_t size); 98 void* (* memcpy)(void *dst, const void *src, size_t size); 99 void* (* memset)(void *dst, int c, size_t size); 100 void (* freeall)(void); 101 } memswa[] = { 102 { nop_read, nop_memcpy, nop_memset, nop_freeall }, 103 { ofw_read, ofw_memcpy, ofw_memset, ofw_freeall }, 104 { mmu_read, mmu_memcpy, mmu_memset, mmu_freeall } 105 }; 106 107 static struct memsw *memsw = &memswa[0]; 108 109 110 /* 111 * Check if a memory region is already mapped. Return length and virtual 112 * address of unmapped sub-region, if any. 113 */ 114 static uint64_t 115 kvamap_extract(vaddr_t va, vsize_t len, vaddr_t *new_va) 116 { 117 int i; 118 119 *new_va = va; 120 for (i = 0; (len > 0) && (i < MAXSEGNUM); i++) { 121 if (kvamap[i].start == NULL) 122 break; 123 if ((kvamap[i].start <= va) && (va < kvamap[i].end)) { 124 uint64_t va_len = kvamap[i].end - va + kvamap[i].start; 125 len = (va_len < len) ? len - va_len : 0; 126 *new_va = kvamap[i].end; 127 } 128 } 129 130 return (len); 131 } 132 133 /* 134 * Record new kernel mapping. 135 */ 136 static void 137 kvamap_enter(uint64_t va, uint64_t len) 138 { 139 int i; 140 141 DPRINTF(("kvamap_enter: %d@%p\n", (int)len, (void*)(u_long)va)); 142 for (i = 0; (len > 0) && (i < MAXSEGNUM); i++) { 143 if (kvamap[i].start == NULL) { 144 kvamap[i].start = va; 145 kvamap[i].end = va + len; 146 break; 147 } 148 } 149 150 if (i == MAXSEGNUM) { 151 panic("Too many allocations requested."); 152 } 153 } 154 155 /* 156 * Initialize TLB as required by MMU mapping functions. 157 */ 158 static void 159 tlb_init(void) 160 { 161 phandle_t child; 162 phandle_t root; 163 char buf[128]; 164 u_int bootcpu; 165 u_int cpu; 166 167 if (dtlb_store != NULL) { 168 return; 169 } 170 171 bootcpu = get_cpuid(); 172 173 if ( (root = prom_findroot()) == -1) { 174 panic("tlb_init: prom_findroot()"); 175 } 176 177 for (child = prom_firstchild(root); child != 0; 178 child = prom_nextsibling(child)) { 179 if (child == -1) { 180 panic("tlb_init: OF_child"); 181 } 182 if (_prom_getprop(child, "device_type", buf, sizeof(buf)) > 0 && 183 strcmp(buf, "cpu") == 0) { 184 if (_prom_getprop(child, "upa-portid", &cpu, 185 sizeof(cpu)) == -1 && _prom_getprop(child, "portid", 186 &cpu, sizeof(cpu)) == -1) 187 panic("tlb_init: prom_getprop"); 188 if (cpu == bootcpu) 189 break; 190 } 191 } 192 if (cpu != bootcpu) 193 panic("tlb_init: no node for bootcpu?!?!"); 194 if (_prom_getprop(child, "#dtlb-entries", &dtlb_slot_max, 195 sizeof(dtlb_slot_max)) == -1 || 196 _prom_getprop(child, "#itlb-entries", &itlb_slot_max, 197 sizeof(itlb_slot_max)) == -1) 198 panic("tlb_init: prom_getprop"); 199 dtlb_store = alloc(dtlb_slot_max * sizeof(*dtlb_store)); 200 itlb_store = alloc(itlb_slot_max * sizeof(*itlb_store)); 201 if (dtlb_store == NULL || itlb_store == NULL) { 202 panic("tlb_init: malloc"); 203 } 204 205 dtlb_slot = itlb_slot = 0; 206 } 207 208 /* 209 * Map requested memory region with permanent 4MB pages. 210 */ 211 static int 212 mmu_mapin(vaddr_t rva, vsize_t len) 213 { 214 uint64_t data; 215 paddr_t pa; 216 vaddr_t va, mva; 217 218 len = roundup2(len + (rva & PAGE_MASK_4M), PAGE_SIZE_4M); 219 rva &= ~PAGE_MASK_4M; 220 221 tlb_init(); 222 for (pa = (paddr_t)-1; len > 0; rva = va) { 223 if ( (len = kvamap_extract(rva, len, &va)) == 0) { 224 /* The rest is already mapped */ 225 break; 226 } 227 228 if (dtlb_va_to_pa(va) == (u_long)-1 || 229 itlb_va_to_pa(va) == (u_long)-1) { 230 /* Allocate a physical page, claim the virtual area */ 231 if (pa == (paddr_t)-1) { 232 pa = OF_alloc_phys(PAGE_SIZE_4M, PAGE_SIZE_4M); 233 if (pa == (paddr_t)-1) 234 panic("out of memory"); 235 mva = OF_claim_virt(va, PAGE_SIZE_4M); 236 if (mva != va) { 237 panic("can't claim virtual page " 238 "(wanted %#lx, got %#lx)", 239 va, mva); 240 } 241 /* The mappings may have changed, be paranoid. */ 242 continue; 243 } 244 245 /* 246 * Actually, we can only allocate two pages less at 247 * most (depending on the kernel TSB size). 248 */ 249 if (dtlb_slot >= dtlb_slot_max) 250 panic("mmu_mapin: out of dtlb_slots"); 251 if (itlb_slot >= itlb_slot_max) 252 panic("mmu_mapin: out of itlb_slots"); 253 254 DPRINTF(("mmu_mapin: 0x%lx:0x%x.0x%x\n", va, 255 hi(pa), lo(pa))); 256 257 data = TSB_DATA(0, /* global */ 258 PGSZ_4M, /* 4mb page */ 259 pa, /* phys.address */ 260 1, /* privileged */ 261 1, /* write */ 262 1, /* cache */ 263 1, /* alias */ 264 1, /* valid */ 265 0 /* endianness */ 266 ); 267 data |= TLB_L | TLB_CV; /* locked, virt.cache */ 268 269 dtlb_store[dtlb_slot].te_pa = pa; 270 dtlb_store[dtlb_slot].te_va = va; 271 dtlb_slot++; 272 dtlb_enter(va, hi(data), lo(data)); 273 pa = (paddr_t)-1; 274 } 275 276 kvamap_enter(va, PAGE_SIZE_4M); 277 278 len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len; 279 va += PAGE_SIZE_4M; 280 } 281 282 if (pa != (paddr_t)-1) { 283 OF_free_phys(pa, PAGE_SIZE_4M); 284 } 285 286 return (0); 287 } 288 289 static ssize_t 290 mmu_read(int f, void *addr, size_t size) 291 { 292 mmu_mapin((vaddr_t)addr, size); 293 return read(f, addr, size); 294 } 295 296 static void* 297 mmu_memcpy(void *dst, const void *src, size_t size) 298 { 299 mmu_mapin((vaddr_t)dst, size); 300 return memcpy(dst, src, size); 301 } 302 303 static void* 304 mmu_memset(void *dst, int c, size_t size) 305 { 306 mmu_mapin((vaddr_t)dst, size); 307 return memset(dst, c, size); 308 } 309 310 static void 311 mmu_freeall(void) 312 { 313 int i; 314 315 dtlb_slot = itlb_slot = 0; 316 for (i = 0; i < MAXSEGNUM; i++) { 317 /* XXX return all mappings to PROM and unmap the pages! */ 318 kvamap[i].start = kvamap[i].end = 0; 319 } 320 } 321 322 /* 323 * Claim requested memory region in OpenFirmware allocation pool. 324 */ 325 static int 326 ofw_mapin(vaddr_t rva, vsize_t len) 327 { 328 vaddr_t va; 329 330 len = roundup2(len + (rva & PAGE_MASK_4M), PAGE_SIZE_4M); 331 rva &= ~PAGE_MASK_4M; 332 333 if ( (len = kvamap_extract(rva, len, &va)) != 0) { 334 if (OF_claim((void *)(long)va, len, PAGE_SIZE_4M) == (void*)-1){ 335 panic("ofw_mapin: Cannot claim memory."); 336 } 337 kvamap_enter(va, len); 338 } 339 340 return (0); 341 } 342 343 static ssize_t 344 ofw_read(int f, void *addr, size_t size) 345 { 346 ofw_mapin((vaddr_t)addr, size); 347 return read(f, addr, size); 348 } 349 350 static void* 351 ofw_memcpy(void *dst, const void *src, size_t size) 352 { 353 ofw_mapin((vaddr_t)dst, size); 354 return memcpy(dst, src, size); 355 } 356 357 static void* 358 ofw_memset(void *dst, int c, size_t size) 359 { 360 ofw_mapin((vaddr_t)dst, size); 361 return memset(dst, c, size); 362 } 363 364 static void 365 ofw_freeall(void) 366 { 367 int i; 368 369 dtlb_slot = itlb_slot = 0; 370 for (i = 0; i < MAXSEGNUM; i++) { 371 OF_release((void*)(u_long)kvamap[i].start, 372 (u_int)(kvamap[i].end - kvamap[i].start)); 373 kvamap[i].start = kvamap[i].end = 0; 374 } 375 } 376 377 /* 378 * NOP implementation exists solely for kernel header loading sake. Here 379 * we use alloc() interface to allocate memory and avoid doing some dangerous 380 * things. 381 */ 382 static ssize_t 383 nop_read(int f, void *addr, size_t size) 384 { 385 return read(f, addr, size); 386 } 387 388 static void* 389 nop_memcpy(void *dst, const void *src, size_t size) 390 { 391 /* 392 * Real NOP to make LOAD_HDR work: loadfile_elfXX copies ELF headers 393 * right after the highest kernel address which will not be mapped with 394 * nop_XXX operations. 395 */ 396 return (dst); 397 } 398 399 static void* 400 nop_memset(void *dst, int c, size_t size) 401 { 402 return memset(dst, c, size); 403 } 404 405 static void 406 nop_freeall(void) 407 { } 408 409 /* 410 * loadfile() hooks. 411 */ 412 ssize_t 413 sparc64_read(int f, void *addr, size_t size) 414 { 415 return (*memsw->read)(f, addr, size); 416 } 417 418 void* 419 sparc64_memcpy(void *dst, const void *src, size_t size) 420 { 421 return (*memsw->memcpy)(dst, src, size); 422 } 423 424 void* 425 sparc64_memset(void *dst, int c, size_t size) 426 { 427 return (*memsw->memset)(dst, c, size); 428 } 429 430 /* 431 * Remove write permissions from text mappings in the dTLB. 432 * Add entries in the iTLB. 433 */ 434 void 435 sparc64_finalize_tlb(u_long data_va) 436 { 437 int i; 438 int64_t data; 439 bool writable_text = false; 440 441 for (i = 0; i < dtlb_slot; i++) { 442 if (dtlb_store[i].te_va >= data_va) { 443 /* 444 * If (for whatever reason) the start of the 445 * writable section is right at the start of 446 * the kernel, we need to map it into the ITLB 447 * nevertheless (and don't make it readonly). 448 */ 449 if (i == 0 && dtlb_store[i].te_va == data_va) 450 writable_text = true; 451 else 452 continue; 453 } 454 455 data = TSB_DATA(0, /* global */ 456 PGSZ_4M, /* 4mb page */ 457 dtlb_store[i].te_pa, /* phys.address */ 458 1, /* privileged */ 459 0, /* write */ 460 1, /* cache */ 461 1, /* alias */ 462 1, /* valid */ 463 0 /* endianness */ 464 ); 465 data |= TLB_L | TLB_CV; /* locked, virt.cache */ 466 if (!writable_text) 467 dtlb_replace(dtlb_store[i].te_va, hi(data), lo(data)); 468 itlb_store[itlb_slot] = dtlb_store[i]; 469 itlb_slot++; 470 itlb_enter(dtlb_store[i].te_va, hi(data), lo(data)); 471 } 472 if (writable_text) 473 printf("WARNING: kernel text mapped writable!\n"); 474 } 475 476 /* 477 * Record kernel mappings in bootinfo structure. 478 */ 479 void 480 sparc64_bi_add(void) 481 { 482 int i; 483 int itlb_size, dtlb_size; 484 struct btinfo_count bi_count; 485 struct btinfo_tlb *bi_itlb, *bi_dtlb; 486 487 bi_count.count = itlb_slot; 488 bi_add(&bi_count, BTINFO_ITLB_SLOTS, sizeof(bi_count)); 489 bi_count.count = dtlb_slot; 490 bi_add(&bi_count, BTINFO_DTLB_SLOTS, sizeof(bi_count)); 491 492 itlb_size = sizeof(*bi_itlb) + sizeof(struct tlb_entry) * itlb_slot; 493 dtlb_size = sizeof(*bi_dtlb) + sizeof(struct tlb_entry) * dtlb_slot; 494 495 bi_itlb = alloc(itlb_size); 496 bi_dtlb = alloc(dtlb_size); 497 498 if ((bi_itlb == NULL) || (bi_dtlb == NULL)) { 499 panic("Out of memory in sparc64_bi_add.\n"); 500 } 501 502 for (i = 0; i < itlb_slot; i++) { 503 bi_itlb->tlb[i].te_va = itlb_store[i].te_va; 504 bi_itlb->tlb[i].te_pa = itlb_store[i].te_pa; 505 } 506 bi_add(bi_itlb, BTINFO_ITLB, itlb_size); 507 508 for (i = 0; i < dtlb_slot; i++) { 509 bi_dtlb->tlb[i].te_va = dtlb_store[i].te_va; 510 bi_dtlb->tlb[i].te_pa = dtlb_store[i].te_pa; 511 } 512 bi_add(bi_dtlb, BTINFO_DTLB, dtlb_size); 513 } 514 515 /* 516 * Choose kernel image mapping strategy: 517 * 518 * LOADFILE_NOP_ALLOCATOR To load kernel image headers 519 * LOADFILE_OFW_ALLOCATOR To map the kernel by OpenFirmware means 520 * LOADFILE_MMU_ALLOCATOR To use permanent 4MB mappings 521 */ 522 void 523 loadfile_set_allocator(int type) 524 { 525 if (type >= (sizeof(memswa) / sizeof(struct memsw))) { 526 panic("Bad allocator request.\n"); 527 } 528 529 /* 530 * Release all memory claimed by previous allocator and schedule 531 * another allocator for succeeding memory allocation calls. 532 */ 533 (*memsw->freeall)(); 534 memsw = &memswa[type]; 535 } 536