1/* $NetBSD$ */ 2 3/*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 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 <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/pool.h> 38#include <sys/msgbuf.h> 39#include <sys/socketvar.h> /* XXX: for sock_loan_thresh */ 40 41#include <uvm/uvm.h> 42 43#include <sh3/mmu.h> 44#include <sh3/cache.h> 45 46#ifdef DEBUG 47#define STATIC 48#else 49#define STATIC static 50#endif 51 52#define __PMAP_PTP_SHIFT 22 53#define __PMAP_PTP_TRUNC(va) \ 54 (((va) + (1 << __PMAP_PTP_SHIFT) - 1) & ~((1 << __PMAP_PTP_SHIFT) - 1)) 55#define __PMAP_PTP_PG_N (PAGE_SIZE / sizeof(pt_entry_t)) 56#define __PMAP_PTP_INDEX(va) (((va) >> __PMAP_PTP_SHIFT) & (__PMAP_PTP_N - 1)) 57#define __PMAP_PTP_OFSET(va) ((va >> PGSHIFT) & (__PMAP_PTP_PG_N - 1)) 58 59struct pmap __pmap_kernel; 60struct pmap *const kernel_pmap_ptr = &__pmap_kernel; 61STATIC vaddr_t __pmap_kve; /* VA of last kernel virtual */ 62paddr_t avail_start; /* PA of first available physical page */ 63paddr_t avail_end; /* PA of last available physical page */ 64 65/* For the fast tlb miss handler */ 66pt_entry_t **curptd; /* p1 va of curlwp->...->pm_ptp */ 67 68/* pmap pool */ 69STATIC struct pool __pmap_pmap_pool; 70 71/* pv_entry ops. */ 72struct pv_entry { 73 struct pmap *pv_pmap; 74 vaddr_t pv_va; 75 SLIST_ENTRY(pv_entry) pv_link; 76}; 77#define __pmap_pv_alloc() pool_get(&__pmap_pv_pool, PR_NOWAIT) 78#define __pmap_pv_free(pv) pool_put(&__pmap_pv_pool, (pv)) 79STATIC void __pmap_pv_enter(pmap_t, struct vm_page *, vaddr_t); 80STATIC void __pmap_pv_remove(pmap_t, struct vm_page *, vaddr_t); 81STATIC void *__pmap_pv_page_alloc(struct pool *, int); 82STATIC void __pmap_pv_page_free(struct pool *, void *); 83STATIC struct pool __pmap_pv_pool; 84STATIC struct pool_allocator pmap_pv_page_allocator = { 85 __pmap_pv_page_alloc, __pmap_pv_page_free, 0, 86}; 87 88/* ASID ops. */ 89STATIC int __pmap_asid_alloc(void); 90STATIC void __pmap_asid_free(int); 91STATIC struct { 92 uint32_t map[8]; 93 int hint; /* hint for next allocation */ 94} __pmap_asid; 95 96/* page table entry ops. */ 97STATIC pt_entry_t *__pmap_pte_alloc(pmap_t, vaddr_t); 98 99/* pmap_enter util */ 100STATIC bool __pmap_map_change(pmap_t, vaddr_t, paddr_t, vm_prot_t, 101 pt_entry_t); 102 103void 104pmap_bootstrap(void) 105{ 106 107 /* Steal msgbuf area */ 108 initmsgbuf((void *)uvm_pageboot_alloc(MSGBUFSIZE), MSGBUFSIZE); 109 110 avail_start = ptoa(VM_PHYSMEM_PTR(0)->start); 111 avail_end = ptoa(VM_PHYSMEM_PTR(vm_nphysseg - 1)->end); 112 __pmap_kve = VM_MIN_KERNEL_ADDRESS; 113 114 pmap_kernel()->pm_refcnt = 1; 115 pmap_kernel()->pm_ptp = (pt_entry_t **)uvm_pageboot_alloc(PAGE_SIZE); 116 memset(pmap_kernel()->pm_ptp, 0, PAGE_SIZE); 117 118 /* Enable MMU */ 119 sh_mmu_start(); 120 /* Mask all interrupt */ 121 _cpu_intr_suspend(); 122 /* Enable exception for P3 access */ 123 _cpu_exception_resume(0); 124} 125 126vaddr_t 127pmap_steal_memory(vsize_t size, vaddr_t *vstart, vaddr_t *vend) 128{ 129 struct vm_physseg *bank; 130 int i, j, npage; 131 paddr_t pa; 132 vaddr_t va; 133 134 KDASSERT(!uvm.page_init_done); 135 136 size = round_page(size); 137 npage = atop(size); 138 139 bank = NULL; 140 for (i = 0; i < vm_nphysseg; i++) { 141 bank = VM_PHYSMEM_PTR(i); 142 if (npage <= bank->avail_end - bank->avail_start) 143 break; 144 } 145 KDASSERT(i != vm_nphysseg); 146 KDASSERT(bank != NULL); 147 148 /* Steal pages */ 149 pa = ptoa(bank->avail_start); 150 bank->avail_start += npage; 151 bank->start += npage; 152 153 /* GC memory bank */ 154 if (bank->avail_start == bank->end) { 155 /* Remove this segment from the list. */ 156 vm_nphysseg--; 157 KDASSERT(vm_nphysseg > 0); 158 for (j = i; i < vm_nphysseg; j++) 159 VM_PHYSMEM_PTR_SWAP(j, j + 1); 160 } 161 162 va = SH3_PHYS_TO_P1SEG(pa); 163 memset((void *)va, 0, size); 164 165 return (va); 166} 167 168vaddr_t 169pmap_growkernel(vaddr_t maxkvaddr) 170{ 171 int i, n; 172 173 if (maxkvaddr <= __pmap_kve) 174 return (__pmap_kve); 175 176 i = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS); 177 __pmap_kve = __PMAP_PTP_TRUNC(maxkvaddr); 178 n = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS); 179 180 /* Allocate page table pages */ 181 for (;i < n; i++) { 182 if (__pmap_kernel.pm_ptp[i] != NULL) 183 continue; 184 185 if (uvm.page_init_done) { 186 struct vm_page *pg = uvm_pagealloc(NULL, 0, NULL, 187 UVM_PGA_USERESERVE | UVM_PGA_ZERO); 188 if (pg == NULL) 189 goto error; 190 __pmap_kernel.pm_ptp[i] = (pt_entry_t *) 191 SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg)); 192 } else { 193 pt_entry_t *ptp = (pt_entry_t *) 194 uvm_pageboot_alloc(PAGE_SIZE); 195 if (ptp == NULL) 196 goto error; 197 __pmap_kernel.pm_ptp[i] = ptp; 198 memset(ptp, 0, PAGE_SIZE); 199 } 200 } 201 202 return (__pmap_kve); 203 error: 204 panic("pmap_growkernel: out of memory."); 205 /* NOTREACHED */ 206} 207 208void 209pmap_virtual_space(vaddr_t *start, vaddr_t *end) 210{ 211 212 *start = VM_MIN_KERNEL_ADDRESS; 213 *end = VM_MAX_KERNEL_ADDRESS; 214} 215 216void 217pmap_init(void) 218{ 219 220 /* Initialize pmap module */ 221 pool_init(&__pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl", 222 &pool_allocator_nointr, IPL_NONE); 223 pool_init(&__pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl", 224 &pmap_pv_page_allocator, IPL_NONE); 225 pool_setlowat(&__pmap_pv_pool, 16); 226 227#ifdef SH4 228 if (SH_HAS_VIRTUAL_ALIAS) { 229 /* 230 * XXX 231 * Disable sosend_loan() in src/sys/kern/uipc_socket.c 232 * on SH4 to avoid possible virtual cache aliases and 233 * unnecessary map/unmap thrashing in __pmap_pv_enter(). 234 * (also see comments in __pmap_pv_enter()) 235 * 236 * Ideally, read only shared mapping won't cause aliases 237 * so __pmap_pv_enter() should handle any shared read only 238 * mappings like ARM pmap. 239 */ 240 sock_loan_thresh = -1; 241 } 242#endif 243} 244 245pmap_t 246pmap_create(void) 247{ 248 pmap_t pmap; 249 250 pmap = pool_get(&__pmap_pmap_pool, PR_WAITOK); 251 memset(pmap, 0, sizeof(struct pmap)); 252 pmap->pm_asid = -1; 253 pmap->pm_refcnt = 1; 254 /* Allocate page table page holder (512 slot) */ 255 pmap->pm_ptp = (pt_entry_t **) 256 SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS( 257 uvm_pagealloc(NULL, 0, NULL, 258 UVM_PGA_USERESERVE | UVM_PGA_ZERO))); 259 260 return (pmap); 261} 262 263void 264pmap_destroy(pmap_t pmap) 265{ 266 int i; 267 268 if (--pmap->pm_refcnt > 0) 269 return; 270 271 /* Deallocate all page table page */ 272 for (i = 0; i < __PMAP_PTP_N; i++) { 273 vaddr_t va = (vaddr_t)pmap->pm_ptp[i]; 274 if (va == 0) 275 continue; 276#ifdef DEBUG /* Check no mapping exists. */ 277 { 278 int j; 279 pt_entry_t *pte = (pt_entry_t *)va; 280 for (j = 0; j < __PMAP_PTP_PG_N; j++, pte++) 281 KDASSERT(*pte == 0); 282 } 283#endif /* DEBUG */ 284 /* Purge cache entry for next use of this page. */ 285 if (SH_HAS_VIRTUAL_ALIAS) 286 sh_dcache_inv_range(va, PAGE_SIZE); 287 /* Free page table */ 288 uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va))); 289 } 290 /* Deallocate page table page holder */ 291 if (SH_HAS_VIRTUAL_ALIAS) 292 sh_dcache_inv_range((vaddr_t)pmap->pm_ptp, PAGE_SIZE); 293 uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS((vaddr_t)pmap->pm_ptp))); 294 295 /* Free ASID */ 296 __pmap_asid_free(pmap->pm_asid); 297 298 pool_put(&__pmap_pmap_pool, pmap); 299} 300 301void 302pmap_reference(pmap_t pmap) 303{ 304 305 pmap->pm_refcnt++; 306} 307 308void 309pmap_activate(struct lwp *l) 310{ 311 pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap; 312 313 if (pmap->pm_asid == -1) 314 pmap->pm_asid = __pmap_asid_alloc(); 315 316 KDASSERT(pmap->pm_asid >=0 && pmap->pm_asid < 256); 317 318 sh_tlb_set_asid(pmap->pm_asid); 319 curptd = pmap->pm_ptp; 320} 321 322void 323pmap_deactivate(struct lwp *l) 324{ 325 326 /* Nothing to do */ 327} 328 329int 330pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags) 331{ 332 struct vm_page *pg; 333 struct vm_page_md *pvh; 334 pt_entry_t entry, *pte; 335 bool kva = (pmap == pmap_kernel()); 336 337 /* "flags" never exceed "prot" */ 338 KDASSERT(prot != 0 && ((flags & VM_PROT_ALL) & ~prot) == 0); 339 340 pg = PHYS_TO_VM_PAGE(pa); 341 entry = (pa & PG_PPN) | PG_4K; 342 if (flags & PMAP_WIRED) 343 entry |= _PG_WIRED; 344 345 if (pg != NULL) { /* memory-space */ 346 pvh = VM_PAGE_TO_MD(pg); 347 entry |= PG_C; /* always cached */ 348 349 /* Seed modified/reference tracking */ 350 if (flags & VM_PROT_WRITE) { 351 entry |= PG_V | PG_D; 352 pvh->pvh_flags |= PVH_MODIFIED | PVH_REFERENCED; 353 } else if (flags & VM_PROT_ALL) { 354 entry |= PG_V; 355 pvh->pvh_flags |= PVH_REFERENCED; 356 } 357 358 /* Protection */ 359 if ((prot & VM_PROT_WRITE) && (pvh->pvh_flags & PVH_MODIFIED)) { 360 if (kva) 361 entry |= PG_PR_KRW | PG_SH; 362 else 363 entry |= PG_PR_URW; 364 } else { 365 /* RO or COW page */ 366 if (kva) 367 entry |= PG_PR_KRO | PG_SH; 368 else 369 entry |= PG_PR_URO; 370 } 371 372 /* Check for existing mapping */ 373 if (__pmap_map_change(pmap, va, pa, prot, entry)) 374 return (0); 375 376 /* Add to physical-virtual map list of this page */ 377 __pmap_pv_enter(pmap, pg, va); 378 379 } else { /* bus-space (always uncached map) */ 380 if (kva) { 381 entry |= PG_V | PG_SH | 382 ((prot & VM_PROT_WRITE) ? 383 (PG_PR_KRW | PG_D) : PG_PR_KRO); 384 } else { 385 entry |= PG_V | 386 ((prot & VM_PROT_WRITE) ? 387 (PG_PR_URW | PG_D) : PG_PR_URO); 388 } 389 } 390 391 /* Register to page table */ 392 if (kva) 393 pte = __pmap_kpte_lookup(va); 394 else { 395 pte = __pmap_pte_alloc(pmap, va); 396 if (pte == NULL) { 397 if (flags & PMAP_CANFAIL) 398 return ENOMEM; 399 panic("pmap_enter: cannot allocate pte"); 400 } 401 } 402 403 *pte = entry; 404 405 if (pmap->pm_asid != -1) 406 sh_tlb_update(pmap->pm_asid, va, entry); 407 408 if (!SH_HAS_UNIFIED_CACHE && 409 (prot == (VM_PROT_READ | VM_PROT_EXECUTE))) 410 sh_icache_sync_range_index(va, PAGE_SIZE); 411 412 if (entry & _PG_WIRED) 413 pmap->pm_stats.wired_count++; 414 pmap->pm_stats.resident_count++; 415 416 return (0); 417} 418 419/* 420 * bool __pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa, 421 * vm_prot_t prot, pt_entry_t entry): 422 * Handle the situation that pmap_enter() is called to enter a 423 * mapping at a virtual address for which a mapping already 424 * exists. 425 */ 426bool 427__pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, 428 pt_entry_t entry) 429{ 430 pt_entry_t *pte, oentry; 431 vaddr_t eva = va + PAGE_SIZE; 432 433 if ((pte = __pmap_pte_lookup(pmap, va)) == NULL || 434 ((oentry = *pte) == 0)) 435 return (false); /* no mapping exists. */ 436 437 if (pa != (oentry & PG_PPN)) { 438 /* Enter a mapping at a mapping to another physical page. */ 439 pmap_remove(pmap, va, eva); 440 return (false); 441 } 442 443 /* Pre-existing mapping */ 444 445 /* Protection change. */ 446 if ((oentry & PG_PR_MASK) != (entry & PG_PR_MASK)) 447 pmap_protect(pmap, va, eva, prot); 448 449 /* Wired change */ 450 if (oentry & _PG_WIRED) { 451 if (!(entry & _PG_WIRED)) { 452 /* wired -> unwired */ 453 *pte = entry; 454 /* "wired" is software bits. no need to update TLB */ 455 pmap->pm_stats.wired_count--; 456 } 457 } else if (entry & _PG_WIRED) { 458 /* unwired -> wired. make sure to reflect "flags" */ 459 pmap_remove(pmap, va, eva); 460 return (false); 461 } 462 463 return (true); /* mapping was changed. */ 464} 465 466/* 467 * void __pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr): 468 * Insert physical-virtual map to vm_page. 469 * Assume pre-existed mapping is already removed. 470 */ 471void 472__pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va) 473{ 474 struct vm_page_md *pvh; 475 struct pv_entry *pv; 476 int s; 477 478 s = splvm(); 479 if (SH_HAS_VIRTUAL_ALIAS) { 480 /* 481 * Remove all other mappings on this physical page 482 * which have different virtual cache indexes to 483 * avoid virtual cache aliases. 484 * 485 * XXX We should also handle shared mappings which 486 * XXX have different virtual cache indexes by 487 * XXX mapping them uncached (like arm and mips do). 488 */ 489 again: 490 pvh = VM_PAGE_TO_MD(pg); 491 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { 492 if (sh_cache_indexof(va) != 493 sh_cache_indexof(pv->pv_va)) { 494 pmap_remove(pv->pv_pmap, pv->pv_va, 495 pv->pv_va + PAGE_SIZE); 496 goto again; 497 } 498 } 499 } 500 501 /* Register pv map */ 502 pvh = VM_PAGE_TO_MD(pg); 503 pv = __pmap_pv_alloc(); 504 pv->pv_pmap = pmap; 505 pv->pv_va = va; 506 507 SLIST_INSERT_HEAD(&pvh->pvh_head, pv, pv_link); 508 splx(s); 509} 510 511void 512pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva) 513{ 514 struct vm_page *pg; 515 pt_entry_t *pte, entry; 516 vaddr_t va; 517 518 KDASSERT((sva & PGOFSET) == 0); 519 520 for (va = sva; va < eva; va += PAGE_SIZE) { 521 if ((pte = __pmap_pte_lookup(pmap, va)) == NULL || 522 (entry = *pte) == 0) 523 continue; 524 525 if ((pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL) 526 __pmap_pv_remove(pmap, pg, va); 527 528 if (entry & _PG_WIRED) 529 pmap->pm_stats.wired_count--; 530 pmap->pm_stats.resident_count--; 531 *pte = 0; 532 533 /* 534 * When pmap->pm_asid == -1 (invalid ASID), old entry attribute 535 * to this pmap is already removed by pmap_activate(). 536 */ 537 if (pmap->pm_asid != -1) 538 sh_tlb_invalidate_addr(pmap->pm_asid, va); 539 } 540} 541 542/* 543 * void __pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr): 544 * Remove physical-virtual map from vm_page. 545 */ 546void 547__pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr) 548{ 549 struct vm_page_md *pvh; 550 struct pv_entry *pv; 551 int s; 552 553 s = splvm(); 554 pvh = VM_PAGE_TO_MD(pg); 555 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { 556 if (pv->pv_pmap == pmap && pv->pv_va == vaddr) { 557 if (SH_HAS_VIRTUAL_ALIAS || 558 (SH_HAS_WRITEBACK_CACHE && 559 (pvh->pvh_flags & PVH_MODIFIED))) { 560 /* 561 * Always use index ops. since I don't want to 562 * worry about address space. 563 */ 564 sh_dcache_wbinv_range_index 565 (pv->pv_va, PAGE_SIZE); 566 } 567 568 SLIST_REMOVE(&pvh->pvh_head, pv, pv_entry, pv_link); 569 __pmap_pv_free(pv); 570 break; 571 } 572 } 573#ifdef DEBUG 574 /* Check duplicated map. */ 575 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) 576 KDASSERT(!(pv->pv_pmap == pmap && pv->pv_va == vaddr)); 577#endif 578 splx(s); 579} 580 581void 582pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags) 583{ 584 pt_entry_t *pte, entry; 585 586 KDASSERT((va & PGOFSET) == 0); 587 KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS); 588 589 entry = (pa & PG_PPN) | PG_V | PG_SH | PG_4K; 590 if (prot & VM_PROT_WRITE) 591 entry |= (PG_PR_KRW | PG_D); 592 else 593 entry |= PG_PR_KRO; 594 595 if (PHYS_TO_VM_PAGE(pa)) 596 entry |= PG_C; 597 598 pte = __pmap_kpte_lookup(va); 599 600 KDASSERT(*pte == 0); 601 *pte = entry; 602 603 sh_tlb_update(0, va, entry); 604} 605 606void 607pmap_kremove(vaddr_t va, vsize_t len) 608{ 609 pt_entry_t *pte; 610 vaddr_t eva = va + len; 611 612 KDASSERT((va & PGOFSET) == 0); 613 KDASSERT((len & PGOFSET) == 0); 614 KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && eva <= VM_MAX_KERNEL_ADDRESS); 615 616 for (; va < eva; va += PAGE_SIZE) { 617 pte = __pmap_kpte_lookup(va); 618 KDASSERT(pte != NULL); 619 if (*pte == 0) 620 continue; 621 622 if (SH_HAS_VIRTUAL_ALIAS && PHYS_TO_VM_PAGE(*pte & PG_PPN)) 623 sh_dcache_wbinv_range(va, PAGE_SIZE); 624 *pte = 0; 625 626 sh_tlb_invalidate_addr(0, va); 627 } 628} 629 630bool 631pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap) 632{ 633 pt_entry_t *pte; 634 635 /* handle P1 and P2 specially: va == pa */ 636 if (pmap == pmap_kernel() && (va >> 30) == 2) { 637 if (pap != NULL) 638 *pap = va & SH3_PHYS_MASK; 639 return (true); 640 } 641 642 pte = __pmap_pte_lookup(pmap, va); 643 if (pte == NULL || *pte == 0) 644 return (false); 645 646 if (pap != NULL) 647 *pap = (*pte & PG_PPN) | (va & PGOFSET); 648 649 return (true); 650} 651 652void 653pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot) 654{ 655 bool kernel = pmap == pmap_kernel(); 656 pt_entry_t *pte, entry, protbits; 657 vaddr_t va; 658 659 sva = trunc_page(sva); 660 661 if ((prot & VM_PROT_READ) == VM_PROT_NONE) { 662 pmap_remove(pmap, sva, eva); 663 return; 664 } 665 666 switch (prot) { 667 default: 668 panic("pmap_protect: invalid protection mode %x", prot); 669 /* NOTREACHED */ 670 case VM_PROT_READ: 671 /* FALLTHROUGH */ 672 case VM_PROT_READ | VM_PROT_EXECUTE: 673 protbits = kernel ? PG_PR_KRO : PG_PR_URO; 674 break; 675 case VM_PROT_READ | VM_PROT_WRITE: 676 /* FALLTHROUGH */ 677 case VM_PROT_ALL: 678 protbits = kernel ? PG_PR_KRW : PG_PR_URW; 679 break; 680 } 681 682 for (va = sva; va < eva; va += PAGE_SIZE) { 683 684 if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) || 685 (entry = *pte) == 0) 686 continue; 687 688 if (SH_HAS_VIRTUAL_ALIAS && (entry & PG_D)) { 689 if (!SH_HAS_UNIFIED_CACHE && (prot & VM_PROT_EXECUTE)) 690 sh_icache_sync_range_index(va, PAGE_SIZE); 691 else 692 sh_dcache_wbinv_range_index(va, PAGE_SIZE); 693 } 694 695 entry = (entry & ~PG_PR_MASK) | protbits; 696 *pte = entry; 697 698 if (pmap->pm_asid != -1) 699 sh_tlb_update(pmap->pm_asid, va, entry); 700 } 701} 702 703void 704pmap_page_protect(struct vm_page *pg, vm_prot_t prot) 705{ 706 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 707 struct pv_entry *pv; 708 struct pmap *pmap; 709 vaddr_t va; 710 int s; 711 712 switch (prot) { 713 case VM_PROT_READ | VM_PROT_WRITE: 714 /* FALLTHROUGH */ 715 case VM_PROT_ALL: 716 break; 717 718 case VM_PROT_READ: 719 /* FALLTHROUGH */ 720 case VM_PROT_READ | VM_PROT_EXECUTE: 721 s = splvm(); 722 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { 723 pmap = pv->pv_pmap; 724 va = pv->pv_va; 725 726 KDASSERT(pmap); 727 pmap_protect(pmap, va, va + PAGE_SIZE, prot); 728 } 729 splx(s); 730 break; 731 732 default: 733 /* Remove all */ 734 s = splvm(); 735 while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) { 736 va = pv->pv_va; 737 pmap_remove(pv->pv_pmap, va, va + PAGE_SIZE); 738 } 739 splx(s); 740 } 741} 742 743void 744pmap_unwire(pmap_t pmap, vaddr_t va) 745{ 746 pt_entry_t *pte, entry; 747 748 if ((pte = __pmap_pte_lookup(pmap, va)) == NULL || 749 (entry = *pte) == 0 || 750 (entry & _PG_WIRED) == 0) 751 return; 752 753 *pte = entry & ~_PG_WIRED; 754 pmap->pm_stats.wired_count--; 755} 756 757void 758pmap_procwr(struct proc *p, vaddr_t va, size_t len) 759{ 760 761 if (!SH_HAS_UNIFIED_CACHE) 762 sh_icache_sync_range_index(va, len); 763} 764 765void 766pmap_zero_page(paddr_t phys) 767{ 768 769 if (SH_HAS_VIRTUAL_ALIAS) { /* don't polute cache */ 770 /* sync cache since we access via P2. */ 771 sh_dcache_wbinv_all(); 772 memset((void *)SH3_PHYS_TO_P2SEG(phys), 0, PAGE_SIZE); 773 } else { 774 memset((void *)SH3_PHYS_TO_P1SEG(phys), 0, PAGE_SIZE); 775 } 776} 777 778void 779pmap_copy_page(paddr_t src, paddr_t dst) 780{ 781 782 if (SH_HAS_VIRTUAL_ALIAS) { /* don't polute cache */ 783 /* sync cache since we access via P2. */ 784 sh_dcache_wbinv_all(); 785 memcpy((void *)SH3_PHYS_TO_P2SEG(dst), 786 (void *)SH3_PHYS_TO_P2SEG(src), PAGE_SIZE); 787 } else { 788 memcpy((void *)SH3_PHYS_TO_P1SEG(dst), 789 (void *)SH3_PHYS_TO_P1SEG(src), PAGE_SIZE); 790 } 791} 792 793bool 794pmap_is_referenced(struct vm_page *pg) 795{ 796 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 797 798 return ((pvh->pvh_flags & PVH_REFERENCED) ? true : false); 799} 800 801bool 802pmap_clear_reference(struct vm_page *pg) 803{ 804 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 805 struct pv_entry *pv; 806 pt_entry_t *pte; 807 pmap_t pmap; 808 vaddr_t va; 809 int s; 810 811 if ((pvh->pvh_flags & PVH_REFERENCED) == 0) 812 return (false); 813 814 pvh->pvh_flags &= ~PVH_REFERENCED; 815 816 s = splvm(); 817 /* Restart reference bit emulation */ 818 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { 819 pmap = pv->pv_pmap; 820 va = pv->pv_va; 821 822 if ((pte = __pmap_pte_lookup(pmap, va)) == NULL) 823 continue; 824 if ((*pte & PG_V) == 0) 825 continue; 826 *pte &= ~PG_V; 827 828 if (pmap->pm_asid != -1) 829 sh_tlb_invalidate_addr(pmap->pm_asid, va); 830 } 831 splx(s); 832 833 return (true); 834} 835 836bool 837pmap_is_modified(struct vm_page *pg) 838{ 839 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 840 841 return ((pvh->pvh_flags & PVH_MODIFIED) ? true : false); 842} 843 844bool 845pmap_clear_modify(struct vm_page *pg) 846{ 847 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 848 struct pv_entry *pv; 849 struct pmap *pmap; 850 pt_entry_t *pte, entry; 851 bool modified; 852 vaddr_t va; 853 int s; 854 855 modified = pvh->pvh_flags & PVH_MODIFIED; 856 if (!modified) 857 return (false); 858 859 pvh->pvh_flags &= ~PVH_MODIFIED; 860 861 s = splvm(); 862 if (SLIST_EMPTY(&pvh->pvh_head)) {/* no map on this page */ 863 splx(s); 864 return (true); 865 } 866 867 /* Write-back and invalidate TLB entry */ 868 if (!SH_HAS_VIRTUAL_ALIAS && SH_HAS_WRITEBACK_CACHE) 869 sh_dcache_wbinv_all(); 870 871 SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { 872 pmap = pv->pv_pmap; 873 va = pv->pv_va; 874 if ((pte = __pmap_pte_lookup(pmap, va)) == NULL) 875 continue; 876 entry = *pte; 877 if ((entry & PG_D) == 0) 878 continue; 879 880 if (SH_HAS_VIRTUAL_ALIAS) 881 sh_dcache_wbinv_range_index(va, PAGE_SIZE); 882 883 *pte = entry & ~PG_D; 884 if (pmap->pm_asid != -1) 885 sh_tlb_invalidate_addr(pmap->pm_asid, va); 886 } 887 splx(s); 888 889 return (true); 890} 891 892paddr_t 893pmap_phys_address(paddr_t cookie) 894{ 895 896 return (sh3_ptob(cookie)); 897} 898 899#ifdef SH4 900/* 901 * pmap_prefer(vaddr_t foff, vaddr_t *vap) 902 * 903 * Find first virtual address >= *vap that doesn't cause 904 * a virtual cache alias against vaddr_t foff. 905 */ 906void 907pmap_prefer(vaddr_t foff, vaddr_t *vap) 908{ 909 vaddr_t va; 910 911 if (SH_HAS_VIRTUAL_ALIAS) { 912 va = *vap; 913 914 *vap = va + ((foff - va) & sh_cache_prefer_mask); 915 } 916} 917#endif /* SH4 */ 918 919/* 920 * pv_entry pool allocator: 921 * void *__pmap_pv_page_alloc(struct pool *pool, int flags): 922 * void __pmap_pv_page_free(struct pool *pool, void *v): 923 */ 924void * 925__pmap_pv_page_alloc(struct pool *pool, int flags) 926{ 927 struct vm_page *pg; 928 929 pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE); 930 if (pg == NULL) 931 return (NULL); 932 933 return ((void *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg))); 934} 935 936void 937__pmap_pv_page_free(struct pool *pool, void *v) 938{ 939 vaddr_t va = (vaddr_t)v; 940 941 /* Invalidate cache for next use of this page */ 942 if (SH_HAS_VIRTUAL_ALIAS) 943 sh_icache_sync_range_index(va, PAGE_SIZE); 944 uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va))); 945} 946 947/* 948 * pt_entry_t __pmap_pte_alloc(pmap_t pmap, vaddr_t va): 949 * lookup page table entry. if found returns it, else allocate it. 950 * page table is accessed via P1. 951 */ 952pt_entry_t * 953__pmap_pte_alloc(pmap_t pmap, vaddr_t va) 954{ 955 struct vm_page *pg; 956 pt_entry_t *ptp, *pte; 957 958 if ((pte = __pmap_pte_lookup(pmap, va)) != NULL) 959 return (pte); 960 961 /* Allocate page table (not managed page) */ 962 pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE | UVM_PGA_ZERO); 963 if (pg == NULL) 964 return NULL; 965 966 ptp = (pt_entry_t *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg)); 967 pmap->pm_ptp[__PMAP_PTP_INDEX(va)] = ptp; 968 969 return (ptp + __PMAP_PTP_OFSET(va)); 970} 971 972/* 973 * pt_entry_t *__pmap_pte_lookup(pmap_t pmap, vaddr_t va): 974 * lookup page table entry, if not allocated, returns NULL. 975 */ 976pt_entry_t * 977__pmap_pte_lookup(pmap_t pmap, vaddr_t va) 978{ 979 pt_entry_t *ptp; 980 981 if (pmap == pmap_kernel()) 982 return (__pmap_kpte_lookup(va)); 983 984 /* Lookup page table page */ 985 ptp = pmap->pm_ptp[__PMAP_PTP_INDEX(va)]; 986 if (ptp == NULL) 987 return (NULL); 988 989 return (ptp + __PMAP_PTP_OFSET(va)); 990} 991 992/* 993 * pt_entry_t *__pmap_kpte_lookup(vaddr_t va): 994 * kernel virtual only version of __pmap_pte_lookup(). 995 */ 996pt_entry_t * 997__pmap_kpte_lookup(vaddr_t va) 998{ 999 pt_entry_t *ptp; 1000 1001 ptp = __pmap_kernel.pm_ptp[__PMAP_PTP_INDEX(va-VM_MIN_KERNEL_ADDRESS)]; 1002 if (ptp == NULL) 1003 return NULL; 1004 1005 return (ptp + __PMAP_PTP_OFSET(va)); 1006} 1007 1008/* 1009 * bool __pmap_pte_load(pmap_t pmap, vaddr_t va, int flags): 1010 * lookup page table entry, if found it, load to TLB. 1011 * flags specify do emulate reference and/or modified bit or not. 1012 */ 1013bool 1014__pmap_pte_load(pmap_t pmap, vaddr_t va, int flags) 1015{ 1016 struct vm_page *pg; 1017 pt_entry_t *pte; 1018 pt_entry_t entry; 1019 1020 KDASSERT((((int)va < 0) && (pmap == pmap_kernel())) || 1021 (((int)va >= 0) && (pmap != pmap_kernel()))); 1022 1023 /* Lookup page table entry */ 1024 if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) || 1025 ((entry = *pte) == 0)) 1026 return (false); 1027 1028 KDASSERT(va != 0); 1029 1030 /* Emulate reference/modified tracking for managed page. */ 1031 if (flags != 0 && (pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL) { 1032 struct vm_page_md *pvh = VM_PAGE_TO_MD(pg); 1033 1034 if (flags & PVH_REFERENCED) { 1035 pvh->pvh_flags |= PVH_REFERENCED; 1036 entry |= PG_V; 1037 } 1038 if (flags & PVH_MODIFIED) { 1039 pvh->pvh_flags |= PVH_MODIFIED; 1040 entry |= PG_D; 1041 } 1042 *pte = entry; 1043 } 1044 1045 /* When pmap has valid ASID, register to TLB */ 1046 if (pmap->pm_asid != -1) 1047 sh_tlb_update(pmap->pm_asid, va, entry); 1048 1049 return (true); 1050} 1051 1052/* 1053 * int __pmap_asid_alloc(void): 1054 * Allocate new ASID. if all ASID is used, steal from other process. 1055 */ 1056int 1057__pmap_asid_alloc(void) 1058{ 1059 struct proc *p; 1060 int i, j, k, n, map, asid; 1061 1062 /* Search free ASID */ 1063 i = __pmap_asid.hint >> 5; 1064 n = i + 8; 1065 for (; i < n; i++) { 1066 k = i & 0x7; 1067 map = __pmap_asid.map[k]; 1068 for (j = 0; j < 32; j++) { 1069 if ((map & (1 << j)) == 0 && (k + j) != 0) { 1070 __pmap_asid.map[k] |= (1 << j); 1071 __pmap_asid.hint = (k << 5) + j; 1072 return (__pmap_asid.hint); 1073 } 1074 } 1075 } 1076 1077 /* Steal ASID */ 1078 LIST_FOREACH(p, &allproc, p_list) { 1079 if ((asid = p->p_vmspace->vm_map.pmap->pm_asid) > 0) { 1080 pmap_t pmap = p->p_vmspace->vm_map.pmap; 1081 pmap->pm_asid = -1; 1082 __pmap_asid.hint = asid; 1083 /* Invalidate all old ASID entry */ 1084 sh_tlb_invalidate_asid(pmap->pm_asid); 1085 1086 return (__pmap_asid.hint); 1087 } 1088 } 1089 1090 panic("No ASID allocated."); 1091 /* NOTREACHED */ 1092} 1093 1094/* 1095 * void __pmap_asid_free(int asid): 1096 * Return unused ASID to pool. and remove all TLB entry of ASID. 1097 */ 1098void 1099__pmap_asid_free(int asid) 1100{ 1101 int i; 1102 1103 if (asid < 1) /* Don't invalidate kernel ASID 0 */ 1104 return; 1105 1106 sh_tlb_invalidate_asid(asid); 1107 1108 i = asid >> 5; 1109 __pmap_asid.map[i] &= ~(1 << (asid - (i << 5))); 1110} 1111