#include <sys/types.h>
#include <sys/systm.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/t_lock.h>
#include <sys/vmem.h>
#include <sys/mman.h>
#include <sys/vm.h>
#include <sys/cpu.h>
#include <sys/cmn_err.h>
#include <sys/cpuvar.h>
#include <sys/atomic.h>
#include <vm/as.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/seg.h>
#include <vm/seg_kmem.h>
#include <vm/seg_kpm.h>
#include <vm/hat_sfmmu.h>
#include <sys/debug.h>
#include <sys/cpu_module.h>
#include <sys/mem_cage.h>
#define clsettoarray(color, set) ((color * nsets) + set)
int pp_slots = 4;
int pp_consistent_coloring = PPAGE_STORES_POLLUTE | PPAGE_LOADS_POLLUTE;
static caddr_t ppmap_vaddrs[PPMAPSIZE / MMU_PAGESIZE];
static int nsets;
static int ppmap_pages;
static int ppmap_shift;
#ifdef PPDEBUG
#define MAXCOLORS 16
static int ppalloc_noslot = 0;
static int align_hits[MAXCOLORS];
static int pp_allocs;
#endif
static struct ppmap_va {
caddr_t ppmap_slots[MAXPP_SLOTS];
} ppmap_va[NCPU];
void
ppmapinit(void)
{
int color, nset, setsize;
caddr_t va;
ASSERT(pp_slots <= MAXPP_SLOTS);
va = (caddr_t)PPMAPBASE;
if (cache & CACHE_VAC) {
int a;
ppmap_pages = mmu_btop(shm_alignment);
nsets = PPMAPSIZE / shm_alignment;
setsize = shm_alignment;
ppmap_shift = MMU_PAGESHIFT;
a = ppmap_pages;
while (a >>= 1)
ppmap_shift++;
} else {
ppmap_pages = 1;
nsets = mmu_btop(PPMAPSIZE);
setsize = MMU_PAGESIZE;
ppmap_shift = MMU_PAGESHIFT;
}
for (color = 0; color < ppmap_pages; color++) {
for (nset = 0; nset < nsets; nset++) {
ppmap_vaddrs[clsettoarray(color, nset)] =
(caddr_t)((uintptr_t)va + (nset * setsize));
}
va += MMU_PAGESIZE;
}
}
caddr_t
ppmapin(page_t *pp, uint_t vprot, caddr_t hint)
{
int color, nset, index, start;
caddr_t va;
#ifdef PPDEBUG
pp_allocs++;
#endif
if (cache & CACHE_VAC) {
color = sfmmu_get_ppvcolor(pp);
if (color == -1) {
if ((intptr_t)hint != -1L) {
color = addr_to_vcolor(hint);
} else {
color = addr_to_vcolor(mmu_ptob(pp->p_pagenum));
}
}
} else {
color = 0;
}
start = color;
do {
for (nset = 0; nset < nsets; nset++) {
index = clsettoarray(color, nset);
va = ppmap_vaddrs[index];
if (va != NULL) {
#ifdef PPDEBUG
align_hits[color]++;
#endif
if (atomic_cas_ptr(&ppmap_vaddrs[index],
va, NULL) == va) {
hat_memload(kas.a_hat, va, pp,
vprot | HAT_NOSYNC,
HAT_LOAD_LOCK);
return (va);
}
}
}
if (++color == ppmap_pages)
color = 0;
} while (color != start);
#ifdef PPDEBUG
ppalloc_noslot++;
#endif
va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
hat_memload(kas.a_hat, va, pp, vprot | HAT_NOSYNC, HAT_LOAD_LOCK);
return (va);
}
void
ppmapout(caddr_t va)
{
int color, nset, index;
if (va >= kernelheap && va < ekernelheap) {
hat_unload(kas.a_hat, va, PAGESIZE,
(HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK));
vmem_free(heap_arena, va, PAGESIZE);
} else {
color = addr_to_vcolor(va);
ASSERT((cache & CACHE_VAC)? (color < ppmap_pages) : 1);
nset = ((uintptr_t)va >> ppmap_shift) & (nsets - 1);
index = clsettoarray(color, nset);
hat_unload(kas.a_hat, va, PAGESIZE,
(HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK));
ASSERT(ppmap_vaddrs[index] == NULL);
ppmap_vaddrs[index] = va;
}
}
#ifdef DEBUG
#define PP_STAT_ADD(stat) (stat)++
uint_t pload, ploadfail;
uint_t ppzero, ppzero_short;
#else
#define PP_STAT_ADD(stat)
#endif
static caddr_t
pp_load_tlb(processorid_t cpu, caddr_t **pslot, page_t *pp, uint_t prot)
{
struct ppmap_va *ppmap;
tte_t tte;
caddr_t *myslot;
caddr_t va;
long i, start, stride;
int vcolor;
uint_t flags, strict_flag;
PP_STAT_ADD(pload);
ppmap = &ppmap_va[cpu];
va = (caddr_t)(PPMAP_FAST_BASE + (MMU_PAGESIZE * MAXPP_SLOTS) * cpu);
myslot = ppmap->ppmap_slots;
ASSERT(addr_to_vcolor(va) == 0);
if (prot & TTE_HWWR_INT) {
flags = PPAGE_STORE_VCOLORING | PPAGE_STORES_POLLUTE;
strict_flag = PPAGE_STORES_POLLUTE;
} else {
flags = PPAGE_LOAD_VCOLORING | PPAGE_LOADS_POLLUTE;
strict_flag = PPAGE_LOADS_POLLUTE;
}
if (pp_consistent_coloring & flags) {
vcolor = sfmmu_get_ppvcolor(pp);
if ((vcolor == -1) &&
(pp_consistent_coloring & strict_flag))
return (NULL);
} else {
vcolor = -1;
}
if (vcolor != -1) {
va += MMU_PAGESIZE * vcolor;
start = vcolor;
stride = ppmap_pages;
myslot += vcolor;
} else {
start = 0;
stride = 1;
}
for (i = start; i < pp_slots; i += stride) {
if (*myslot == NULL) {
if (atomic_cas_ptr(myslot, NULL, va) == NULL)
break;
}
myslot += stride;
va += MMU_PAGESIZE * stride;
}
if (i >= pp_slots) {
PP_STAT_ADD(ploadfail);
return (NULL);
}
ASSERT(vcolor == -1 || addr_to_vcolor(va) == vcolor);
tte.tte_inthi = TTE_VALID_INT | TTE_PFN_INTHI(pp->p_pagenum);
tte.tte_intlo = TTE_PFN_INTLO(pp->p_pagenum) | TTE_CP_INT |
TTE_CV_INT | TTE_PRIV_INT | TTE_LCK_INT | prot;
ASSERT(CPU->cpu_id == cpu);
sfmmu_dtlb_ld_kva(va, &tte);
*pslot = myslot;
return (va);
}
static void
pp_unload_tlb(caddr_t *pslot, caddr_t va)
{
ASSERT(*pslot == va);
vtag_flushpage(va, (uint64_t)ksfmmup);
*pslot = NULL;
}
int
ppcopy_common(page_t *fm_pp, page_t *to_pp)
{
caddr_t fm_va, to_va;
caddr_t *fm_slot, *to_slot;
processorid_t cpu;
label_t ljb;
int ret = 1;
ASSERT(fm_pp != NULL && PAGE_LOCKED(fm_pp));
ASSERT(to_pp != NULL && PAGE_LOCKED(to_pp));
if (!use_hw_bcopy && (cache & CACHE_VAC))
return (0);
kpreempt_disable();
cpu = CPU->cpu_id;
fm_va = pp_load_tlb(cpu, &fm_slot, fm_pp, 0);
if (fm_va == NULL) {
kpreempt_enable();
return (0);
}
to_va = pp_load_tlb(cpu, &to_slot, to_pp, TTE_HWWR_INT);
if (to_va == NULL) {
pp_unload_tlb(fm_slot, fm_va);
kpreempt_enable();
return (0);
}
if (on_fault(&ljb)) {
ret = 0;
goto faulted;
}
hwblkpagecopy(fm_va, to_va);
no_fault();
faulted:
ASSERT(CPU->cpu_id == cpu);
pp_unload_tlb(fm_slot, fm_va);
pp_unload_tlb(to_slot, to_va);
kpreempt_enable();
return (ret);
}
void
ppcopy_kernel__relocatable(page_t *fm_pp, page_t *to_pp)
{
uint64_t fm_pa, to_pa;
size_t nbytes;
fm_pa = (uint64_t)(fm_pp->p_pagenum) << MMU_PAGESHIFT;
to_pa = (uint64_t)(to_pp->p_pagenum) << MMU_PAGESHIFT;
nbytes = MMU_PAGESIZE;
for (; nbytes > 0; fm_pa += 32, to_pa += 32, nbytes -= 32)
hw_pa_bcopy32(fm_pa, to_pa);
}
int
ppcopy(page_t *fm_pp, page_t *to_pp)
{
caddr_t fm_va, to_va;
label_t ljb;
int ret = 1;
boolean_t use_kpm = B_FALSE;
if (ppcopy_common(fm_pp, to_pp))
return (1);
if (kpm_enable) {
if (curthread == kcage_cageout_thread)
use_kpm = B_TRUE;
}
if (use_kpm) {
if ((fm_va = hat_kpm_mapin(fm_pp, NULL)) == NULL ||
(to_va = hat_kpm_mapin(to_pp, NULL)) == NULL) {
if (fm_va != NULL)
hat_kpm_mapout(fm_pp, NULL, fm_va);
use_kpm = B_FALSE;
}
}
if (use_kpm == B_FALSE) {
fm_va = ppmapin(fm_pp, PROT_READ, (caddr_t)-1);
to_va = ppmapin(to_pp, PROT_READ | PROT_WRITE, fm_va);
if (on_fault(&ljb)) {
ret = 0;
goto faulted;
}
}
bcopy(fm_va, to_va, PAGESIZE);
no_fault();
faulted:
if (use_kpm == B_TRUE) {
hat_kpm_mapout(fm_pp, NULL, fm_va);
hat_kpm_mapout(to_pp, NULL, to_va);
} else {
ppmapout(fm_va);
ppmapout(to_va);
}
return (ret);
}
void
pagezero(page_t *pp, uint_t off, uint_t len)
{
caddr_t va;
caddr_t *slot;
int fast = 1;
processorid_t cpu;
extern int hwblkclr(void *, size_t);
extern int use_hw_bzero;
ASSERT((int)len > 0 && (int)off >= 0 && off + len <= PAGESIZE);
ASSERT(PAGE_LOCKED(pp));
PP_STAT_ADD(ppzero);
if (len != MMU_PAGESIZE || !use_hw_bzero) {
fast = 0;
va = NULL;
PP_STAT_ADD(ppzero_short);
}
kpreempt_disable();
if (fast) {
cpu = CPU->cpu_id;
va = pp_load_tlb(cpu, &slot, pp, TTE_HWWR_INT);
}
if (va == NULL) {
va = ppmapin(pp, PROT_READ | PROT_WRITE, (caddr_t)-1);
fast = 0;
}
if (hwblkclr(va + off, len)) {
ASSERT(fast == 0);
sync_icache(va + off, len);
} else {
doflush(va);
}
if (fast) {
ASSERT(CPU->cpu_id == cpu);
pp_unload_tlb(slot, va);
} else {
ppmapout(va);
}
kpreempt_enable();
}