#include "opt_riscv_debug.h"
#include "opt_multiprocessor.h"
#define __PMAP_PRIVATE
#include <sys/cdefs.h>
__RCSID("$NetBSD: pmap_machdep.c,v 1.28 2026/05/17 06:15:05 skrll Exp $");
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/cpu.h>
#include <uvm/uvm.h>
#include <riscv/machdep.h>
#include <riscv/sbi.h>
#include <riscv/sysreg.h>
#ifdef VERBOSE_INIT_RISCV
#define VPRINTF(...) printf(__VA_ARGS__)
#else
#define VPRINTF(...) __nothing
#endif
vaddr_t pmap_direct_base __read_mostly;
vaddr_t pmap_direct_end __read_mostly;
#ifdef _LP64
static pt_entry_t pmap_pte_pbmt_mask __read_mostly;
static pt_entry_t pmap_pte_pma __read_mostly;
static pt_entry_t pmap_pte_nc __read_mostly;
static pt_entry_t pmap_pte_io __read_mostly;
#endif
void
pmap_zero_page(paddr_t pa)
{
#ifdef _LP64
#ifdef PMAP_DIRECT_MAP
memset((void *)PMAP_DIRECT_MAP(pa), 0, PAGE_SIZE);
#else
#error "no direct map"
#endif
#else
KASSERT(false);
#endif
}
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
#ifdef _LP64
#ifdef PMAP_DIRECT_MAP
memcpy((void *)PMAP_DIRECT_MAP(dst), (const void *)PMAP_DIRECT_MAP(src),
PAGE_SIZE);
#else
#error "no direct map"
#endif
#else
KASSERT(false);
#endif
}
#ifdef _LP64
pt_entry_t
pte_enter_flags_to_pbmt(int flags)
{
if (flags & PMAP_DEV) {
return pmap_pte_io;
} else if (flags & PMAP_NOCACHE) {
return pmap_pte_nc;
}
return pmap_pte_pma;
}
#endif
struct vm_page *
pmap_md_alloc_poolpage(int flags)
{
return uvm_pagealloc(NULL, 0, NULL, flags);
}
vaddr_t
pmap_md_map_poolpage(paddr_t pa, vsize_t len)
{
#ifdef _LP64
return PMAP_DIRECT_MAP(pa);
#else
panic("not supported");
#endif
}
void
pmap_md_unmap_poolpage(vaddr_t pa, vsize_t len)
{
}
bool
pmap_md_direct_mapped_vaddr_p(vaddr_t va)
{
#ifdef _LP64
return RISCV_DIRECTMAP_P(va);
#else
return false;
#endif
}
bool
pmap_md_io_vaddr_p(vaddr_t va)
{
return false;
}
paddr_t
pmap_md_direct_mapped_vaddr_to_paddr(vaddr_t va)
{
#ifdef _LP64
#ifdef PMAP_DIRECT_MAP
return PMAP_DIRECT_UNMAP(va);
#else
KASSERT(false);
return 0;
#endif
#else
KASSERT(false);
return 0;
#endif
}
vaddr_t
pmap_md_direct_map_paddr(paddr_t pa)
{
#ifdef _LP64
return PMAP_DIRECT_MAP(pa);
#else
panic("not supported");
#endif
}
void
pmap_md_init(void)
{
pmap_tlb_info_evcnt_attach(&pmap_tlb0_info);
}
bool
pmap_md_ok_to_steal_p(const uvm_physseg_t bank, size_t npgs)
{
return true;
}
#ifdef MULTIPROCESSOR
void
pmap_md_tlb_info_attach(struct pmap_tlb_info *ti, struct cpu_info *ci)
{
}
#endif
void
pmap_md_pdetab_init(struct pmap *pmap)
{
KASSERT(pmap != NULL);
const vaddr_t pdetabva = (vaddr_t)pmap->pm_pdetab;
const paddr_t pdetabpa = pmap_md_direct_mapped_vaddr_to_paddr(pdetabva);
pmap->pm_md.md_ppn = pdetabpa >> PAGE_SHIFT;
for (size_t i = NPDEPG / 2; i < NPDEPG; ++i) {
pd_entry_t pde = pmap_kernel()->pm_pdetab->pde_pde[i];
if (pde) {
pmap->pm_pdetab->pde_pde[i] = pde;
}
}
}
void
pmap_md_pdetab_fini(struct pmap *pmap)
{
if (pmap == pmap_kernel())
return;
for (size_t i = NPDEPG / 2; i < NPDEPG; ++i) {
KASSERT(pte_invalid_pde() == 0);
pmap->pm_pdetab->pde_pde[i] = 0;
}
}
static void
pmap_md_grow(pmap_pdetab_t *ptb, vaddr_t va, vsize_t vshift,
vsize_t *remaining)
{
KASSERT((va & (NBSEG - 1)) == 0);
#ifdef _LP64
const vaddr_t pdetab_mask = PMAP_PDETABSIZE - 1;
const vsize_t vinc = 1UL << vshift;
for (size_t i = (va >> vshift) & pdetab_mask;
i < PMAP_PDETABSIZE; i++, va += vinc) {
pd_entry_t * const pde_p =
&ptb->pde_pde[(va >> vshift) & pdetab_mask];
vaddr_t pdeva;
if (pte_pde_valid_p(*pde_p)) {
const paddr_t pa = pte_pde_to_paddr(*pde_p);
pdeva = pmap_md_direct_map_paddr(pa);
} else {
pdeva = uvm_pageboot_alloc(PAGE_SIZE);
paddr_t pdepa = RISCV_KVA_TO_PA(pdeva);
*pde_p = pte_pde_pdetab(pdepa, true);
memset((void *)pdeva, 0, PAGE_SIZE);
}
if (vshift > SEGSHIFT) {
pmap_md_grow((pmap_pdetab_t *)pdeva, va,
vshift - SEGLENGTH, remaining);
} else {
if (*remaining > vinc)
*remaining -= vinc;
else
*remaining = 0;
}
if (*remaining == 0)
return;
}
#endif
}
void
pmap_pte_xmae(void)
{
#ifdef _LP64
pmap_pte_pbmt_mask = PTE_XMAE;
pmap_pte_pma = PTE_XMAE_PMA;
pmap_pte_nc = PTE_XMAE_NC;
pmap_pte_io = PTE_XMAE_IO;
#endif
}
void
pmap_bootstrap(vaddr_t vstart, vaddr_t vend)
{
extern pmap_pdetab_t bootstrap_pde[PAGE_SIZE / sizeof(pd_entry_t)];
pmap_t pm = pmap_kernel();
VPRINTF("common ");
pmap_bootstrap_common();
#ifdef MULTIPROCESSOR
VPRINTF("cpusets ");
struct cpu_info * const ci = curcpu();
kcpuset_create(&ci->ci_shootdowncpus, true);
#endif
VPRINTF("bs_pde %p ", bootstrap_pde);
pm->pm_pdetab = bootstrap_pde;
pm->pm_md.md_ppn = atop(KERN_VTOPHYS((vaddr_t)bootstrap_pde));
pmap_tlb_info_init(&pmap_tlb0_info);
VPRINTF("ASID max %x ", pmap_tlb0_info.ti_asid_max);
#ifdef MULTIPROCESSOR
VPRINTF("kcpusets ");
kcpuset_create(&pm->pm_onproc, true);
kcpuset_create(&pm->pm_active, true);
KASSERT(pm->pm_onproc != NULL);
KASSERT(pm->pm_active != NULL);
kcpuset_set(pm->pm_onproc, cpu_index(ci));
kcpuset_set(pm->pm_active, cpu_index(ci));
#endif
VPRINTF("nkmempages ");
kmeminit_nkmempages();
buf_setvalimit((VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / 8);
vsize_t bufsz = buf_memcalc();
buf_setvalimit(bufsz);
vsize_t kvmsize = (VM_PHYS_SIZE + (ubc_nwins << ubc_winshift) +
bufsz + 16 * NCARGS + pager_map_size) +
nkmempages * NBPG;
#ifdef SYSVSHM
kvmsize += shminfo.shmall;
#endif
kvmsize = roundup(kvmsize, NBSEG);
pmap_limits.avail_start = ptoa(uvm_physseg_get_start(uvm_physseg_get_first()));
pmap_limits.avail_end = ptoa(uvm_physseg_get_end(uvm_physseg_get_last()));
pmap_limits.virtual_start = vstart;
pmap_limits.virtual_end = vend;
VPRINTF("limits: %" PRIxVADDR " - %" PRIxVADDR "\n", vstart, vend);
const vaddr_t kvmstart = vstart;
pmap_curmaxkvaddr = vstart + kvmsize;
VPRINTF("kva : %" PRIxVADDR " - %" PRIxVADDR "\n", kvmstart,
pmap_curmaxkvaddr);
pmap_md_grow(pmap_kernel()->pm_pdetab, kvmstart, XSEGSHIFT, &kvmsize);
pool_init(&pmap_pmap_pool, PMAP_SIZE, 0, 0, 0, "pmappl",
&pool_allocator_nointr, IPL_NONE);
pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl",
#ifdef KASAN
NULL,
#else
&pmap_pv_page_allocator,
#endif
IPL_NONE);
pmap_pvlist_lock_init(CACHE_LINE_SIZE);
}
vsize_t
pmap_kenter_range(vaddr_t va, paddr_t pa, vsize_t size,
vm_prot_t prot, u_int flags)
{
extern pd_entry_t l1_pte[PAGE_SIZE / sizeof(pd_entry_t)];
vaddr_t sva = MEGAPAGE_TRUNC(va);
paddr_t spa = MEGAPAGE_TRUNC(pa);
const vaddr_t eva = MEGAPAGE_ROUND(va + size);
const vaddr_t pdetab_mask = PMAP_PDETABSIZE - 1;
const vsize_t vshift = SEGSHIFT;
const pt_entry_t pbmt_flag = pte_enter_flags_to_pbmt(flags);
while (sva < eva) {
const size_t sidx = (sva >> vshift) & pdetab_mask;
l1_pte[sidx] = PA_TO_PTE(spa) | PTE_KERN | PTE_HARDWIRED |
PTE_RW | pbmt_flag;
spa += NBSEG;
sva += NBSEG;
}
return 0;
}