#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap_subr.c,v 1.30 2020/07/06 10:31:24 rin Exp $");
#ifdef _KERNEL_OPT
#include "opt_altivec.h"
#include "opt_multiprocessor.h"
#include "opt_pmap.h"
#include "opt_ppcarch.h"
#endif
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#if defined (PPC_OEA) || defined (PPC_OEA64) || defined (PPC_OEA64_BRIDGE)
#include <powerpc/oea/vmparam.h>
#ifdef ALTIVEC
#include <powerpc/altivec.h>
#endif
#endif
#include <powerpc/psl.h>
#define MFMSR() mfmsr()
#define MTMSR(psl) __asm volatile("sync; mtmsr %0; isync" :: "r"(psl))
#ifdef PMAPCOUNTERS
#define PMAPCOUNT(ev) ((pmap_evcnt_ ## ev).ev_count++)
#define PMAPCOUNT2(ev) ((ev).ev_count++)
struct evcnt pmap_evcnt_zeroed_pages =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap",
"pages zeroed");
struct evcnt pmap_evcnt_copied_pages =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap",
"pages copied");
struct evcnt pmap_evcnt_idlezeroed_pages =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap",
"pages idle zeroed");
EVCNT_ATTACH_STATIC(pmap_evcnt_zeroed_pages);
EVCNT_ATTACH_STATIC(pmap_evcnt_copied_pages);
EVCNT_ATTACH_STATIC(pmap_evcnt_idlezeroed_pages);
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
struct evcnt pmap_evcnt_mappings =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "pages mapped");
struct evcnt pmap_evcnt_unmappings =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_mappings,
"pmap", "pages unmapped");
struct evcnt pmap_evcnt_kernel_mappings =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "kernel pages mapped");
struct evcnt pmap_evcnt_kernel_unmappings =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_kernel_mappings,
"pmap", "kernel pages unmapped");
struct evcnt pmap_evcnt_mappings_replaced =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "page mappings replaced");
struct evcnt pmap_evcnt_exec_mappings =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_mappings,
"pmap", "exec pages mapped");
struct evcnt pmap_evcnt_exec_cached =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_mappings,
"pmap", "exec pages cached");
struct evcnt pmap_evcnt_exec_synced =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages synced");
struct evcnt pmap_evcnt_exec_synced_clear_modify =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages synced (CM)");
struct evcnt pmap_evcnt_exec_synced_pvo_remove =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages synced (PR)");
struct evcnt pmap_evcnt_exec_uncached_page_protect =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages uncached (PP)");
struct evcnt pmap_evcnt_exec_uncached_clear_modify =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages uncached (CM)");
struct evcnt pmap_evcnt_exec_uncached_zero_page =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages uncached (ZP)");
struct evcnt pmap_evcnt_exec_uncached_copy_page =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages uncached (CP)");
struct evcnt pmap_evcnt_exec_uncached_pvo_remove =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &pmap_evcnt_exec_mappings,
"pmap", "exec pages uncached (PR)");
struct evcnt pmap_evcnt_updates =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "updates");
struct evcnt pmap_evcnt_collects =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "collects");
struct evcnt pmap_evcnt_copies =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "copies");
struct evcnt pmap_evcnt_ptes_spilled =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes spilled from overflow");
struct evcnt pmap_evcnt_ptes_unspilled =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes not spilled");
struct evcnt pmap_evcnt_ptes_evicted =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes evicted");
struct evcnt pmap_evcnt_ptes_primary[8] = {
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[0]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[1]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[2]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[3]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[4]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[5]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[6]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at primary[7]"),
};
struct evcnt pmap_evcnt_ptes_secondary[8] = {
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[0]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[1]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[2]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[3]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[4]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[5]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[6]"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes added at secondary[7]"),
};
struct evcnt pmap_evcnt_ptes_removed =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes removed");
struct evcnt pmap_evcnt_ptes_changed =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "ptes changed");
struct evcnt pmap_evcnt_pvos_reclaimed =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "pvos reclaimed");
struct evcnt pmap_evcnt_pvos_failed =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL,
"pmap", "pvo allocation failures");
EVCNT_ATTACH_STATIC(pmap_evcnt_mappings);
EVCNT_ATTACH_STATIC(pmap_evcnt_mappings_replaced);
EVCNT_ATTACH_STATIC(pmap_evcnt_unmappings);
EVCNT_ATTACH_STATIC(pmap_evcnt_kernel_mappings);
EVCNT_ATTACH_STATIC(pmap_evcnt_kernel_unmappings);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_mappings);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_cached);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_synced);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_synced_clear_modify);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_synced_pvo_remove);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_uncached_page_protect);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_uncached_clear_modify);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_uncached_zero_page);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_uncached_copy_page);
EVCNT_ATTACH_STATIC(pmap_evcnt_exec_uncached_pvo_remove);
EVCNT_ATTACH_STATIC(pmap_evcnt_updates);
EVCNT_ATTACH_STATIC(pmap_evcnt_collects);
EVCNT_ATTACH_STATIC(pmap_evcnt_copies);
EVCNT_ATTACH_STATIC(pmap_evcnt_ptes_spilled);
EVCNT_ATTACH_STATIC(pmap_evcnt_ptes_unspilled);
EVCNT_ATTACH_STATIC(pmap_evcnt_ptes_evicted);
EVCNT_ATTACH_STATIC(pmap_evcnt_ptes_removed);
EVCNT_ATTACH_STATIC(pmap_evcnt_ptes_changed);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 0);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 1);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 2);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 3);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 4);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 5);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 6);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_primary, 7);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 0);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 1);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 2);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 3);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 4);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 5);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 6);
EVCNT_ATTACH_STATIC2(pmap_evcnt_ptes_secondary, 7);
EVCNT_ATTACH_STATIC(pmap_evcnt_pvos_reclaimed);
EVCNT_ATTACH_STATIC(pmap_evcnt_pvos_failed);
#endif
#else
#define PMAPCOUNT(ev) ((void) 0)
#define PMAPCOUNT2(ev) ((void) 0)
#endif
void
pmap_zero_page(paddr_t pa)
{
size_t linewidth;
register_t msr = 0;
#if defined(PPC_OEA) || defined (PPC_OEA64_BRIDGE)
{
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
KDASSERT(pg != NULL);
KDASSERT(LIST_EMPTY(&md->mdpg_pvoh));
#ifdef PMAPCOUNTERS
if (md->mdpg_attrs & PTE_EXEC) {
PMAPCOUNT(exec_uncached_zero_page);
}
#endif
md->mdpg_attrs &= ~PTE_EXEC;
}
#endif
PMAPCOUNT(zeroed_pages);
#ifdef ALTIVEC
if (pmap_use_altivec) {
vzeropage(pa);
return;
}
#endif
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
if (pa >= SEGMENT_LENGTH) {
#endif
msr = MFMSR();
MTMSR(msr & ~PSL_DR);
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
}
#endif
if ((linewidth = curcpu()->ci_ci.dcache_line_size) == 0) {
long *dp = (long *)pa;
long * const ep = dp + PAGE_SIZE/sizeof(dp[0]);
do {
dp[0] = 0; dp[1] = 0; dp[2] = 0; dp[3] = 0;
dp[4] = 0; dp[5] = 0; dp[6] = 0; dp[7] = 0;
} while ((dp += 8) < ep);
} else {
size_t i = 0;
do {
__asm ("dcbz %0,%1" :: "b"(pa), "r"(i)); i += linewidth;
__asm ("dcbz %0,%1" :: "b"(pa), "r"(i)); i += linewidth;
} while (i < PAGE_SIZE);
}
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
if (pa >= SEGMENT_LENGTH)
#endif
MTMSR(msr);
}
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
const register_t *sp;
register_t *dp;
register_t msr;
size_t i;
#if defined(PPC_OEA) || defined (PPC_OEA64_BRIDGE)
{
struct vm_page *pg = PHYS_TO_VM_PAGE(dst);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
KDASSERT(pg != NULL);
KDASSERT(LIST_EMPTY(&md->mdpg_pvoh));
#ifdef PMAPCOUNTERS
if (md->mdpg_attrs & PTE_EXEC) {
PMAPCOUNT(exec_uncached_copy_page);
}
#endif
md->mdpg_attrs &= ~PTE_EXEC;
}
#endif
PMAPCOUNT(copied_pages);
#ifdef ALTIVEC
if (pmap_use_altivec) {
vcopypage(dst, src);
return;
}
#endif
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
if (src < SEGMENT_LENGTH && dst < SEGMENT_LENGTH) {
memcpy((void *) dst, (void *) src, PAGE_SIZE);
return;
}
#endif
msr = MFMSR();
MTMSR(msr & ~PSL_DR);
sp = (const register_t *) src;
dp = (register_t *) dst;
for (i = 0; i < PAGE_SIZE/sizeof(dp[0]); i += 8, dp += 8, sp += 8) {
dp[0] = sp[0]; dp[1] = sp[1]; dp[2] = sp[2]; dp[3] = sp[3];
dp[4] = sp[4]; dp[5] = sp[5]; dp[6] = sp[6]; dp[7] = sp[7];
}
MTMSR(msr);
}
void
pmap_syncicache(paddr_t pa, psize_t len)
{
#ifdef MULTIPROCESSOR__
__syncicache((void *)pa, len);
#else
const size_t linewidth = curcpu()->ci_ci.icache_line_size;
register_t msr;
size_t i;
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
if (pa + len <= SEGMENT_LENGTH) {
__syncicache((void *)pa, len);
return;
}
#endif
msr = MFMSR();
MTMSR(msr & ~(PSL_IR|PSL_DR));
len += pa - (pa & ~linewidth);
pa &= ~linewidth;
i = 0;
do {
__asm ("dcbst %0,%1" :: "b"(pa), "r"(i)); i += linewidth;
} while (i < len);
__asm volatile("sync");
i = 0;
do {
__asm ("icbi %0,%1" :: "b"(pa), "r"(i)); i += linewidth;
} while (i < len);
MTMSR(msr);
#endif
}
bool
pmap_pageidlezero(paddr_t pa)
{
register_t msr;
register_t *dp = (register_t *) pa;
struct cpu_info * const ci = curcpu();
bool rv = true;
int i;
#if defined(PPC_OEA) || defined (PPC_OEA64_BRIDGE)
if (pa < SEGMENT_LENGTH) {
for (i = 0; i < PAGE_SIZE / sizeof(dp[0]); i++) {
if (ci->ci_want_resched)
return false;
*dp++ = 0;
}
PMAPCOUNT(idlezeroed_pages);
return true;
}
#endif
msr = MFMSR();
MTMSR(msr & ~(PSL_IR|PSL_DR));
for (i = 0; i < PAGE_SIZE / sizeof(dp[0]); i++) {
if (ci->ci_want_resched) {
rv = false;
break;
}
*dp++ = 0;
}
MTMSR(msr);
#ifdef PMAPCOUNTERS
if (rv)
PMAPCOUNT(idlezeroed_pages);
#endif
return rv;
}