Home | History | Annotate | Line # | Download | only in aarch64
      1 /*	$NetBSD: pmapboot.c,v 1.20 2024/12/14 07:43:09 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2018 Ryo Shimizu
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     25  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: pmapboot.c,v 1.20 2024/12/14 07:43:09 skrll Exp $");
     31 
     32 #include "opt_arm_debug.h"
     33 #include "opt_ddb.h"
     34 #include "opt_multiprocessor.h"
     35 #include "opt_pmap.h"
     36 #include "opt_pmapboot.h"
     37 
     38 #include <sys/param.h>
     39 #include <sys/types.h>
     40 
     41 #include <uvm/uvm.h>
     42 
     43 #include <arm/cpufunc.h>
     44 
     45 #include <aarch64/armreg.h>
     46 #ifdef DDB
     47 #include <aarch64/db_machdep.h>
     48 #endif
     49 #include <aarch64/machdep.h>
     50 #include <aarch64/pmap.h>
     51 #include <aarch64/pte.h>
     52 
     53 #include <arm/cpufunc.h>
     54 
     55 #define OPTIMIZE_TLB_CONTIG
     56 
     57 static void
     58 pmapboot_protect_entry(pt_entry_t *pte, vm_prot_t clrprot)
     59 {
     60 	extern uint64_t pmap_attr_gp;
     61 
     62 	if (clrprot & VM_PROT_READ)
     63 		*pte &= ~LX_BLKPAG_AF;
     64 	if (clrprot & VM_PROT_WRITE) {
     65 		*pte &= ~LX_BLKPAG_AP;
     66 		*pte |= LX_BLKPAG_AP_RO;
     67 		*pte |= pmap_attr_gp;
     68 	}
     69 	if (clrprot & VM_PROT_EXECUTE)
     70 		*pte |= LX_BLKPAG_UXN | LX_BLKPAG_PXN;
     71 }
     72 
     73 /*
     74  * like pmap_protect(), but not depend on struct pmap.
     75  * this work before pmap_bootstrap().
     76  * 'clrprot' specified by bit of VM_PROT_{READ,WRITE,EXECUTE}
     77  * will be dropped from a pte entry.
     78  *
     79  * require direct (cached) mappings because TLB entries are already cached on.
     80  */
     81 int
     82 pmapboot_protect(vaddr_t sva, vaddr_t eva, vm_prot_t clrprot)
     83 {
     84 	int idx;
     85 	vaddr_t va;
     86 	paddr_t pa;
     87 	pd_entry_t *l0, *l1, *l2, *l3;
     88 
     89 	switch (aarch64_addressspace(sva)) {
     90 	case AARCH64_ADDRSPACE_LOWER:
     91 		/* 0x0000xxxxxxxxxxxx */
     92 		pa = (reg_ttbr0_el1_read() & TTBR_BADDR);
     93 		break;
     94 	case AARCH64_ADDRSPACE_UPPER:
     95 		/* 0xFFFFxxxxxxxxxxxx */
     96 		pa = (reg_ttbr1_el1_read() & TTBR_BADDR);
     97 		break;
     98 	default:
     99 		return -1;
    100 	}
    101 	l0 = (pd_entry_t *)AARCH64_PA_TO_KVA(pa);
    102 
    103 	for (va = sva; va < eva;) {
    104 		idx = l0pde_index(va);
    105 		if (!l0pde_valid(l0[idx]))
    106 			return -1;
    107 		pa = l0pde_pa(l0[idx]);
    108 		l1 = (pd_entry_t *)AARCH64_PA_TO_KVA(pa);
    109 
    110 		idx = l1pde_index(va);
    111 		if (!l1pde_valid(l1[idx]))
    112 			return -1;
    113 		if (l1pde_is_block(l1[idx])) {
    114 			pmapboot_protect_entry(&l1[idx], clrprot);
    115 			va += L1_SIZE;
    116 			continue;
    117 		}
    118 		pa = l1pde_pa(l1[idx]);
    119 		l2 = (pd_entry_t *)AARCH64_PA_TO_KVA(pa);
    120 
    121 		idx = l2pde_index(va);
    122 		if (!l2pde_valid(l2[idx]))
    123 			return -1;
    124 		if (l2pde_is_block(l2[idx])) {
    125 			pmapboot_protect_entry(&l2[idx], clrprot);
    126 			va += L2_SIZE;
    127 			continue;
    128 		}
    129 		pa = l2pde_pa(l2[idx]);
    130 		l3 = (pd_entry_t *)AARCH64_PA_TO_KVA(pa);
    131 
    132 		idx = l3pte_index(va);
    133 		if (!l3pte_valid(l3[idx]))
    134 			return -1;
    135 		if (!l3pte_is_page(l3[idx]))
    136 			return -1;
    137 
    138 		pmapboot_protect_entry(&l3[idx], clrprot);
    139 		va += L3_SIZE;
    140 	}
    141 
    142 	return 0;
    143 }
    144 
    145 
    146 /*
    147  * these function will be called from locore without MMU.
    148  * the load address varies depending on the bootloader.
    149  * cannot use absolute addressing to refer text/data/bss.
    150  *
    151  * (*pr) function may be minimal printf. (when provided from locore)
    152  * it supports only maximum 7 argument, and only '%d', '%x', and '%s' formats.
    153  */
    154 
    155 #ifdef VERBOSE_INIT_ARM
    156 #define VPRINTF(fmt, args...)	\
    157 	while (pr != NULL) { pr(fmt, ## args); break; }
    158 #else
    159 #define VPRINTF(fmt, args...)	__nothing
    160 #endif
    161 
    162 #ifdef PMAPBOOT_DEBUG
    163 static void
    164 pmapboot_pte_print(pt_entry_t pte, int level,
    165     void (*pr)(const char *, ...) __printflike(1, 2))
    166 {
    167 #ifdef DDB
    168 	db_pte_print(pte, level, pr);
    169 #else
    170 	__USE(level);
    171 	pr(" %s PA=%016lx\n",
    172 	    l0pde_valid(pte) ? "VALID" : "INVALID",
    173 	    l0pde_pa(pte));
    174 #endif
    175 }
    176 #define PMAPBOOT_DPRINTF(fmt, args...)	\
    177 	while (pr != NULL) { pr(fmt, ## args); break; }
    178 #define PMAPBOOT_DPRINT_PTE(pte, l)	\
    179 	while (pr != NULL) { pmapboot_pte_print((pte), (l), pr); break; }
    180 #else /* PMAPBOOT_DEBUG */
    181 #define PMAPBOOT_DPRINTF(fmt, args...)	__nothing
    182 #define PMAPBOOT_DPRINT_PTE(pte, l)	__nothing
    183 #endif /* PMAPBOOT_DEBUG */
    184 
    185 
    186 #ifdef OPTIMIZE_TLB_CONTIG
    187 static inline bool
    188 tlb_contiguous_p(vaddr_t va, paddr_t pa, vaddr_t start, vaddr_t end,
    189     vsize_t blocksize)
    190 {
    191 	/*
    192 	 * when using 4KB granule, 16 adjacent and aligned entries can be
    193 	 * unified to one TLB cache entry.
    194 	 * in other size of granule, not supported.
    195 	 */
    196 	const vaddr_t mask = (blocksize << 4) - 1;
    197 
    198 	/* if the output address doesn't align it can't be contiguous */
    199 	if ((va & mask) != (pa & mask))
    200 		return false;
    201 
    202 	if ((va & ~mask) >= start && (va | mask) <= end)
    203 		return true;
    204 
    205 	return false;
    206 }
    207 #endif /* OPTIMIZE_TLB_CONTIG */
    208 
    209 /*
    210  * pmapboot_enter() accesses pagetables by physical address.
    211  * this should be called while identity mapping (VA=PA) available.
    212  */
    213 void
    214 pmapboot_enter(vaddr_t va, paddr_t pa, psize_t size, psize_t blocksize,
    215     pt_entry_t attr, void (*pr)(const char *, ...) __printflike(1, 2))
    216 {
    217 	int level, idx0, idx1, idx2, idx3, nskip = 0;
    218 	int ttbr __unused;
    219 	vaddr_t va_end;
    220 	pd_entry_t *l0, *l1, *l2, *l3, pte;
    221 #ifdef OPTIMIZE_TLB_CONTIG
    222 	vaddr_t va_start;
    223 	pd_entry_t *ll;
    224 	int i, llidx;
    225 #endif
    226 
    227 	switch (blocksize) {
    228 	case L1_SIZE:
    229 		level = 1;
    230 		break;
    231 	case L2_SIZE:
    232 		level = 2;
    233 		break;
    234 	case L3_SIZE:
    235 		level = 3;
    236 		break;
    237 	default:
    238 		panic("%s: bad blocksize (%" PRIxPSIZE ")", __func__, blocksize);
    239 	}
    240 
    241 	VPRINTF("pmapboot_enter: va=0x%lx, pa=0x%lx, size=0x%lx, "
    242 	    "blocksize=0x%lx, attr=0x%016lx\n",
    243 	    va, pa, size, blocksize, attr);
    244 
    245 	pa &= ~(blocksize - 1);
    246 	va_end = (va + size + blocksize - 1) & ~(blocksize - 1);
    247 	va &= ~(blocksize - 1);
    248 #ifdef OPTIMIZE_TLB_CONTIG
    249 	va_start = va;
    250 #endif
    251 
    252 	attr |= LX_BLKPAG_OS_BOOT;
    253 
    254 	switch (aarch64_addressspace(va)) {
    255 	case AARCH64_ADDRSPACE_LOWER:
    256 		/* 0x0000xxxxxxxxxxxx */
    257 		l0 = (pd_entry_t *)(reg_ttbr0_el1_read() & TTBR_BADDR);
    258 		ttbr = 0;
    259 		break;
    260 	case AARCH64_ADDRSPACE_UPPER:
    261 		/* 0xFFFFxxxxxxxxxxxx */
    262 		l0 = (pd_entry_t *)(reg_ttbr1_el1_read() & TTBR_BADDR);
    263 		ttbr = 1;
    264 		break;
    265 	default:
    266 		panic("%s: unknown address space (%d/%" PRIxVADDR ")", __func__,
    267 		    aarch64_addressspace(va), va);
    268 	}
    269 
    270 	while (va < va_end) {
    271 #ifdef OPTIMIZE_TLB_CONTIG
    272 		ll = NULL;
    273 		llidx = -1;
    274 #endif
    275 
    276 		idx0 = l0pde_index(va);
    277 		if (l0[idx0] == 0) {
    278 			l1 = pmapboot_pagealloc();
    279 			if (l1 == NULL) {
    280 				VPRINTF("pmapboot_enter: "
    281 				    "cannot allocate L1 page\n");
    282 				panic("%s: can't allocate memory", __func__);
    283 			}
    284 
    285 			pte = (uint64_t)l1 | L0_TABLE;
    286 			l0[idx0] = pte;
    287 			PMAPBOOT_DPRINTF("TTBR%d[%d] (new)\t= %016lx:",
    288 			    ttbr, idx0, pte);
    289 			PMAPBOOT_DPRINT_PTE(pte, 0);
    290 		} else {
    291 			l1 = (uint64_t *)(l0[idx0] & LX_TBL_PA);
    292 		}
    293 
    294 		idx1 = l1pde_index(va);
    295 		if (level == 1) {
    296 			pte = pa |
    297 			    L1_BLOCK |
    298 			    LX_BLKPAG_AF |
    299 #ifdef MULTIPROCESSOR
    300 			    LX_BLKPAG_SH_IS |
    301 #endif
    302 			    attr;
    303 #ifdef OPTIMIZE_TLB_CONTIG
    304 			if (tlb_contiguous_p(va, pa, va_start, va_end, blocksize))
    305 				pte |= LX_BLKPAG_CONTIG;
    306 			ll = l1;
    307 			llidx = idx1;
    308 #endif
    309 
    310 			if (l1pde_valid(l1[idx1]) && l1[idx1] != pte) {
    311 				nskip++;
    312 				goto nextblk;
    313 			}
    314 
    315 			l1[idx1] = pte;
    316 			PMAPBOOT_DPRINTF("TTBR%d[%d][%d]\t= %016lx:", ttbr,
    317 			    idx0, idx1, pte);
    318 			PMAPBOOT_DPRINT_PTE(pte, 1);
    319 			goto nextblk;
    320 		}
    321 
    322 		if (!l1pde_valid(l1[idx1])) {
    323 			l2 = pmapboot_pagealloc();
    324 			if (l2 == NULL) {
    325 				VPRINTF("pmapboot_enter: "
    326 				    "cannot allocate L2 page\n");
    327 				panic("%s: can't allocate memory", __func__);
    328 			}
    329 
    330 			pte = (uint64_t)l2 | L1_TABLE;
    331 			l1[idx1] = pte;
    332 			PMAPBOOT_DPRINTF("TTBR%d[%d][%d] (new)\t= %016lx:",
    333 			    ttbr, idx0, idx1, pte);
    334 			PMAPBOOT_DPRINT_PTE(pte, 1);
    335 		} else {
    336 			l2 = (uint64_t *)(l1[idx1] & LX_TBL_PA);
    337 		}
    338 
    339 		idx2 = l2pde_index(va);
    340 		if (level == 2) {
    341 			pte = pa |
    342 			    L2_BLOCK |
    343 			    LX_BLKPAG_AF |
    344 #ifdef MULTIPROCESSOR
    345 			    LX_BLKPAG_SH_IS |
    346 #endif
    347 			    attr;
    348 #ifdef OPTIMIZE_TLB_CONTIG
    349 			if (tlb_contiguous_p(va, pa, va_start, va_end, blocksize))
    350 				pte |= LX_BLKPAG_CONTIG;
    351 			ll = l2;
    352 			llidx = idx2;
    353 #endif
    354 			if (l2pde_valid(l2[idx2]) && l2[idx2] != pte) {
    355 				nskip++;
    356 				goto nextblk;
    357 			}
    358 
    359 			l2[idx2] = pte;
    360 			PMAPBOOT_DPRINTF("TTBR%d[%d][%d][%d]\t= %016lx:", ttbr,
    361 			    idx0, idx1, idx2, pte);
    362 			PMAPBOOT_DPRINT_PTE(pte, 2);
    363 			goto nextblk;
    364 		}
    365 
    366 		if (!l2pde_valid(l2[idx2])) {
    367 			l3 = pmapboot_pagealloc();
    368 			if (l3 == NULL) {
    369 				VPRINTF("pmapboot_enter: "
    370 				    "cannot allocate L3 page\n");
    371 				panic("%s: can't allocate memory", __func__);
    372 			}
    373 
    374 			pte = (uint64_t)l3 | L2_TABLE;
    375 			l2[idx2] = pte;
    376 			PMAPBOOT_DPRINTF("TTBR%d[%d][%d][%d] (new)\t= %016lx:",
    377 			    ttbr, idx0, idx1, idx2, pte);
    378 			PMAPBOOT_DPRINT_PTE(pte, 2);
    379 		} else {
    380 			l3 = (uint64_t *)(l2[idx2] & LX_TBL_PA);
    381 		}
    382 
    383 		idx3 = l3pte_index(va);
    384 
    385 		pte = pa |
    386 		    L3_PAGE |
    387 		    LX_BLKPAG_AF |
    388 #ifdef MULTIPROCESSOR
    389 		    LX_BLKPAG_SH_IS |
    390 #endif
    391 		    attr;
    392 #ifdef OPTIMIZE_TLB_CONTIG
    393 		if (tlb_contiguous_p(va, pa, va_start, va_end, blocksize))
    394 			pte |= LX_BLKPAG_CONTIG;
    395 		ll = l3;
    396 		llidx = idx3;
    397 #endif
    398 		if (l3pte_valid(l3[idx3]) && l3[idx3] != pte) {
    399 			nskip++;
    400 			goto nextblk;
    401 		}
    402 
    403 		l3[idx3] = pte;
    404 		PMAPBOOT_DPRINTF("TTBR%d[%d][%d][%d][%d]\t= %lx:", ttbr,
    405 		    idx0, idx1, idx2, idx3, pte);
    406 		PMAPBOOT_DPRINT_PTE(pte, 3);
    407  nextblk:
    408 #ifdef OPTIMIZE_TLB_CONTIG
    409 		/*
    410 		 * when overwriting a pte entry the contiguous bit in entries
    411 		 * before/after the entry should be cleared.
    412 		 */
    413 		if (ll != NULL) {
    414 			if (va == va_start && (llidx & 15) != 0) {
    415 				/* clear CONTIG flag before this pte entry */
    416 				for (i = (llidx & ~15); i < llidx; i++) {
    417 					ll[i] &= ~LX_BLKPAG_CONTIG;
    418 				}
    419 			}
    420 			if (va == va_end && (llidx & 15) != 15) {
    421 				/* clear CONTIG flag after this pte entry */
    422 				for (i = (llidx + 1); i < ((llidx + 16) & ~15);
    423 				    i++) {
    424 					ll[i] &= ~LX_BLKPAG_CONTIG;
    425 				}
    426 			}
    427 		}
    428 #endif
    429 		switch (level) {
    430 		case 1:
    431 			va += L1_SIZE;
    432 			pa += L1_SIZE;
    433 			break;
    434 		case 2:
    435 			va += L2_SIZE;
    436 			pa += L2_SIZE;
    437 			break;
    438 		case 3:
    439 			va += L3_SIZE;
    440 			pa += L3_SIZE;
    441 			break;
    442 		}
    443 	}
    444 
    445 	dsb(ish);
    446 
    447 	if (nskip != 0)
    448 		panic("%s: overlapping/incompatible mappings (%d)", __func__, nskip);
    449 }
    450 
    451 paddr_t pmapboot_pagebase __attribute__((__section__(".data")));
    452 
    453 pd_entry_t *
    454 pmapboot_pagealloc(void)
    455 {
    456 	extern long kernend_extra;
    457 
    458 	if (kernend_extra < 0)
    459 		return NULL;
    460 
    461 	paddr_t pa = pmapboot_pagebase + kernend_extra;
    462 	kernend_extra += PAGE_SIZE;
    463 
    464 	char *s = (char *)pa;
    465 	char *e = s + PAGE_SIZE;
    466 
    467 	while (s < e)
    468 		*s++ = 0;
    469 
    470 	return (pd_entry_t *)pa;
    471 }
    472 
    473 void
    474 pmapboot_enter_range(vaddr_t va, paddr_t pa, psize_t size, pt_entry_t attr,
    475     void (*pr)(const char *, ...) __printflike(1, 2))
    476 {
    477 	vaddr_t vend;
    478 	vsize_t left, mapsize, nblocks;
    479 
    480 	vend = round_page(va + size);
    481 	va = trunc_page(va);
    482 	left = vend - va;
    483 
    484 	/* align the start address to L2 blocksize */
    485 	nblocks = ulmin(left / L3_SIZE,
    486 	    Ln_ENTRIES - __SHIFTOUT(va, L3_ADDR_BITS));
    487 	if (((va & L3_ADDR_BITS) != 0) && (nblocks > 0)) {
    488 		mapsize = nblocks * L3_SIZE;
    489 		VPRINTF("Creating L3 tables: %016lx-%016lx : %016lx-%016lx\n",
    490 		    va, va + mapsize - 1, pa, pa + mapsize - 1);
    491 		pmapboot_enter(va, pa, mapsize, L3_SIZE, attr, pr);
    492 		va += mapsize;
    493 		pa += mapsize;
    494 		left -= mapsize;
    495 	}
    496 
    497 	/* align the start address to L1 blocksize */
    498 	nblocks = ulmin(left / L2_SIZE,
    499 	    Ln_ENTRIES - __SHIFTOUT(va, L2_ADDR_BITS));
    500 	if (((va & L2_ADDR_BITS) != 0) && (nblocks > 0)) {
    501 		mapsize = nblocks * L2_SIZE;
    502 		VPRINTF("Creating L2 tables: %016lx-%016lx : %016lx-%016lx\n",
    503 		    va, va + mapsize - 1, pa, pa + mapsize - 1);
    504 		pmapboot_enter(va, pa, mapsize, L2_SIZE, attr, pr);
    505 		va += mapsize;
    506 		pa += mapsize;
    507 		left -= mapsize;
    508 	}
    509 
    510 	nblocks = left / L1_SIZE;
    511 	if (nblocks > 0) {
    512 		mapsize = nblocks * L1_SIZE;
    513 		VPRINTF("Creating L1 tables: %016lx-%016lx : %016lx-%016lx\n",
    514 		    va, va + mapsize - 1, pa, pa + mapsize - 1);
    515 		pmapboot_enter(va, pa, mapsize, L1_SIZE, attr, pr);
    516 		va += mapsize;
    517 		pa += mapsize;
    518 		left -= mapsize;
    519 	}
    520 
    521 	if ((left & L2_ADDR_BITS) != 0) {
    522 		nblocks = left / L2_SIZE;
    523 		mapsize = nblocks * L2_SIZE;
    524 		VPRINTF("Creating L2 tables: %016lx-%016lx : %016lx-%016lx\n",
    525 		    va, va + mapsize - 1, pa, pa + mapsize - 1);
    526 		pmapboot_enter(va, pa, mapsize, L2_SIZE, attr, pr);
    527 		va += mapsize;
    528 		pa += mapsize;
    529 		left -= mapsize;
    530 	}
    531 
    532 	if ((left & L3_ADDR_BITS) != 0) {
    533 		nblocks = left / L3_SIZE;
    534 		mapsize = nblocks * L3_SIZE;
    535 		VPRINTF("Creating L3 tables: %016lx-%016lx : %016lx-%016lx\n",
    536 		    va, va + mapsize - 1, pa, pa + mapsize - 1);
    537 		pmapboot_enter(va, pa, mapsize, L3_SIZE, attr, pr);
    538 		va += mapsize;
    539 		pa += mapsize;
    540 		left -= mapsize;
    541 	}
    542 }
    543