Home | History | Annotate | Line # | Download | only in riscv
      1 /* $NetBSD: pmap_machdep.c,v 1.29 2026/09/29 18:46:38 skrll Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2014, 2019, 2021 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Matt Thomas (of 3am Software Foundry), Maxime Villard, and
      9  * Nick Hudson.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include "opt_riscv_debug.h"
     34 #include "opt_multiprocessor.h"
     35 
     36 #define	__PMAP_PRIVATE
     37 
     38 #include <sys/cdefs.h>
     39 __RCSID("$NetBSD: pmap_machdep.c,v 1.29 2026/09/29 18:46:38 skrll Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/buf.h>
     43 #include <sys/cpu.h>
     44 
     45 #include <uvm/uvm.h>
     46 
     47 #include <riscv/machdep.h>
     48 #include <riscv/sbi.h>
     49 #include <riscv/sysreg.h>
     50 
     51 #ifdef VERBOSE_INIT_RISCV
     52 #define	VPRINTF(...)	printf(__VA_ARGS__)
     53 #else
     54 #define	VPRINTF(...)	__nothing
     55 #endif
     56 
     57 vaddr_t pmap_direct_base __read_mostly;
     58 vaddr_t pmap_direct_end __read_mostly;
     59 
     60 #ifdef _LP64
     61 static pt_entry_t pmap_pte_pbmt_mask __read_mostly;
     62 static pt_entry_t pmap_pte_pma __read_mostly;
     63 static pt_entry_t pmap_pte_nc __read_mostly;
     64 static pt_entry_t pmap_pte_io __read_mostly;
     65 #endif
     66 
     67 void
     68 pmap_zero_page(paddr_t pa)
     69 {
     70 #ifdef _LP64
     71 #ifdef PMAP_DIRECT_MAP
     72 	memset((void *)PMAP_DIRECT_MAP(pa), 0, PAGE_SIZE);
     73 #else
     74 #error "no direct map"
     75 #endif
     76 #else
     77 	KASSERT(false);
     78 #endif
     79 }
     80 
     81 void
     82 pmap_copy_page(paddr_t src, paddr_t dst)
     83 {
     84 #ifdef _LP64
     85 #ifdef PMAP_DIRECT_MAP
     86 	memcpy((void *)PMAP_DIRECT_MAP(dst), (const void *)PMAP_DIRECT_MAP(src),
     87 	    PAGE_SIZE);
     88 #else
     89 #error "no direct map"
     90 #endif
     91 #else
     92 	KASSERT(false);
     93 #endif
     94 }
     95 
     96 #ifdef _LP64
     97 pt_entry_t
     98 pte_enter_flags_to_pbmt(int flags)
     99 {
    100 	if (flags & PMAP_DEV) {
    101 		return pmap_pte_io;
    102 	} else if (flags & PMAP_NOCACHE) {
    103 		return pmap_pte_nc;
    104 	}
    105 	return pmap_pte_pma;
    106 }
    107 #endif
    108 
    109 struct vm_page *
    110 pmap_md_alloc_poolpage(int flags)
    111 {
    112 
    113 	return uvm_pagealloc(NULL, 0, NULL, flags);
    114 }
    115 
    116 vaddr_t
    117 pmap_md_map_poolpage(paddr_t pa, vsize_t len)
    118 {
    119 #ifdef _LP64
    120 	return PMAP_DIRECT_MAP(pa);
    121 #else
    122 	panic("not supported");
    123 #endif
    124 }
    125 
    126 void
    127 pmap_md_unmap_poolpage(vaddr_t pa, vsize_t len)
    128 {
    129 	/* nothing to do */
    130 }
    131 
    132 
    133 bool
    134 pmap_md_direct_mapped_vaddr_p(vaddr_t va)
    135 {
    136 #ifdef _LP64
    137 	return RISCV_DIRECTMAP_P(va);
    138 #else
    139 	return false;
    140 #endif
    141 }
    142 
    143 bool
    144 pmap_md_io_vaddr_p(vaddr_t va)
    145 {
    146 	return false;
    147 }
    148 
    149 paddr_t
    150 pmap_md_direct_mapped_vaddr_to_paddr(vaddr_t va)
    151 {
    152 #ifdef _LP64
    153 #ifdef PMAP_DIRECT_MAP
    154 	return PMAP_DIRECT_UNMAP(va);
    155 #else
    156 	KASSERT(false);
    157 	return 0;
    158 #endif
    159 #else
    160 	KASSERT(false);
    161 	return 0;
    162 #endif
    163 }
    164 
    165 vaddr_t
    166 pmap_md_direct_map_paddr(paddr_t pa)
    167 {
    168 #ifdef _LP64
    169 	return PMAP_DIRECT_MAP(pa);
    170 #else
    171 	panic("not supported");
    172 #endif
    173 }
    174 
    175 void
    176 pmap_md_init(void)
    177 {
    178 	pmap_tlb_info_evcnt_attach(&pmap_tlb0_info);
    179 }
    180 
    181 bool
    182 pmap_md_ok_to_steal_p(const uvm_physseg_t bank, size_t npgs)
    183 {
    184 	return true;
    185 }
    186 
    187 #ifdef MULTIPROCESSOR
    188 void
    189 pmap_md_tlb_info_attach(struct pmap_tlb_info *ti, struct cpu_info *ci)
    190 {
    191 }
    192 #endif
    193 
    194 
    195 void
    196 pmap_md_pdetab_init(struct pmap *pmap)
    197 {
    198 	KASSERT(pmap != NULL);
    199 
    200 	const vaddr_t pdetabva = (vaddr_t)pmap->pm_pdetab;
    201 	const paddr_t pdetabpa = pmap_md_direct_mapped_vaddr_to_paddr(pdetabva);
    202 	pmap->pm_md.md_ppn = pdetabpa >> PAGE_SHIFT;
    203 
    204 	/* XXXSB can we "pre-optimise" this by keeping a list of pdes to copy? */
    205 	/* XXXSB for relatively normal size memory (8gb) we only need 10-20ish ptes? */
    206 	/* XXXSB most (all?) of these ptes are  in two consecutive ranges. */
    207 	for (size_t i = NPDEPG / 2; i < NPDEPG; ++i) {
    208 		/*
    209 		 * XXXSB where/when do new entries in pmap_kernel()->pm_pdetab
    210 		 * XXXSB get added to existing pmaps?
    211 		 *
    212 		 * pmap_growkernel doesn't have support for fixing up exiting
    213 		 * pmaps. (yet)
    214 		 *
    215 		 * Various options:
    216 		 *
    217 		 * - do the x86 thing. maintain a list of pmaps and update them
    218 		 *   all in pmap_growkernel.
    219 		 * - make sure the top level entries are populated and them simply
    220 		 *   copy "them all" here.  If pmap_growkernel runs the new entries
    221 		 *   will become visible to all pmaps.
    222 		 * - ...
    223 		 */
    224 
    225 		/* XXXSB is this any faster than blindly copying all "high" entries? */
    226 		pd_entry_t pde = pmap_kernel()->pm_pdetab->pde_pde[i];
    227 
    228 		/*  we might have leaf entries (direct map) as well as non-leaf */
    229 		if (pde) {
    230 			pmap->pm_pdetab->pde_pde[i] = pde;
    231 		}
    232 	}
    233 }
    234 
    235 void
    236 pmap_md_pdetab_fini(struct pmap *pmap)
    237 {
    238 
    239 	if (pmap == pmap_kernel())
    240 		return;
    241 	for (size_t i = NPDEPG / 2; i < NPDEPG; ++i) {
    242 		KASSERT(pte_invalid_pde() == 0);
    243 		pmap->pm_pdetab->pde_pde[i] = 0;
    244 	}
    245 }
    246 
    247 static void
    248 pmap_md_grow(pmap_pdetab_t *ptb, vaddr_t va, vsize_t vshift,
    249     vsize_t *remaining)
    250 {
    251 	KASSERT((va & (NBSEG - 1)) == 0);
    252 #ifdef _LP64
    253 	const vaddr_t pdetab_mask = PMAP_PDETABSIZE - 1;
    254 	const vsize_t vinc = 1UL << vshift;
    255 
    256 	for (size_t i = (va >> vshift) & pdetab_mask;
    257 	    i < PMAP_PDETABSIZE; i++, va += vinc) {
    258 		pd_entry_t * const pde_p =
    259 		    &ptb->pde_pde[(va >> vshift) & pdetab_mask];
    260 
    261 		vaddr_t pdeva;
    262 		if (pte_pde_valid_p(*pde_p)) {
    263 			const paddr_t pa = pte_pde_to_paddr(*pde_p);
    264 			pdeva = pmap_md_direct_map_paddr(pa);
    265 		} else {
    266 			/*
    267 			 * uvm_pageboot_alloc() returns a direct mapped address
    268 			 */
    269 			pdeva = uvm_pageboot_alloc(PAGE_SIZE);
    270 			paddr_t pdepa = RISCV_KVA_TO_PA(pdeva);
    271 			*pde_p = pte_pde_pdetab(pdepa, true);
    272 			memset((void *)pdeva, 0, PAGE_SIZE);
    273 		}
    274 
    275 		if (vshift > SEGSHIFT) {
    276 			pmap_md_grow((pmap_pdetab_t *)pdeva, va,
    277 			    vshift - SEGLENGTH, remaining);
    278 		} else {
    279 			if (*remaining > vinc)
    280 				*remaining -= vinc;
    281 			else
    282 				*remaining = 0;
    283 		}
    284 		if (*remaining == 0)
    285 			return;
    286 	}
    287 #endif
    288 }
    289 
    290 void
    291 pmap_pte_xmae(void)
    292 {
    293 #ifdef _LP64
    294 	pmap_pte_pbmt_mask = PTE_XMAE;
    295 	pmap_pte_pma       = PTE_XMAE_PMA;
    296 	pmap_pte_nc        = PTE_XMAE_NC;
    297 	pmap_pte_io        = PTE_XMAE_IO;
    298 #endif
    299 }
    300 
    301 void
    302 pmap_bootstrap(vaddr_t vstart, vaddr_t vend)
    303 {
    304 	extern pmap_pdetab_t bootstrap_pde[PAGE_SIZE / sizeof(pd_entry_t)];
    305 
    306 //	pmap_pdetab_t * const kptb = &pmap_kern_pdetab;
    307 	pmap_t pm = pmap_kernel();
    308 
    309 	VPRINTF("common ");
    310 	pmap_bootstrap_common();
    311 
    312 	VPRINTF("bs_pde %p ", bootstrap_pde);
    313 
    314 //	kend = (kend + 0x200000 - 1) & -0x200000;
    315 
    316 	/* Use the tables we already built in init_riscv() */
    317 	pm->pm_pdetab = bootstrap_pde;
    318 
    319 	/* Get the PPN for our page table root */
    320 	pm->pm_md.md_ppn = atop(KERN_VTOPHYS((vaddr_t)bootstrap_pde));
    321 
    322 	/* Setup basic info like pagesize=PAGE_SIZE */
    323 //	uvm_md_init();
    324 
    325 	/* init the lock */
    326 	// XXXNH per cpu?
    327 	pmap_tlb_info_init(&pmap_tlb0_info);
    328 
    329 	VPRINTF("ASID max %x ", pmap_tlb0_info.ti_asid_max);
    330 
    331 #ifdef MULTIPROCESSOR
    332 	VPRINTF("kcpusets ");
    333 
    334 	kcpuset_create(&pm->pm_onproc, true);
    335 	kcpuset_create(&pm->pm_active, true);
    336 	KASSERT(pm->pm_onproc != NULL);
    337 	KASSERT(pm->pm_active != NULL);
    338 
    339 	struct cpu_info * const ci = curcpu();
    340 	kcpuset_set(pm->pm_onproc, cpu_index(ci));
    341 	kcpuset_set(pm->pm_active, cpu_index(ci));
    342 #endif
    343 
    344 	VPRINTF("nkmempages ");
    345 	/*
    346 	 * Compute the number of pages kmem_arena will have.  This will also
    347 	 * be called by uvm_km_bootstrap later, but that doesn't matter
    348 	 */
    349 	kmeminit_nkmempages();
    350 
    351 	/* Get size of buffer cache and set an upper limit */
    352 	buf_setvalimit((VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / 8);
    353 	vsize_t bufsz = buf_memcalc();
    354 	buf_setvalimit(bufsz);
    355 
    356 	vsize_t kvmsize = (VM_PHYS_SIZE + (ubc_nwins << ubc_winshift) +
    357 	    bufsz + 16 * NCARGS + pager_map_size) +
    358 	    /*(maxproc * UPAGES) + */nkmempages * NBPG;
    359 
    360 #ifdef SYSVSHM
    361 	kvmsize += shminfo.shmall;
    362 #endif
    363 
    364 	/* Calculate VA address space and roundup to NBSEG tables */
    365 	kvmsize = roundup(kvmsize, NBSEG);
    366 
    367 	/*
    368 	 * Initialize `FYI' variables.	Note we're relying on
    369 	 * the fact that BSEARCH sorts the vm_physmem[] array
    370 	 * for us.  Must do this before uvm_pageboot_alloc()
    371 	 * can be called.
    372 	 */
    373 	pmap_limits.avail_start = ptoa(uvm_physseg_get_start(uvm_physseg_get_first()));
    374 	pmap_limits.avail_end = ptoa(uvm_physseg_get_end(uvm_physseg_get_last()));
    375 
    376 	/*
    377 	 * Update the naive settings in pmap_limits to the actual KVA range.
    378 	 */
    379 	pmap_limits.virtual_start = vstart;
    380 	pmap_limits.virtual_end = vend;
    381 
    382 	VPRINTF("limits: %" PRIxVADDR " - %" PRIxVADDR "\n", vstart, vend);
    383 
    384 	const vaddr_t kvmstart = vstart;
    385 	pmap_curmaxkvaddr = vstart + kvmsize;
    386 
    387 	VPRINTF("kva   : %" PRIxVADDR " - %" PRIxVADDR "\n", kvmstart,
    388 	    pmap_curmaxkvaddr);
    389 
    390 	pmap_md_grow(pmap_kernel()->pm_pdetab, kvmstart, XSEGSHIFT, &kvmsize);
    391 
    392 	/*
    393 	 * Initialize the pools.
    394 	 */
    395 	pool_init(&pmap_pmap_pool, PMAP_SIZE, 0, 0, 0, "pmappl",
    396 	    &pool_allocator_nointr, IPL_NONE);
    397 
    398 	pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl",
    399 #ifdef KASAN
    400 	    NULL,
    401 #else
    402 	    &pmap_pv_page_allocator,
    403 #endif
    404 	    IPL_NONE);
    405 
    406 	// riscv_dcache_align
    407 	pmap_pvlist_lock_init(CACHE_LINE_SIZE);
    408 }
    409 
    410 
    411 vsize_t
    412 pmap_kenter_range(vaddr_t va, paddr_t pa, vsize_t size,
    413     vm_prot_t prot, u_int flags)
    414 {
    415 	extern pd_entry_t l1_pte[PAGE_SIZE / sizeof(pd_entry_t)];
    416 
    417 	vaddr_t sva = MEGAPAGE_TRUNC(va);
    418 	paddr_t spa = MEGAPAGE_TRUNC(pa);
    419 	const vaddr_t eva = MEGAPAGE_ROUND(va + size);
    420 	const vaddr_t pdetab_mask = PMAP_PDETABSIZE - 1;
    421 	const vsize_t vshift = SEGSHIFT;
    422 	const pt_entry_t pbmt_flag = pte_enter_flags_to_pbmt(flags);
    423 
    424 	while (sva < eva) {
    425 		const size_t sidx = (sva >> vshift) & pdetab_mask;
    426 
    427 		l1_pte[sidx] = PA_TO_PTE(spa) | PTE_KERN | PTE_HARDWIRED |
    428 		    PTE_RW | pbmt_flag;
    429 		spa += NBSEG;
    430 		sva += NBSEG;
    431 	}
    432 
    433 	return 0;
    434 }
    435