1 /* $NetBSD: pmap_machdep.c,v 1.18 2026/06/13 12:12:47 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2022 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 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 "opt_arm_debug.h" 33 #include "opt_efi.h" 34 #include "opt_multiprocessor.h" 35 #include "opt_uvmhist.h" 36 37 #define __PMAP_PRIVATE 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: pmap_machdep.c,v 1.18 2026/06/13 12:12:47 skrll Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/types.h> 44 45 #include <sys/buf.h> 46 #include <sys/cpu.h> 47 #include <sys/kernel.h> 48 49 #include <uvm/uvm.h> 50 #include <uvm/uvm_page.h> 51 #include <uvm/pmap/pmap_pvt.h> 52 53 #include <aarch64/cpufunc.h> 54 55 #include <arm/locore.h> 56 57 #ifdef VERBOSE_INIT_ARM 58 #define VPRINTF(...) printf(__VA_ARGS__) 59 #else 60 #define VPRINTF(...) __nothing 61 #endif 62 63 /* Set to LX_BLKPAG_GP if supported. */ 64 uint64_t pmap_attr_gp = 0; 65 66 /* 67 * Misc variables 68 */ 69 vaddr_t virtual_avail; 70 vaddr_t virtual_end; 71 72 PMAP_COUNTER(fixup_referenced, "page reference emulations"); 73 PMAP_COUNTER(fixup_modified, "page modification emulations"); 74 75 paddr_t 76 vtophys(vaddr_t va) 77 { 78 paddr_t pa; 79 80 if (pmap_extract(pmap_kernel(), va, &pa) == false) 81 return 0; 82 return pa; 83 } 84 85 bool 86 pmap_extract_coherency(pmap_t pm, vaddr_t va, paddr_t *pap, bool *coherentp) 87 { 88 paddr_t pa; 89 bool coherency = false; 90 91 if (pm == pmap_kernel()) { 92 if (pmap_md_direct_mapped_vaddr_p(va)) { 93 pa = pmap_md_direct_mapped_vaddr_to_paddr(va); 94 goto done; 95 } 96 if (pmap_md_io_vaddr_p(va)) 97 panic("pmap_extract: io address %#"PRIxVADDR"", va); 98 99 if (va >= pmap_limits.virtual_end) 100 panic("%s: illegal kernel mapped address %#"PRIxVADDR, 101 __func__, va); 102 } 103 104 kpreempt_disable(); 105 const pt_entry_t * const ptep = pmap_pte_lookup(pm, va); 106 pt_entry_t pte; 107 108 if (ptep == NULL || !pte_valid_p(pte = *ptep)) { 109 kpreempt_enable(); 110 return false; 111 } 112 kpreempt_enable(); 113 114 pa = pte_to_paddr(pte) | (va & PGOFSET); 115 116 switch (pte & LX_BLKPAG_ATTR_MASK) { 117 case LX_BLKPAG_ATTR_NORMAL_NC: 118 case LX_BLKPAG_ATTR_DEVICE_MEM: 119 case LX_BLKPAG_ATTR_DEVICE_MEM_NP: 120 coherency = true; 121 break; 122 } 123 124 done: 125 if (pap != NULL) { 126 *pap = pa; 127 } 128 if (coherentp != NULL) { 129 *coherentp = coherency; 130 } 131 return true; 132 } 133 134 bool 135 pmap_fault_fixup(pmap_t pm, vaddr_t va, vm_prot_t ftype, bool user) 136 { 137 UVMHIST_FUNC(__func__); UVMHIST_CALLED(pmaphist); 138 139 KASSERT(!user || (pm != pmap_kernel())); 140 141 kpreempt_disable(); 142 143 // struct cpu_info * const ci = curcpu(); 144 struct pmap_asid_info * const pai = PMAP_PAI(pm, cpu_tlb_info(ci)); 145 146 UVMHIST_LOG(pmaphist, " pm=%#jx, va=%#jx, ftype=%#jx, user=%jd", 147 (uintptr_t)pm, va, ftype, user); 148 UVMHIST_LOG(pmaphist, " ti=%#jx pai=%#jx asid=%#jx", 149 (uintptr_t)cpu_tlb_info(curcpu()), 150 (uintptr_t)pai, (uintptr_t)pai->pai_asid, 0); 151 152 bool fixed = false; 153 pt_entry_t * const ptep = pmap_pte_lookup(pm, va); 154 if (ptep == NULL) { 155 UVMHIST_LOG(pmaphist, "... no ptep", 0, 0, 0, 0); 156 goto done; 157 } 158 159 const pt_entry_t opte = atomic_load_relaxed(ptep); 160 if (!l3pte_valid(opte)) { 161 UVMHIST_LOG(pmaphist, "invalid pte: %016llx: va=%016lx", 162 opte, va, 0, 0); 163 goto done; 164 } 165 166 const paddr_t pa = l3pte_pa(opte); 167 struct vm_page * const pg = PHYS_TO_VM_PAGE(pa); 168 if (pg == NULL) { 169 UVMHIST_LOG(pmaphist, "pg not found: va=%016lx", va, 0, 0, 0); 170 goto done; 171 } 172 173 struct vm_page_md * const mdpg = VM_PAGE_TO_MD(pg); 174 UVMHIST_LOG(pmaphist, " pg=%#jx, opte=%#jx, ptep=%#jx", (uintptr_t)pg, 175 opte, (uintptr_t)ptep, 0); 176 177 if ((ftype & VM_PROT_WRITE) && (opte & LX_BLKPAG_OS_MODEMUL) != 0) { 178 /* 179 * This is "page modified" emulation... 180 */ 181 pmap_page_set_attributes(mdpg, VM_PAGEMD_MODIFIED | VM_PAGEMD_REFERENCED); 182 183 /* 184 * Enable write permissions for the page by setting the Access 185 * Flag, marking the page as writeable. The PTE being writeable 186 * and OS_MODEMUL indicates it's been modified. 187 */ 188 const pt_entry_t npte = 189 (opte & ~LX_BLKPAG_AP) | 190 LX_BLKPAG_AF | 191 LX_BLKPAG_AP_RW; 192 atomic_swap_64(ptep, npte); 193 // tlb_invalidate_addr does dsb(ishst) 194 tlb_invalidate_addr(va, pai->pai_asid); 195 fixed = true; 196 197 PMAP_COUNT(fixup_modified); 198 199 UVMHIST_LOG(pmaphist, " <-- done (mod emul: changed pte " 200 "from %#jx to %#jx)", opte, npte, 0, 0); 201 } else if ((ftype & VM_PROT_READ) && (opte & LX_BLKPAG_AP) == LX_BLKPAG_AP_RO) { 202 /* 203 * This looks like a good candidate for "page referenced" 204 * emulation. 205 */ 206 207 KASSERT((opte & LX_BLKPAG_AF) == 0); 208 209 pmap_page_set_attributes(mdpg, VM_PAGEMD_REFERENCED); 210 211 /* 212 * Enable write permissions for the page by setting the Access Flag. 213 */ 214 const pt_entry_t npte = opte | LX_BLKPAG_AF; 215 atomic_swap_64(ptep, npte); 216 // tlb_invalidate_addr does dsb(ishst) 217 tlb_invalidate_addr(va, pai->pai_asid); 218 fixed = true; 219 220 PMAP_COUNT(fixup_referenced); 221 222 UVMHIST_LOG(pmaphist, " <-- done (ref emul: changed pte " 223 "from %#jx to %#jx)", opte, npte, 0, 0); 224 } 225 226 done: 227 kpreempt_enable(); 228 229 return fixed; 230 } 231 232 233 void 234 pmap_icache_sync_range(pmap_t pm, vaddr_t sva, vaddr_t eva) 235 { 236 UVMHIST_FUNC(__func__); 237 UVMHIST_CALLARGS(pmaphist, "pm %#jx sva %#jx eva %#jx", 238 (uintptr_t)pm, sva, eva, 0); 239 240 KASSERT((sva & PAGE_MASK) == 0); 241 KASSERT((eva & PAGE_MASK) == 0); 242 243 pmap_lock(pm); 244 245 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) { 246 pt_entry_t * const ptep = pmap_pte_lookup(pm, va); 247 if (ptep == NULL) 248 continue; 249 250 pt_entry_t opte = *ptep; 251 if (!l3pte_valid(opte)) { 252 UVMHIST_LOG(pmaphist, "invalid pte: %016llx: va=%016lx", 253 opte, va, 0, 0); 254 goto done; 255 } 256 257 if (l3pte_readable(opte)) { 258 cpu_icache_sync_range(va, PAGE_SIZE); 259 } else { 260 /* 261 * change to accessible temporarily 262 * to do cpu_icache_sync_range() 263 */ 264 struct pmap_asid_info * const pai = PMAP_PAI(pm, 265 cpu_tlb_info(ci)); 266 267 atomic_swap_64(ptep, opte | LX_BLKPAG_AF); 268 // tlb_invalidate_addr does the dsb(ishst); 269 tlb_invalidate_addr(va, pai->pai_asid); 270 cpu_icache_sync_range(va, PAGE_SIZE); 271 atomic_swap_64(ptep, opte); 272 tlb_invalidate_addr(va, pai->pai_asid); 273 } 274 } 275 done: 276 pmap_unlock(pm); 277 } 278 279 280 struct vm_page * 281 pmap_md_alloc_poolpage(int flags) 282 { 283 284 /* 285 * Any managed page works for us. 286 */ 287 return uvm_pagealloc(NULL, 0, NULL, flags); 288 } 289 290 291 vaddr_t 292 pmap_md_map_poolpage(paddr_t pa, size_t len) 293 { 294 struct vm_page * const pg = PHYS_TO_VM_PAGE(pa); 295 const vaddr_t va = pmap_md_direct_map_paddr(pa); 296 KASSERT(cold || pg != NULL); 297 298 if (pg != NULL) { 299 struct vm_page_md * const mdpg = VM_PAGE_TO_MD(pg); 300 const pv_entry_t pv = &mdpg->mdpg_first; 301 const vaddr_t last_va = trunc_page(pv->pv_va); 302 303 KASSERT(len == PAGE_SIZE || last_va == pa); 304 KASSERT(pv->pv_pmap == NULL); 305 KASSERT(pv->pv_next == NULL); 306 KASSERT(!VM_PAGEMD_EXECPAGE_P(mdpg)); 307 308 pv->pv_va = va; 309 } 310 311 return va; 312 } 313 314 315 paddr_t 316 pmap_md_unmap_poolpage(vaddr_t va, size_t len) 317 { 318 KASSERT(len == PAGE_SIZE); 319 KASSERT(pmap_md_direct_mapped_vaddr_p(va)); 320 321 const paddr_t pa = pmap_md_direct_mapped_vaddr_to_paddr(va); 322 struct vm_page * const pg = PHYS_TO_VM_PAGE(pa); 323 324 KASSERT(pg); 325 struct vm_page_md * const mdpg = VM_PAGE_TO_MD(pg); 326 327 KASSERT(!VM_PAGEMD_EXECPAGE_P(mdpg)); 328 329 const pv_entry_t pv = &mdpg->mdpg_first; 330 331 /* Note last mapped address for future color check */ 332 pv->pv_va = va; 333 334 KASSERT(pv->pv_pmap == NULL); 335 KASSERT(pv->pv_next == NULL); 336 337 return pa; 338 } 339 340 341 bool 342 pmap_md_kernel_vaddr_p(vaddr_t va) 343 { 344 extern char __kernel_text[]; 345 extern char _end[]; 346 extern long kernend_extra; 347 348 const vaddr_t kernstart = trunc_page((vaddr_t)__kernel_text); 349 const vaddr_t kernend = round_page((vaddr_t)_end); 350 351 const vaddr_t fva = L2_TRUNC_BLOCK(kernstart); 352 const vaddr_t lva = L2_ROUND_BLOCK(kernend + kernend_extra); 353 354 if (va >= fva && va < lva) { 355 return true; 356 } 357 358 return false; 359 } 360 361 paddr_t 362 pmap_md_kernel_vaddr_to_paddr(vaddr_t va) 363 { 364 365 if (pmap_md_kernel_vaddr_p(va)) { 366 return KERN_VTOPHYS(va); 367 } 368 panic("%s: va %#" PRIxVADDR " not direct mapped!", __func__, va); 369 } 370 371 372 bool 373 pmap_md_direct_mapped_vaddr_p(vaddr_t va) 374 { 375 if (!AARCH64_KVA_P(va)) 376 return false; 377 378 paddr_t pa = AARCH64_KVA_TO_PA(va); 379 if (physical_start <= pa && pa < physical_end) 380 return true; 381 382 return false; 383 } 384 385 386 paddr_t 387 pmap_md_direct_mapped_vaddr_to_paddr(vaddr_t va) 388 { 389 390 return AARCH64_KVA_TO_PA(va); 391 } 392 393 394 vaddr_t 395 pmap_md_direct_map_paddr(paddr_t pa) 396 { 397 398 return AARCH64_PA_TO_KVA(pa); 399 } 400 401 402 bool 403 pmap_md_io_vaddr_p(vaddr_t va) 404 { 405 406 if (pmap_devmap_find_va(va, PAGE_SIZE)) { 407 return true; 408 } 409 return false; 410 } 411 412 413 static void 414 pmap_md_grow(pmap_pdetab_t *ptb, vaddr_t va, vsize_t vshift, 415 vsize_t *remaining) 416 { 417 KASSERT((va & (NBSEG - 1)) == 0); 418 const vaddr_t pdetab_mask = PMAP_PDETABSIZE - 1; 419 const vsize_t vinc = 1UL << vshift; 420 421 for (size_t i = (va >> vshift) & pdetab_mask; 422 i < PMAP_PDETABSIZE; i++, va += vinc) { 423 pd_entry_t * const pde_p = 424 &ptb->pde_pde[(va >> vshift) & pdetab_mask]; 425 426 vaddr_t pdeva; 427 if (pte_pde_valid_p(*pde_p)) { 428 const paddr_t pa = pte_pde_to_paddr(*pde_p); 429 pdeva = pmap_md_direct_map_paddr(pa); 430 } else { 431 /* 432 * uvm_pageboot_alloc() returns a direct mapped address 433 */ 434 pdeva = uvm_pageboot_alloc(Ln_TABLE_SIZE); 435 paddr_t pdepa = AARCH64_KVA_TO_PA(pdeva); 436 *pde_p = pte_pde_pdetab(pdepa, true); 437 memset((void *)pdeva, 0, PAGE_SIZE); 438 } 439 440 if (vshift > SEGSHIFT) { 441 pmap_md_grow((pmap_pdetab_t *)pdeva, va, 442 vshift - SEGLENGTH, remaining); 443 } else { 444 if (*remaining > vinc) 445 *remaining -= vinc; 446 else 447 *remaining = 0; 448 } 449 if (*remaining == 0) 450 return; 451 } 452 } 453 454 455 void 456 pmap_bootstrap(vaddr_t vstart, vaddr_t vend) 457 { 458 pmap_t pm = pmap_kernel(); 459 460 /* 461 * Initialise the kernel pmap object 462 */ 463 virtual_avail = vstart; 464 virtual_end = vend; 465 466 aarch64_tlbi_all(); 467 468 pm->pm_l0_pa = __SHIFTOUT(reg_ttbr1_el1_read(), TTBR_BADDR); 469 pm->pm_pdetab = (pmap_pdetab_t *)AARCH64_PA_TO_KVA(pm->pm_l0_pa); 470 471 VPRINTF("common "); 472 pmap_bootstrap_common(); 473 474 VPRINTF("tlb0 "); 475 pmap_tlb_info_init(&pmap_tlb0_info); 476 477 #ifdef MULTIPROCESSOR 478 VPRINTF("kcpusets "); 479 480 kcpuset_create(&pm->pm_onproc, true); 481 kcpuset_create(&pm->pm_active, true); 482 KASSERT(pm->pm_onproc != NULL); 483 KASSERT(pm->pm_active != NULL); 484 kcpuset_set(pm->pm_onproc, cpu_number()); 485 kcpuset_set(pm->pm_active, cpu_number()); 486 #endif 487 488 VPRINTF("nkmempages "); 489 /* 490 * Compute the number of pages kmem_arena will have. This will also 491 * be called by uvm_km_bootstrap later, but that doesn't matter 492 */ 493 kmeminit_nkmempages(); 494 495 /* Get size of buffer cache and set an upper limit */ 496 buf_setvalimit((VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / 8); 497 vsize_t bufsz = buf_memcalc(); 498 buf_setvalimit(bufsz); 499 500 vsize_t kvmsize = (VM_PHYS_SIZE + (ubc_nwins << ubc_winshift) + 501 bufsz + 16 * NCARGS + pager_map_size) + 502 /*(maxproc * UPAGES) + */nkmempages * NBPG; 503 504 #ifdef SYSVSHM 505 kvmsize += shminfo.shmall; 506 #endif 507 508 /* Calculate VA address space and roundup to NBSEG tables */ 509 kvmsize = roundup(kvmsize, NBSEG); 510 511 /* 512 * Initialize `FYI' variables. Note we're relying on 513 * the fact that BSEARCH sorts the vm_physmem[] array 514 * for us. Must do this before uvm_pageboot_alloc() 515 * can be called. 516 */ 517 pmap_limits.avail_start = ptoa(uvm_physseg_get_start(uvm_physseg_get_first())); 518 pmap_limits.avail_end = ptoa(uvm_physseg_get_end(uvm_physseg_get_last())); 519 520 /* 521 * Update the naive settings in pmap_limits to the actual KVA range. 522 */ 523 pmap_limits.virtual_start = vstart; 524 pmap_limits.virtual_end = vend; 525 526 VPRINTF("\nlimits: %" PRIxVADDR " - %" PRIxVADDR "\n", vstart, vend); 527 528 const vaddr_t kvmstart = vstart; 529 pmap_curmaxkvaddr = vstart + kvmsize; 530 531 VPRINTF("kva : %" PRIxVADDR " - %" PRIxVADDR "\n", kvmstart, 532 pmap_curmaxkvaddr); 533 534 pmap_md_grow(pmap_kernel()->pm_pdetab, kvmstart, XSEGSHIFT, &kvmsize); 535 536 #if defined(EFI_RUNTIME) 537 vaddr_t efi_l0va = uvm_pageboot_alloc(Ln_TABLE_SIZE); 538 KASSERT((efi_l0va & PAGE_MASK) == 0); 539 540 pmap_t efipm = pmap_efirt(); 541 efipm->pm_l0_pa = AARCH64_KVA_TO_PA(efi_l0va); 542 efipm->pm_pdetab = (pmap_pdetab_t *)efi_l0va; 543 #endif 544 545 pool_init(&pmap_pmap_pool, PMAP_SIZE, 0, 0, 0, "pmappl", 546 &pool_allocator_nointr, IPL_NONE); 547 548 pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl", 549 #ifdef KASAN 550 NULL, 551 #else 552 &pmap_pv_page_allocator, 553 #endif 554 IPL_NONE); 555 556 // arm_dcache_align 557 pmap_pvlist_lock_init(CACHE_LINE_SIZE); 558 559 VPRINTF("done\n"); 560 } 561 562 563 #if defined(EFI_RUNTIME) 564 void 565 pmap_md_activate_efirt(void) 566 { 567 kpreempt_disable(); 568 569 // struct cpu_info * const ci = curcpu(); 570 struct pmap * const pm = pmap_efirt(); 571 struct pmap_asid_info * const pai = PMAP_PAI(pm, cpu_tlb_info(ci)); 572 573 pmap_md_asid_activate(pai->pai_asid, pmap_efirt(), NULL); 574 } 575 576 void 577 pmap_md_deactivate_efirt(void) 578 { 579 pmap_md_asid_deactivate(pmap_efirt()); 580 581 KASSERT((reg_tcr_el1_read() & TCR_EPD0) != 0); 582 583 kpreempt_enable(); 584 } 585 #endif 586 587 588 void 589 pmap_md_pdetab_init(struct pmap *pm) 590 { 591 592 KASSERT(pm != NULL); 593 594 pmap_extract(pmap_kernel(), (vaddr_t)pm->pm_pdetab, &pm->pm_l0_pa); 595 } 596 597 void 598 pmap_md_pdetab_fini(struct pmap *pm) 599 { 600 601 KASSERT(pm != NULL); 602 } 603 604 605 void 606 pmap_md_page_syncicache(struct vm_page_md *mdpg, const kcpuset_t *onproc) 607 { 608 UVMHIST_FUNC(__func__); UVMHIST_CALLED(pmaphist); 609 610 /* 611 * This handles the PIPT case where we can use the direct map 612 * to sync the icache without worrying about aliasing. 613 */ 614 struct vm_page * const pg = VM_MD_TO_PAGE(mdpg); 615 const paddr_t pa = VM_PAGE_TO_PHYS(pg); 616 const vaddr_t kva = pmap_md_direct_map_paddr(pa); 617 cpu_icache_sync_range(kva, PAGE_SIZE); 618 } 619 620 621 bool 622 pmap_md_ok_to_steal_p(const uvm_physseg_t bank, size_t npgs) 623 { 624 625 return true; 626 } 627 628 629 pd_entry_t * 630 pmap_l0table(struct pmap *pm) 631 { 632 633 return pm->pm_pdetab->pde_pde; 634 } 635 636 637 #define L1_BLK_MAPPABLE_P(va, pa, size) \ 638 ((((va) | (pa)) & L1_OFFSET) == 0 && (size) >= L1_SIZE) 639 640 #define L2_BLK_MAPPABLE_P(va, pa, size) \ 641 ((((va) | (pa)) & L2_OFFSET) == 0 && (size) >= L2_SIZE) 642 643 644 vsize_t 645 pmap_kenter_range(vaddr_t va, paddr_t pa, vsize_t size, 646 vm_prot_t prot, u_int flags) 647 { 648 pt_entry_t attr; 649 psize_t blocksize; 650 651 vsize_t resid = round_page(size); 652 vsize_t mapped = 0; 653 654 while (resid > 0) { 655 if (L1_BLK_MAPPABLE_P(va, pa, resid)) { 656 blocksize = L1_SIZE; 657 attr = L1_BLOCK; 658 } else if (L2_BLK_MAPPABLE_P(va, pa, resid)) { 659 blocksize = L2_SIZE; 660 attr = L2_BLOCK; 661 } else { 662 blocksize = L3_SIZE; 663 attr = L3_PAGE; 664 } 665 666 pt_entry_t pte = pte_make_kenter_pa(pa, NULL, prot, flags); 667 pte &= ~LX_TYPE; 668 attr |= pte; 669 670 pmapboot_enter(va, pa, blocksize, blocksize, attr, NULL); 671 672 va += blocksize; 673 pa += blocksize; 674 resid -= blocksize; 675 mapped += blocksize; 676 } 677 678 return mapped; 679 } 680 681 #ifdef MULTIPROCESSOR 682 void 683 pmap_md_tlb_info_attach(struct pmap_tlb_info *ti, struct cpu_info *ci) 684 { 685 /* nothing */ 686 } 687 #endif /* MULTIPROCESSOR */ 688