xref: /onnv-gate/usr/src/uts/sun4u/vm/mach_kpm.c (revision 7393:8a8d73648b44)
12296Sae112802 /*
22296Sae112802  * CDDL HEADER START
32296Sae112802  *
42296Sae112802  * The contents of this file are subject to the terms of the
52296Sae112802  * Common Development and Distribution License (the "License").
62296Sae112802  * You may not use this file except in compliance with the License.
72296Sae112802  *
82296Sae112802  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92296Sae112802  * or http://www.opensolaris.org/os/licensing.
102296Sae112802  * See the License for the specific language governing permissions
112296Sae112802  * and limitations under the License.
122296Sae112802  *
132296Sae112802  * When distributing Covered Code, include this CDDL HEADER in each
142296Sae112802  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152296Sae112802  * If applicable, add the following below this CDDL HEADER, with the
162296Sae112802  * fields enclosed by brackets "[]" replaced with your own identifying
172296Sae112802  * information: Portions Copyright [yyyy] [name of copyright owner]
182296Sae112802  *
192296Sae112802  * CDDL HEADER END
202296Sae112802  */
212296Sae112802 /*
22*7393SDonghai.Qiao@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232296Sae112802  * Use is subject to license terms.
242296Sae112802  */
252296Sae112802 
262296Sae112802 /*
272296Sae112802  * Kernel Physical Mapping (segkpm) hat interface routines for sun4u.
282296Sae112802  */
292296Sae112802 
302296Sae112802 #include <sys/types.h>
312296Sae112802 #include <vm/hat.h>
322296Sae112802 #include <vm/hat_sfmmu.h>
332296Sae112802 #include <vm/page.h>
342296Sae112802 #include <sys/sysmacros.h>
352296Sae112802 #include <sys/cmn_err.h>
362296Sae112802 #include <sys/machsystm.h>
372296Sae112802 #include <vm/seg_kpm.h>
382296Sae112802 #include <sys/cpu_module.h>
392296Sae112802 #include <vm/mach_kpm.h>
402296Sae112802 
412296Sae112802 /* kpm prototypes */
422296Sae112802 static caddr_t	sfmmu_kpm_mapin(page_t *);
432296Sae112802 static void	sfmmu_kpm_mapout(page_t *, caddr_t);
442296Sae112802 static int	sfmmu_kpme_lookup(struct kpme *, page_t *);
452296Sae112802 static void	sfmmu_kpme_add(struct kpme *, page_t *);
462296Sae112802 static void	sfmmu_kpme_sub(struct kpme *, page_t *);
472296Sae112802 static caddr_t	sfmmu_kpm_getvaddr(page_t *, int *);
482296Sae112802 static int	sfmmu_kpm_fault(caddr_t, struct memseg *, page_t *);
492296Sae112802 static int	sfmmu_kpm_fault_small(caddr_t, struct memseg *, page_t *);
502296Sae112802 static void	sfmmu_kpm_vac_conflict(page_t *, caddr_t);
512296Sae112802 void	sfmmu_kpm_pageunload(page_t *);
522296Sae112802 void	sfmmu_kpm_vac_unload(page_t *, caddr_t);
532296Sae112802 static void	sfmmu_kpm_demap_large(caddr_t);
542296Sae112802 static void	sfmmu_kpm_demap_small(caddr_t);
552296Sae112802 static void	sfmmu_kpm_demap_tlbs(caddr_t);
562296Sae112802 void	sfmmu_kpm_hme_unload(page_t *);
572296Sae112802 kpm_hlk_t *sfmmu_kpm_kpmp_enter(page_t *, pgcnt_t);
582296Sae112802 void	sfmmu_kpm_kpmp_exit(kpm_hlk_t *kpmp);
592296Sae112802 void	sfmmu_kpm_page_cache(page_t *, int, int);
602296Sae112802 
612296Sae112802 /*
622296Sae112802  * Kernel Physical Mapping (kpm) facility
632296Sae112802  */
642296Sae112802 
652296Sae112802 void
662296Sae112802 mach_kpm_init()
672296Sae112802 {}
682296Sae112802 
692296Sae112802 /* -- hat_kpm interface section -- */
702296Sae112802 
712296Sae112802 /*
722296Sae112802  * Mapin a locked page and return the vaddr.
732296Sae112802  * When a kpme is provided by the caller it is added to
742296Sae112802  * the page p_kpmelist. The page to be mapped in must
752296Sae112802  * be at least read locked (p_selock).
762296Sae112802  */
772296Sae112802 caddr_t
782296Sae112802 hat_kpm_mapin(struct page *pp, struct kpme *kpme)
792296Sae112802 {
802296Sae112802 	kmutex_t	*pml;
812296Sae112802 	caddr_t		vaddr;
822296Sae112802 
832296Sae112802 	if (kpm_enable == 0) {
842296Sae112802 		cmn_err(CE_WARN, "hat_kpm_mapin: kpm_enable not set");
852296Sae112802 		return ((caddr_t)NULL);
862296Sae112802 	}
872296Sae112802 
882296Sae112802 	if (pp == NULL || PAGE_LOCKED(pp) == 0) {
892296Sae112802 		cmn_err(CE_WARN, "hat_kpm_mapin: pp zero or not locked");
902296Sae112802 		return ((caddr_t)NULL);
912296Sae112802 	}
922296Sae112802 
932296Sae112802 	pml = sfmmu_mlist_enter(pp);
942296Sae112802 	ASSERT(pp->p_kpmref >= 0);
952296Sae112802 
962296Sae112802 	vaddr = (pp->p_kpmref == 0) ?
97*7393SDonghai.Qiao@Sun.COM 	    sfmmu_kpm_mapin(pp) : hat_kpm_page2va(pp, 1);
982296Sae112802 
992296Sae112802 	if (kpme != NULL) {
1002296Sae112802 		/*
1012296Sae112802 		 * Tolerate multiple mapins for the same kpme to avoid
1022296Sae112802 		 * the need for an extra serialization.
1032296Sae112802 		 */
1042296Sae112802 		if ((sfmmu_kpme_lookup(kpme, pp)) == 0)
1052296Sae112802 			sfmmu_kpme_add(kpme, pp);
1062296Sae112802 
1072296Sae112802 		ASSERT(pp->p_kpmref > 0);
1082296Sae112802 
1092296Sae112802 	} else {
1102296Sae112802 		pp->p_kpmref++;
1112296Sae112802 	}
1122296Sae112802 
1132296Sae112802 	sfmmu_mlist_exit(pml);
1142296Sae112802 	return (vaddr);
1152296Sae112802 }
1162296Sae112802 
1172296Sae112802 /*
1182296Sae112802  * Mapout a locked page.
1192296Sae112802  * When a kpme is provided by the caller it is removed from
1202296Sae112802  * the page p_kpmelist. The page to be mapped out must be at
1212296Sae112802  * least read locked (p_selock).
1222296Sae112802  * Note: The seg_kpm layer provides a mapout interface for the
1232296Sae112802  * case that a kpme is used and the underlying page is unlocked.
1242296Sae112802  * This can be used instead of calling this function directly.
1252296Sae112802  */
1262296Sae112802 void
1272296Sae112802 hat_kpm_mapout(struct page *pp, struct kpme *kpme, caddr_t vaddr)
1282296Sae112802 {
1292296Sae112802 	kmutex_t	*pml;
1302296Sae112802 
1312296Sae112802 	if (kpm_enable == 0) {
1322296Sae112802 		cmn_err(CE_WARN, "hat_kpm_mapout: kpm_enable not set");
1332296Sae112802 		return;
1342296Sae112802 	}
1352296Sae112802 
1362296Sae112802 	if (IS_KPM_ADDR(vaddr) == 0) {
1372296Sae112802 		cmn_err(CE_WARN, "hat_kpm_mapout: no kpm address");
1382296Sae112802 		return;
1392296Sae112802 	}
1402296Sae112802 
1412296Sae112802 	if (pp == NULL || PAGE_LOCKED(pp) == 0) {
1422296Sae112802 		cmn_err(CE_WARN, "hat_kpm_mapout: page zero or not locked");
1432296Sae112802 		return;
1442296Sae112802 	}
1452296Sae112802 
1462296Sae112802 	if (kpme != NULL) {
1472296Sae112802 		ASSERT(pp == kpme->kpe_page);
1482296Sae112802 		pp = kpme->kpe_page;
1492296Sae112802 		pml = sfmmu_mlist_enter(pp);
1502296Sae112802 
1512296Sae112802 		if (sfmmu_kpme_lookup(kpme, pp) == 0)
1522296Sae112802 			panic("hat_kpm_mapout: kpme not found pp=%p",
153*7393SDonghai.Qiao@Sun.COM 			    (void *)pp);
1542296Sae112802 
1552296Sae112802 		ASSERT(pp->p_kpmref > 0);
1562296Sae112802 		sfmmu_kpme_sub(kpme, pp);
1572296Sae112802 
1582296Sae112802 	} else {
1592296Sae112802 		pml = sfmmu_mlist_enter(pp);
1602296Sae112802 		pp->p_kpmref--;
1612296Sae112802 	}
1622296Sae112802 
1632296Sae112802 	ASSERT(pp->p_kpmref >= 0);
1642296Sae112802 	if (pp->p_kpmref == 0)
1652296Sae112802 		sfmmu_kpm_mapout(pp, vaddr);
1662296Sae112802 
1672296Sae112802 	sfmmu_mlist_exit(pml);
1682296Sae112802 }
1692296Sae112802 
1702296Sae112802 /*
1712296Sae112802  * Return the kpm virtual address for the page at pp.
1722296Sae112802  * If checkswap is non zero and the page is backed by a
1732296Sae112802  * swap vnode the physical address is used rather than
1742296Sae112802  * p_offset to determine the kpm region.
1752296Sae112802  * Note: The function has to be used w/ extreme care. The
1762296Sae112802  * stability of the page identity is in the responsibility
1772296Sae112802  * of the caller.
1782296Sae112802  */
1792296Sae112802 /*ARGSUSED*/
1802296Sae112802 caddr_t
1812296Sae112802 hat_kpm_page2va(struct page *pp, int checkswap)
1822296Sae112802 {
1832296Sae112802 	int		vcolor, vcolor_pa;
1842296Sae112802 	uintptr_t	paddr, vaddr;
1852296Sae112802 
1862296Sae112802 	ASSERT(kpm_enable);
1872296Sae112802 
1882296Sae112802 	paddr = ptob(pp->p_pagenum);
1892296Sae112802 	vcolor_pa = addr_to_vcolor(paddr);
1902296Sae112802 
1912296Sae112802 	if (checkswap && pp->p_vnode && IS_SWAPFSVP(pp->p_vnode))
1922296Sae112802 		vcolor = (PP_ISNC(pp)) ? vcolor_pa : PP_GET_VCOLOR(pp);
1932296Sae112802 	else
1942296Sae112802 		vcolor = addr_to_vcolor(pp->p_offset);
1952296Sae112802 
1962296Sae112802 	vaddr = (uintptr_t)kpm_vbase + paddr;
1972296Sae112802 
1982296Sae112802 	if (vcolor_pa != vcolor) {
1992296Sae112802 		vaddr += ((uintptr_t)(vcolor - vcolor_pa) << MMU_PAGESHIFT);
2002296Sae112802 		vaddr += (vcolor_pa > vcolor) ?
201*7393SDonghai.Qiao@Sun.COM 		    ((uintptr_t)vcolor_pa << kpm_size_shift) :
202*7393SDonghai.Qiao@Sun.COM 		    ((uintptr_t)(vcolor - vcolor_pa) << kpm_size_shift);
2032296Sae112802 	}
2042296Sae112802 
2052296Sae112802 	return ((caddr_t)vaddr);
2062296Sae112802 }
2072296Sae112802 
2082296Sae112802 /*
2092296Sae112802  * Return the page for the kpm virtual address vaddr.
2102296Sae112802  * Caller is responsible for the kpm mapping and lock
2112296Sae112802  * state of the page.
2122296Sae112802  */
2132296Sae112802 page_t *
2142296Sae112802 hat_kpm_vaddr2page(caddr_t vaddr)
2152296Sae112802 {
2162296Sae112802 	uintptr_t	paddr;
2172296Sae112802 	pfn_t		pfn;
2182296Sae112802 
2192296Sae112802 	ASSERT(IS_KPM_ADDR(vaddr));
2202296Sae112802 
2212296Sae112802 	SFMMU_KPM_VTOP(vaddr, paddr);
2222296Sae112802 	pfn = (pfn_t)btop(paddr);
2232296Sae112802 
2242296Sae112802 	return (page_numtopp_nolock(pfn));
2252296Sae112802 }
2262296Sae112802 
2272296Sae112802 /* page to kpm_page */
2282296Sae112802 #define	PP2KPMPG(pp, kp) {						\
2292296Sae112802 	struct memseg	*mseg;						\
2302296Sae112802 	pgcnt_t		inx;						\
2312296Sae112802 	pfn_t		pfn;						\
2322296Sae112802 									\
2332296Sae112802 	pfn = pp->p_pagenum;						\
2342296Sae112802 	mseg = page_numtomemseg_nolock(pfn);				\
2352296Sae112802 	ASSERT(mseg);							\
2362296Sae112802 	inx = ptokpmp(kpmptop(ptokpmp(pfn)) - mseg->kpm_pbase);		\
2372296Sae112802 	ASSERT(inx < mseg->kpm_nkpmpgs);				\
2382296Sae112802 	kp = &mseg->kpm_pages[inx];					\
2392296Sae112802 }
2402296Sae112802 
2412296Sae112802 /* page to kpm_spage */
2422296Sae112802 #define	PP2KPMSPG(pp, ksp) {						\
2432296Sae112802 	struct memseg	*mseg;						\
2442296Sae112802 	pgcnt_t		inx;						\
2452296Sae112802 	pfn_t		pfn;						\
2462296Sae112802 									\
2472296Sae112802 	pfn = pp->p_pagenum;						\
2482296Sae112802 	mseg = page_numtomemseg_nolock(pfn);				\
2492296Sae112802 	ASSERT(mseg);							\
2502296Sae112802 	inx = pfn - mseg->kpm_pbase;					\
2512296Sae112802 	ksp = &mseg->kpm_spages[inx];					\
2522296Sae112802 }
2532296Sae112802 
2542296Sae112802 /*
2552296Sae112802  * hat_kpm_fault is called from segkpm_fault when a kpm tsbmiss occurred
2562296Sae112802  * which could not be resolved by the trap level tsbmiss handler for the
2572296Sae112802  * following reasons:
2582296Sae112802  * . The vaddr is in VAC alias range (always PAGESIZE mapping size).
2592296Sae112802  * . The kpm (s)page range of vaddr is in a VAC alias prevention state.
2602296Sae112802  * . tsbmiss handling at trap level is not desired (DEBUG kernel only,
2612296Sae112802  *   kpm_tsbmtl == 0).
2622296Sae112802  */
2632296Sae112802 int
2642296Sae112802 hat_kpm_fault(struct hat *hat, caddr_t vaddr)
2652296Sae112802 {
2662296Sae112802 	int		error;
2672296Sae112802 	uintptr_t	paddr;
2682296Sae112802 	pfn_t		pfn;
2692296Sae112802 	struct memseg	*mseg;
2702296Sae112802 	page_t	*pp;
2712296Sae112802 
2722296Sae112802 	if (kpm_enable == 0) {
2732296Sae112802 		cmn_err(CE_WARN, "hat_kpm_fault: kpm_enable not set");
2742296Sae112802 		return (ENOTSUP);
2752296Sae112802 	}
2762296Sae112802 
2772296Sae112802 	ASSERT(hat == ksfmmup);
2782296Sae112802 	ASSERT(IS_KPM_ADDR(vaddr));
2792296Sae112802 
2802296Sae112802 	SFMMU_KPM_VTOP(vaddr, paddr);
2812296Sae112802 	pfn = (pfn_t)btop(paddr);
2822296Sae112802 	mseg = page_numtomemseg_nolock(pfn);
2832296Sae112802 	if (mseg == NULL)
2842296Sae112802 		return (EFAULT);
2852296Sae112802 
2862296Sae112802 	pp = &mseg->pages[(pgcnt_t)(pfn - mseg->pages_base)];
2872296Sae112802 	ASSERT((pfn_t)pp->p_pagenum == pfn);
2882296Sae112802 
2892296Sae112802 	if (!PAGE_LOCKED(pp))
2902296Sae112802 		return (EFAULT);
2912296Sae112802 
2922296Sae112802 	if (kpm_smallpages == 0)
2932296Sae112802 		error = sfmmu_kpm_fault(vaddr, mseg, pp);
2942296Sae112802 	else
2952296Sae112802 		error = sfmmu_kpm_fault_small(vaddr, mseg, pp);
2962296Sae112802 
2972296Sae112802 	return (error);
2982296Sae112802 }
2992296Sae112802 
3002296Sae112802 /*
3012296Sae112802  * memseg_hash[] was cleared, need to clear memseg_phash[] too.
3022296Sae112802  */
3032296Sae112802 void
3042296Sae112802 hat_kpm_mseghash_clear(int nentries)
3052296Sae112802 {
3062296Sae112802 	pgcnt_t i;
3072296Sae112802 
3082296Sae112802 	if (kpm_enable == 0)
3092296Sae112802 		return;
3102296Sae112802 
3112296Sae112802 	for (i = 0; i < nentries; i++)
3122296Sae112802 		memseg_phash[i] = MSEG_NULLPTR_PA;
3132296Sae112802 }
3142296Sae112802 
3152296Sae112802 /*
3162296Sae112802  * Update memseg_phash[inx] when memseg_hash[inx] was changed.
3172296Sae112802  */
3182296Sae112802 void
3192296Sae112802 hat_kpm_mseghash_update(pgcnt_t inx, struct memseg *msp)
3202296Sae112802 {
3212296Sae112802 	if (kpm_enable == 0)
3222296Sae112802 		return;
3232296Sae112802 
3242296Sae112802 	memseg_phash[inx] = (msp) ? va_to_pa(msp) : MSEG_NULLPTR_PA;
3252296Sae112802 }
3262296Sae112802 
3272296Sae112802 /*
3282296Sae112802  * Update kpm memseg members from basic memseg info.
3292296Sae112802  */
3302296Sae112802 void
3312296Sae112802 hat_kpm_addmem_mseg_update(struct memseg *msp, pgcnt_t nkpmpgs,
3322296Sae112802 	offset_t kpm_pages_off)
3332296Sae112802 {
3342296Sae112802 	if (kpm_enable == 0)
3352296Sae112802 		return;
3362296Sae112802 
3372296Sae112802 	msp->kpm_pages = (kpm_page_t *)((caddr_t)msp->pages + kpm_pages_off);
3382296Sae112802 	msp->kpm_nkpmpgs = nkpmpgs;
3392296Sae112802 	msp->kpm_pbase = kpmptop(ptokpmp(msp->pages_base));
3402296Sae112802 	msp->pagespa = va_to_pa(msp->pages);
3412296Sae112802 	msp->epagespa = va_to_pa(msp->epages);
3422296Sae112802 	msp->kpm_pagespa = va_to_pa(msp->kpm_pages);
3432296Sae112802 }
3442296Sae112802 
3452296Sae112802 /*
3462296Sae112802  * Setup nextpa when a memseg is inserted.
3472296Sae112802  * Assumes that the memsegslock is already held.
3482296Sae112802  */
3492296Sae112802 void
3502296Sae112802 hat_kpm_addmem_mseg_insert(struct memseg *msp)
3512296Sae112802 {
3522296Sae112802 	if (kpm_enable == 0)
3532296Sae112802 		return;
3542296Sae112802 
3553446Smrj 	ASSERT(memsegs_lock_held());
3562296Sae112802 	msp->nextpa = (memsegs) ? va_to_pa(memsegs) : MSEG_NULLPTR_PA;
3572296Sae112802 }
3582296Sae112802 
3592296Sae112802 /*
3602296Sae112802  * Setup memsegspa when a memseg is (head) inserted.
3612296Sae112802  * Called before memsegs is updated to complete a
3622296Sae112802  * memseg insert operation.
3632296Sae112802  * Assumes that the memsegslock is already held.
3642296Sae112802  */
3652296Sae112802 void
3662296Sae112802 hat_kpm_addmem_memsegs_update(struct memseg *msp)
3672296Sae112802 {
3682296Sae112802 	if (kpm_enable == 0)
3692296Sae112802 		return;
3702296Sae112802 
3713446Smrj 	ASSERT(memsegs_lock_held());
3722296Sae112802 	ASSERT(memsegs);
3732296Sae112802 	memsegspa = va_to_pa(msp);
3742296Sae112802 }
3752296Sae112802 
3762296Sae112802 /*
3772296Sae112802  * Return end of metadata for an already setup memseg.
3782296Sae112802  *
3792296Sae112802  * Note: kpm_pages and kpm_spages are aliases and the underlying
3802296Sae112802  * member of struct memseg is a union, therefore they always have
3812296Sae112802  * the same address within a memseg. They must be differentiated
3822296Sae112802  * when pointer arithmetic is used with them.
3832296Sae112802  */
3842296Sae112802 caddr_t
3852296Sae112802 hat_kpm_mseg_reuse(struct memseg *msp)
3862296Sae112802 {
3872296Sae112802 	caddr_t end;
3882296Sae112802 
3892296Sae112802 	if (kpm_smallpages == 0)
3902296Sae112802 		end = (caddr_t)(msp->kpm_pages + msp->kpm_nkpmpgs);
3912296Sae112802 	else
3922296Sae112802 		end = (caddr_t)(msp->kpm_spages + msp->kpm_nkpmpgs);
3932296Sae112802 
3942296Sae112802 	return (end);
3952296Sae112802 }
3962296Sae112802 
3972296Sae112802 /*
3982296Sae112802  * Update memsegspa (when first memseg in list
3992296Sae112802  * is deleted) or nextpa  when a memseg deleted.
4002296Sae112802  * Assumes that the memsegslock is already held.
4012296Sae112802  */
4022296Sae112802 void
4032296Sae112802 hat_kpm_delmem_mseg_update(struct memseg *msp, struct memseg **mspp)
4042296Sae112802 {
4052296Sae112802 	struct memseg *lmsp;
4062296Sae112802 
4072296Sae112802 	if (kpm_enable == 0)
4082296Sae112802 		return;
4092296Sae112802 
4103446Smrj 	ASSERT(memsegs_lock_held());
4112296Sae112802 
4122296Sae112802 	if (mspp == &memsegs) {
4132296Sae112802 		memsegspa = (msp->next) ?
414*7393SDonghai.Qiao@Sun.COM 		    va_to_pa(msp->next) : MSEG_NULLPTR_PA;
4152296Sae112802 	} else {
4162296Sae112802 		lmsp = (struct memseg *)
417*7393SDonghai.Qiao@Sun.COM 		    ((uint64_t)mspp - offsetof(struct memseg, next));
4182296Sae112802 		lmsp->nextpa = (msp->next) ?
419*7393SDonghai.Qiao@Sun.COM 		    va_to_pa(msp->next) : MSEG_NULLPTR_PA;
4202296Sae112802 	}
4212296Sae112802 }
4222296Sae112802 
4232296Sae112802 /*
4242296Sae112802  * Update kpm members for all memseg's involved in a split operation
4252296Sae112802  * and do the atomic update of the physical memseg chain.
4262296Sae112802  *
4272296Sae112802  * Note: kpm_pages and kpm_spages are aliases and the underlying member
4282296Sae112802  * of struct memseg is a union, therefore they always have the same
4292296Sae112802  * address within a memseg. With that the direct assignments and
4302296Sae112802  * va_to_pa conversions below don't have to be distinguished wrt. to
4312296Sae112802  * kpm_smallpages. They must be differentiated when pointer arithmetic
4322296Sae112802  * is used with them.
4332296Sae112802  *
4342296Sae112802  * Assumes that the memsegslock is already held.
4352296Sae112802  */
4362296Sae112802 void
4372296Sae112802 hat_kpm_split_mseg_update(struct memseg *msp, struct memseg **mspp,
4382296Sae112802 	struct memseg *lo, struct memseg *mid, struct memseg *hi)
4392296Sae112802 {
4402296Sae112802 	pgcnt_t start, end, kbase, kstart, num;
4412296Sae112802 	struct memseg *lmsp;
4422296Sae112802 
4432296Sae112802 	if (kpm_enable == 0)
4442296Sae112802 		return;
4452296Sae112802 
4463446Smrj 	ASSERT(memsegs_lock_held());
4472296Sae112802 	ASSERT(msp && mid && msp->kpm_pages);
4482296Sae112802 
4492296Sae112802 	kbase = ptokpmp(msp->kpm_pbase);
4502296Sae112802 
4512296Sae112802 	if (lo) {
4522296Sae112802 		num = lo->pages_end - lo->pages_base;
4532296Sae112802 		start = kpmptop(ptokpmp(lo->pages_base));
4542296Sae112802 		/* align end to kpm page size granularity */
4552296Sae112802 		end = kpmptop(ptokpmp(start + num - 1)) + kpmpnpgs;
4562296Sae112802 		lo->kpm_pbase = start;
4572296Sae112802 		lo->kpm_nkpmpgs = ptokpmp(end - start);
4582296Sae112802 		lo->kpm_pages = msp->kpm_pages;
4592296Sae112802 		lo->kpm_pagespa = va_to_pa(lo->kpm_pages);
4602296Sae112802 		lo->pagespa = va_to_pa(lo->pages);
4612296Sae112802 		lo->epagespa = va_to_pa(lo->epages);
4622296Sae112802 		lo->nextpa = va_to_pa(lo->next);
4632296Sae112802 	}
4642296Sae112802 
4652296Sae112802 	/* mid */
4662296Sae112802 	num = mid->pages_end - mid->pages_base;
4672296Sae112802 	kstart = ptokpmp(mid->pages_base);
4682296Sae112802 	start = kpmptop(kstart);
4692296Sae112802 	/* align end to kpm page size granularity */
4702296Sae112802 	end = kpmptop(ptokpmp(start + num - 1)) + kpmpnpgs;
4712296Sae112802 	mid->kpm_pbase = start;
4722296Sae112802 	mid->kpm_nkpmpgs = ptokpmp(end - start);
4732296Sae112802 	if (kpm_smallpages == 0) {
4742296Sae112802 		mid->kpm_pages = msp->kpm_pages + (kstart - kbase);
4752296Sae112802 	} else {
4762296Sae112802 		mid->kpm_spages = msp->kpm_spages + (kstart - kbase);
4772296Sae112802 	}
4782296Sae112802 	mid->kpm_pagespa = va_to_pa(mid->kpm_pages);
4792296Sae112802 	mid->pagespa = va_to_pa(mid->pages);
4802296Sae112802 	mid->epagespa = va_to_pa(mid->epages);
4812296Sae112802 	mid->nextpa = (mid->next) ?  va_to_pa(mid->next) : MSEG_NULLPTR_PA;
4822296Sae112802 
4832296Sae112802 	if (hi) {
4842296Sae112802 		num = hi->pages_end - hi->pages_base;
4852296Sae112802 		kstart = ptokpmp(hi->pages_base);
4862296Sae112802 		start = kpmptop(kstart);
4872296Sae112802 		/* align end to kpm page size granularity */
4882296Sae112802 		end = kpmptop(ptokpmp(start + num - 1)) + kpmpnpgs;
4892296Sae112802 		hi->kpm_pbase = start;
4902296Sae112802 		hi->kpm_nkpmpgs = ptokpmp(end - start);
4912296Sae112802 		if (kpm_smallpages == 0) {
4922296Sae112802 			hi->kpm_pages = msp->kpm_pages + (kstart - kbase);
4932296Sae112802 		} else {
4942296Sae112802 			hi->kpm_spages = msp->kpm_spages + (kstart - kbase);
4952296Sae112802 		}
4962296Sae112802 		hi->kpm_pagespa = va_to_pa(hi->kpm_pages);
4972296Sae112802 		hi->pagespa = va_to_pa(hi->pages);
4982296Sae112802 		hi->epagespa = va_to_pa(hi->epages);
4992296Sae112802 		hi->nextpa = (hi->next) ? va_to_pa(hi->next) : MSEG_NULLPTR_PA;
5002296Sae112802 	}
5012296Sae112802 
5022296Sae112802 	/*
5032296Sae112802 	 * Atomic update of the physical memseg chain
5042296Sae112802 	 */
5052296Sae112802 	if (mspp == &memsegs) {
5062296Sae112802 		memsegspa = (lo) ? va_to_pa(lo) : va_to_pa(mid);
5072296Sae112802 	} else {
5082296Sae112802 		lmsp = (struct memseg *)
509*7393SDonghai.Qiao@Sun.COM 		    ((uint64_t)mspp - offsetof(struct memseg, next));
5102296Sae112802 		lmsp->nextpa = (lo) ? va_to_pa(lo) : va_to_pa(mid);
5112296Sae112802 	}
5122296Sae112802 }
5132296Sae112802 
5142296Sae112802 /*
5152296Sae112802  * Walk the memsegs chain, applying func to each memseg span and vcolor.
5162296Sae112802  */
5172296Sae112802 void
5182296Sae112802 hat_kpm_walk(void (*func)(void *, void *, size_t), void *arg)
5192296Sae112802 {
5202296Sae112802 	pfn_t	pbase, pend;
5212296Sae112802 	int	vcolor;
5222296Sae112802 	void	*base;
5232296Sae112802 	size_t	size;
5242296Sae112802 	struct memseg *msp;
5252296Sae112802 	extern uint_t vac_colors;
5262296Sae112802 
5272296Sae112802 	for (msp = memsegs; msp; msp = msp->next) {
5282296Sae112802 		pbase = msp->pages_base;
5292296Sae112802 		pend = msp->pages_end;
5302296Sae112802 		for (vcolor = 0; vcolor < vac_colors; vcolor++) {
5312296Sae112802 			base = ptob(pbase) + kpm_vbase + kpm_size * vcolor;
5322296Sae112802 			size = ptob(pend - pbase);
5332296Sae112802 			func(arg, base, size);
5342296Sae112802 		}
5352296Sae112802 	}
5362296Sae112802 }
5372296Sae112802 
5382296Sae112802 
5392296Sae112802 /* -- sfmmu_kpm internal section -- */
5402296Sae112802 
5412296Sae112802 /*
5422296Sae112802  * Return the page frame number if a valid segkpm mapping exists
5432296Sae112802  * for vaddr, otherwise return PFN_INVALID. No locks are grabbed.
5442296Sae112802  * Should only be used by other sfmmu routines.
5452296Sae112802  */
5462296Sae112802 pfn_t
5472296Sae112802 sfmmu_kpm_vatopfn(caddr_t vaddr)
5482296Sae112802 {
5492296Sae112802 	uintptr_t	paddr;
5502296Sae112802 	pfn_t		pfn;
5512296Sae112802 	page_t	*pp;
5522296Sae112802 
5532296Sae112802 	ASSERT(kpm_enable && IS_KPM_ADDR(vaddr));
5542296Sae112802 
5552296Sae112802 	SFMMU_KPM_VTOP(vaddr, paddr);
5562296Sae112802 	pfn = (pfn_t)btop(paddr);
5572296Sae112802 	pp = page_numtopp_nolock(pfn);
5582296Sae112802 	if (pp && pp->p_kpmref)
5592296Sae112802 		return (pfn);
5602296Sae112802 	else
5612296Sae112802 		return ((pfn_t)PFN_INVALID);
5622296Sae112802 }
5632296Sae112802 
5642296Sae112802 /*
5652296Sae112802  * Lookup a kpme in the p_kpmelist.
5662296Sae112802  */
5672296Sae112802 static int
5682296Sae112802 sfmmu_kpme_lookup(struct kpme *kpme, page_t *pp)
5692296Sae112802 {
5702296Sae112802 	struct kpme	*p;
5712296Sae112802 
5722296Sae112802 	for (p = pp->p_kpmelist; p; p = p->kpe_next) {
5732296Sae112802 		if (p == kpme)
5742296Sae112802 			return (1);
5752296Sae112802 	}
5762296Sae112802 	return (0);
5772296Sae112802 }
5782296Sae112802 
5792296Sae112802 /*
5802296Sae112802  * Insert a kpme into the p_kpmelist and increment
5812296Sae112802  * the per page kpm reference count.
5822296Sae112802  */
5832296Sae112802 static void
5842296Sae112802 sfmmu_kpme_add(struct kpme *kpme, page_t *pp)
5852296Sae112802 {
5862296Sae112802 	ASSERT(pp->p_kpmref >= 0);
5872296Sae112802 
5882296Sae112802 	/* head insert */
5892296Sae112802 	kpme->kpe_prev = NULL;
5902296Sae112802 	kpme->kpe_next = pp->p_kpmelist;
5912296Sae112802 
5922296Sae112802 	if (pp->p_kpmelist)
5932296Sae112802 		pp->p_kpmelist->kpe_prev = kpme;
5942296Sae112802 
5952296Sae112802 	pp->p_kpmelist = kpme;
5962296Sae112802 	kpme->kpe_page = pp;
5972296Sae112802 	pp->p_kpmref++;
5982296Sae112802 }
5992296Sae112802 
6002296Sae112802 /*
6012296Sae112802  * Remove a kpme from the p_kpmelist and decrement
6022296Sae112802  * the per page kpm reference count.
6032296Sae112802  */
6042296Sae112802 static void
6052296Sae112802 sfmmu_kpme_sub(struct kpme *kpme, page_t *pp)
6062296Sae112802 {
6072296Sae112802 	ASSERT(pp->p_kpmref > 0);
6082296Sae112802 
6092296Sae112802 	if (kpme->kpe_prev) {
6102296Sae112802 		ASSERT(pp->p_kpmelist != kpme);
6112296Sae112802 		ASSERT(kpme->kpe_prev->kpe_page == pp);
6122296Sae112802 		kpme->kpe_prev->kpe_next = kpme->kpe_next;
6132296Sae112802 	} else {
6142296Sae112802 		ASSERT(pp->p_kpmelist == kpme);
6152296Sae112802 		pp->p_kpmelist = kpme->kpe_next;
6162296Sae112802 	}
6172296Sae112802 
6182296Sae112802 	if (kpme->kpe_next) {
6192296Sae112802 		ASSERT(kpme->kpe_next->kpe_page == pp);
6202296Sae112802 		kpme->kpe_next->kpe_prev = kpme->kpe_prev;
6212296Sae112802 	}
6222296Sae112802 
6232296Sae112802 	kpme->kpe_next = kpme->kpe_prev = NULL;
6242296Sae112802 	kpme->kpe_page = NULL;
6252296Sae112802 	pp->p_kpmref--;
6262296Sae112802 }
6272296Sae112802 
6282296Sae112802 /*
6292296Sae112802  * Mapin a single page, it is called every time a page changes it's state
6302296Sae112802  * from kpm-unmapped to kpm-mapped. It may not be called, when only a new
6312296Sae112802  * kpm instance does a mapin and wants to share the mapping.
6322296Sae112802  * Assumes that the mlist mutex is already grabbed.
6332296Sae112802  */
6342296Sae112802 static caddr_t
6352296Sae112802 sfmmu_kpm_mapin(page_t *pp)
6362296Sae112802 {
6372296Sae112802 	kpm_page_t	*kp;
6382296Sae112802 	kpm_hlk_t	*kpmp;
6392296Sae112802 	caddr_t		vaddr;
6402296Sae112802 	int		kpm_vac_range;
6412296Sae112802 	pfn_t		pfn;
6422296Sae112802 	tte_t		tte;
6432296Sae112802 	kmutex_t	*pmtx;
6442296Sae112802 	int		uncached;
6452296Sae112802 	kpm_spage_t	*ksp;
6462296Sae112802 	kpm_shlk_t	*kpmsp;
6472296Sae112802 	int		oldval;
6482296Sae112802 
6492296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
6502296Sae112802 	ASSERT(pp->p_kpmref == 0);
6512296Sae112802 
6522296Sae112802 	vaddr = sfmmu_kpm_getvaddr(pp, &kpm_vac_range);
6532296Sae112802 
6542296Sae112802 	ASSERT(IS_KPM_ADDR(vaddr));
6552296Sae112802 	uncached = PP_ISNC(pp);
6562296Sae112802 	pfn = pp->p_pagenum;
6572296Sae112802 
6582296Sae112802 	if (kpm_smallpages)
6592296Sae112802 		goto smallpages_mapin;
6602296Sae112802 
6612296Sae112802 	PP2KPMPG(pp, kp);
6622296Sae112802 
6632296Sae112802 	kpmp = KPMP_HASH(kp);
6642296Sae112802 	mutex_enter(&kpmp->khl_mutex);
6652296Sae112802 
6662296Sae112802 	ASSERT(PP_ISKPMC(pp) == 0);
6672296Sae112802 	ASSERT(PP_ISKPMS(pp) == 0);
6682296Sae112802 
6692296Sae112802 	if (uncached) {
6702296Sae112802 		/* ASSERT(pp->p_share); XXX use hat_page_getshare */
6712296Sae112802 		if (kpm_vac_range == 0) {
6722296Sae112802 			if (kp->kp_refcnts == 0) {
6732296Sae112802 				/*
6742296Sae112802 				 * Must remove large page mapping if it exists.
6752296Sae112802 				 * Pages in uncached state can only be mapped
6762296Sae112802 				 * small (PAGESIZE) within the regular kpm
6772296Sae112802 				 * range.
6782296Sae112802 				 */
6792296Sae112802 				if (kp->kp_refcntc == -1) {
6802296Sae112802 					/* remove go indication */
6812296Sae112802 					sfmmu_kpm_tsbmtl(&kp->kp_refcntc,
682*7393SDonghai.Qiao@Sun.COM 					    &kpmp->khl_lock, KPMTSBM_STOP);
6832296Sae112802 				}
6842296Sae112802 				if (kp->kp_refcnt > 0 && kp->kp_refcntc == 0)
6852296Sae112802 					sfmmu_kpm_demap_large(vaddr);
6862296Sae112802 			}
6872296Sae112802 			ASSERT(kp->kp_refcntc >= 0);
6882296Sae112802 			kp->kp_refcntc++;
6892296Sae112802 		}
6902296Sae112802 		pmtx = sfmmu_page_enter(pp);
6912296Sae112802 		PP_SETKPMC(pp);
6922296Sae112802 		sfmmu_page_exit(pmtx);
6932296Sae112802 	}
6942296Sae112802 
6952296Sae112802 	if ((kp->kp_refcntc > 0 || kp->kp_refcnts > 0) && kpm_vac_range == 0) {
6962296Sae112802 		/*
6972296Sae112802 		 * Have to do a small (PAGESIZE) mapin within this kpm_page
6982296Sae112802 		 * range since it is marked to be in VAC conflict mode or
6992296Sae112802 		 * when there are still other small mappings around.
7002296Sae112802 		 */
7012296Sae112802 
7022296Sae112802 		/* tte assembly */
7032296Sae112802 		if (uncached == 0)
7042296Sae112802 			KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
7052296Sae112802 		else
7062296Sae112802 			KPM_TTE_VUNCACHED(tte.ll, pfn, TTE8K);
7072296Sae112802 
7082296Sae112802 		/* tsb dropin */
7092296Sae112802 		sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
7102296Sae112802 
7112296Sae112802 		pmtx = sfmmu_page_enter(pp);
7122296Sae112802 		PP_SETKPMS(pp);
7132296Sae112802 		sfmmu_page_exit(pmtx);
7142296Sae112802 
7152296Sae112802 		kp->kp_refcnts++;
7162296Sae112802 		ASSERT(kp->kp_refcnts > 0);
7172296Sae112802 		goto exit;
7182296Sae112802 	}
7192296Sae112802 
7202296Sae112802 	if (kpm_vac_range == 0) {
7212296Sae112802 		/*
7222296Sae112802 		 * Fast path / regular case, no VAC conflict handling
7232296Sae112802 		 * in progress within this kpm_page range.
7242296Sae112802 		 */
7252296Sae112802 		if (kp->kp_refcnt == 0) {
7262296Sae112802 
7272296Sae112802 			/* tte assembly */
7282296Sae112802 			KPM_TTE_VCACHED(tte.ll, pfn, TTE4M);
7292296Sae112802 
7302296Sae112802 			/* tsb dropin */
7312296Sae112802 			sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT4M);
7322296Sae112802 
7332296Sae112802 			/* Set go flag for TL tsbmiss handler */
7342296Sae112802 			if (kp->kp_refcntc == 0)
7352296Sae112802 				sfmmu_kpm_tsbmtl(&kp->kp_refcntc,
736*7393SDonghai.Qiao@Sun.COM 				    &kpmp->khl_lock, KPMTSBM_START);
7372296Sae112802 
7382296Sae112802 			ASSERT(kp->kp_refcntc == -1);
7392296Sae112802 		}
7402296Sae112802 		kp->kp_refcnt++;
7412296Sae112802 		ASSERT(kp->kp_refcnt);
7422296Sae112802 
7432296Sae112802 	} else {
7442296Sae112802 		/*
7452296Sae112802 		 * The page is not setup according to the common VAC
7462296Sae112802 		 * prevention rules for the regular and kpm mapping layer
7472296Sae112802 		 * E.g. the page layer was not able to deliver a right
7482296Sae112802 		 * vcolor'ed page for a given vaddr corresponding to
7492296Sae112802 		 * the wanted p_offset. It has to be mapped in small in
7502296Sae112802 		 * within the corresponding kpm vac range in order to
7512296Sae112802 		 * prevent VAC alias conflicts.
7522296Sae112802 		 */
7532296Sae112802 
7542296Sae112802 		/* tte assembly */
7552296Sae112802 		if (uncached == 0) {
7562296Sae112802 			KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
7572296Sae112802 		} else {
7582296Sae112802 			KPM_TTE_VUNCACHED(tte.ll, pfn, TTE8K);
7592296Sae112802 		}
7602296Sae112802 
7612296Sae112802 		/* tsb dropin */
7622296Sae112802 		sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
7632296Sae112802 
7642296Sae112802 		kp->kp_refcnta++;
7652296Sae112802 		if (kp->kp_refcntc == -1) {
7662296Sae112802 			ASSERT(kp->kp_refcnt > 0);
7672296Sae112802 
7682296Sae112802 			/* remove go indication */
7692296Sae112802 			sfmmu_kpm_tsbmtl(&kp->kp_refcntc, &kpmp->khl_lock,
770*7393SDonghai.Qiao@Sun.COM 			    KPMTSBM_STOP);
7712296Sae112802 		}
7722296Sae112802 		ASSERT(kp->kp_refcntc >= 0);
7732296Sae112802 	}
7742296Sae112802 exit:
7752296Sae112802 	mutex_exit(&kpmp->khl_mutex);
7762296Sae112802 	return (vaddr);
7772296Sae112802 
7782296Sae112802 smallpages_mapin:
7792296Sae112802 	if (uncached == 0) {
7802296Sae112802 		/* tte assembly */
7812296Sae112802 		KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
7822296Sae112802 	} else {
783*7393SDonghai.Qiao@Sun.COM 		/*
784*7393SDonghai.Qiao@Sun.COM 		 * Just in case this same page was mapped cacheable prior to
785*7393SDonghai.Qiao@Sun.COM 		 * this and the old tte remains in tlb.
786*7393SDonghai.Qiao@Sun.COM 		 */
787*7393SDonghai.Qiao@Sun.COM 		sfmmu_kpm_demap_small(vaddr);
788*7393SDonghai.Qiao@Sun.COM 
7892296Sae112802 		/* ASSERT(pp->p_share); XXX use hat_page_getshare */
7902296Sae112802 		pmtx = sfmmu_page_enter(pp);
7912296Sae112802 		PP_SETKPMC(pp);
7922296Sae112802 		sfmmu_page_exit(pmtx);
7932296Sae112802 		/* tte assembly */
7942296Sae112802 		KPM_TTE_VUNCACHED(tte.ll, pfn, TTE8K);
7952296Sae112802 	}
7962296Sae112802 
7972296Sae112802 	/* tsb dropin */
7982296Sae112802 	sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
7992296Sae112802 
8002296Sae112802 	PP2KPMSPG(pp, ksp);
8012296Sae112802 	kpmsp = KPMP_SHASH(ksp);
8022296Sae112802 
803*7393SDonghai.Qiao@Sun.COM 	oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag, &kpmsp->kshl_lock,
804*7393SDonghai.Qiao@Sun.COM 	    (uncached) ? (KPM_MAPPED_GO | KPM_MAPPEDSC) :
805*7393SDonghai.Qiao@Sun.COM 	    (KPM_MAPPED_GO | KPM_MAPPEDS));
8062296Sae112802 
8072296Sae112802 	if (oldval != 0)
8082296Sae112802 		panic("sfmmu_kpm_mapin: stale smallpages mapping");
8092296Sae112802 
8102296Sae112802 	return (vaddr);
8112296Sae112802 }
8122296Sae112802 
8132296Sae112802 /*
8142296Sae112802  * Mapout a single page, it is called every time a page changes it's state
8152296Sae112802  * from kpm-mapped to kpm-unmapped. It may not be called, when only a kpm
8162296Sae112802  * instance calls mapout and there are still other instances mapping the
8172296Sae112802  * page. Assumes that the mlist mutex is already grabbed.
8182296Sae112802  *
8192296Sae112802  * Note: In normal mode (no VAC conflict prevention pending) TLB's are
8202296Sae112802  * not flushed. This is the core segkpm behavior to avoid xcalls. It is
8212296Sae112802  * no problem because a translation from a segkpm virtual address to a
8222296Sae112802  * physical address is always the same. The only downside is a slighty
8232296Sae112802  * increased window of vulnerability for misbehaving _kernel_ modules.
8242296Sae112802  */
8252296Sae112802 static void
8262296Sae112802 sfmmu_kpm_mapout(page_t *pp, caddr_t vaddr)
8272296Sae112802 {
8282296Sae112802 	kpm_page_t	*kp;
8292296Sae112802 	kpm_hlk_t	*kpmp;
8302296Sae112802 	int		alias_range;
8312296Sae112802 	kmutex_t	*pmtx;
8322296Sae112802 	kpm_spage_t	*ksp;
8332296Sae112802 	kpm_shlk_t	*kpmsp;
8342296Sae112802 	int		oldval;
8352296Sae112802 
8362296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
8372296Sae112802 	ASSERT(pp->p_kpmref == 0);
8382296Sae112802 
8392296Sae112802 	alias_range = IS_KPM_ALIAS_RANGE(vaddr);
8402296Sae112802 
8412296Sae112802 	if (kpm_smallpages)
8422296Sae112802 		goto smallpages_mapout;
8432296Sae112802 
8442296Sae112802 	PP2KPMPG(pp, kp);
8452296Sae112802 	kpmp = KPMP_HASH(kp);
8462296Sae112802 	mutex_enter(&kpmp->khl_mutex);
8472296Sae112802 
8482296Sae112802 	if (alias_range) {
8492296Sae112802 		ASSERT(PP_ISKPMS(pp) == 0);
8502296Sae112802 		if (kp->kp_refcnta <= 0) {
8512296Sae112802 			panic("sfmmu_kpm_mapout: bad refcnta kp=%p",
852*7393SDonghai.Qiao@Sun.COM 			    (void *)kp);
8532296Sae112802 		}
8542296Sae112802 
8552296Sae112802 		if (PP_ISTNC(pp))  {
8562296Sae112802 			if (PP_ISKPMC(pp) == 0) {
8572296Sae112802 				/*
8582296Sae112802 				 * Uncached kpm mappings must always have
8592296Sae112802 				 * forced "small page" mode.
8602296Sae112802 				 */
8612296Sae112802 				panic("sfmmu_kpm_mapout: uncached page not "
862*7393SDonghai.Qiao@Sun.COM 				    "kpm marked");
8632296Sae112802 			}
8642296Sae112802 			sfmmu_kpm_demap_small(vaddr);
8652296Sae112802 
8662296Sae112802 			pmtx = sfmmu_page_enter(pp);
8672296Sae112802 			PP_CLRKPMC(pp);
8682296Sae112802 			sfmmu_page_exit(pmtx);
8692296Sae112802 
8702296Sae112802 			/*
8712296Sae112802 			 * Check if we can resume cached mode. This might
8722296Sae112802 			 * be the case if the kpm mapping was the only
8732296Sae112802 			 * mapping in conflict with other non rule
8742296Sae112802 			 * compliant mappings. The page is no more marked
8752296Sae112802 			 * as kpm mapped, so the conv_tnc path will not
8762296Sae112802 			 * change kpm state.
8772296Sae112802 			 */
8782296Sae112802 			conv_tnc(pp, TTE8K);
8792296Sae112802 
8802296Sae112802 		} else if (PP_ISKPMC(pp) == 0) {
8812296Sae112802 			/* remove TSB entry only */
8822296Sae112802 			sfmmu_kpm_unload_tsb(vaddr, MMU_PAGESHIFT);
8832296Sae112802 
8842296Sae112802 		} else {
8852296Sae112802 			/* already demapped */
8862296Sae112802 			pmtx = sfmmu_page_enter(pp);
8872296Sae112802 			PP_CLRKPMC(pp);
8882296Sae112802 			sfmmu_page_exit(pmtx);
8892296Sae112802 		}
8902296Sae112802 		kp->kp_refcnta--;
8912296Sae112802 		goto exit;
8922296Sae112802 	}
8932296Sae112802 
8942296Sae112802 	if (kp->kp_refcntc <= 0 && kp->kp_refcnts == 0) {
8952296Sae112802 		/*
8962296Sae112802 		 * Fast path / regular case.
8972296Sae112802 		 */
8982296Sae112802 		ASSERT(kp->kp_refcntc >= -1);
8992296Sae112802 		ASSERT(!(pp->p_nrm & (P_KPMC | P_KPMS | P_TNC | P_PNC)));
9002296Sae112802 
9012296Sae112802 		if (kp->kp_refcnt <= 0)
9022296Sae112802 			panic("sfmmu_kpm_mapout: bad refcnt kp=%p", (void *)kp);
9032296Sae112802 
9042296Sae112802 		if (--kp->kp_refcnt == 0) {
9052296Sae112802 			/* remove go indication */
9062296Sae112802 			if (kp->kp_refcntc == -1) {
9072296Sae112802 				sfmmu_kpm_tsbmtl(&kp->kp_refcntc,
908*7393SDonghai.Qiao@Sun.COM 				    &kpmp->khl_lock, KPMTSBM_STOP);
9092296Sae112802 			}
9102296Sae112802 			ASSERT(kp->kp_refcntc == 0);
9112296Sae112802 
9122296Sae112802 			/* remove TSB entry */
9132296Sae112802 			sfmmu_kpm_unload_tsb(vaddr, MMU_PAGESHIFT4M);
9142296Sae112802 #ifdef	DEBUG
9152296Sae112802 			if (kpm_tlb_flush)
9162296Sae112802 				sfmmu_kpm_demap_tlbs(vaddr);
9172296Sae112802 #endif
9182296Sae112802 		}
9192296Sae112802 
9202296Sae112802 	} else {
9212296Sae112802 		/*
9222296Sae112802 		 * The VAC alias path.
9232296Sae112802 		 * We come here if the kpm vaddr is not in any alias_range
9242296Sae112802 		 * and we are unmapping a page within the regular kpm_page
9252296Sae112802 		 * range. The kpm_page either holds conflict pages and/or
9262296Sae112802 		 * is in "small page" mode. If the page is not marked
9272296Sae112802 		 * P_KPMS it couldn't have a valid PAGESIZE sized TSB
9282296Sae112802 		 * entry. Dcache flushing is done lazy and follows the
9292296Sae112802 		 * rules of the regular virtual page coloring scheme.
9302296Sae112802 		 *
9312296Sae112802 		 * Per page states and required actions:
9322296Sae112802 		 *   P_KPMC: remove a kpm mapping that is conflicting.
9332296Sae112802 		 *   P_KPMS: remove a small kpm mapping within a kpm_page.
9342296Sae112802 		 *   P_TNC:  check if we can re-cache the page.
9352296Sae112802 		 *   P_PNC:  we cannot re-cache, sorry.
9362296Sae112802 		 * Per kpm_page:
9372296Sae112802 		 *   kp_refcntc > 0: page is part of a kpm_page with conflicts.
9382296Sae112802 		 *   kp_refcnts > 0: rm a small mapped page within a kpm_page.
9392296Sae112802 		 */
9402296Sae112802 
9412296Sae112802 		if (PP_ISKPMS(pp)) {
9422296Sae112802 			if (kp->kp_refcnts < 1) {
9432296Sae112802 				panic("sfmmu_kpm_mapout: bad refcnts kp=%p",
944*7393SDonghai.Qiao@Sun.COM 				    (void *)kp);
9452296Sae112802 			}
9462296Sae112802 			sfmmu_kpm_demap_small(vaddr);
9472296Sae112802 
9482296Sae112802 			/*
9492296Sae112802 			 * Check if we can resume cached mode. This might
9502296Sae112802 			 * be the case if the kpm mapping was the only
9512296Sae112802 			 * mapping in conflict with other non rule
9522296Sae112802 			 * compliant mappings. The page is no more marked
9532296Sae112802 			 * as kpm mapped, so the conv_tnc path will not
9542296Sae112802 			 * change kpm state.
9552296Sae112802 			 */
9562296Sae112802 			if (PP_ISTNC(pp))  {
9572296Sae112802 				if (!PP_ISKPMC(pp)) {
9582296Sae112802 					/*
9592296Sae112802 					 * Uncached kpm mappings must always
9602296Sae112802 					 * have forced "small page" mode.
9612296Sae112802 					 */
9622296Sae112802 					panic("sfmmu_kpm_mapout: uncached "
963*7393SDonghai.Qiao@Sun.COM 					    "page not kpm marked");
9642296Sae112802 				}
9652296Sae112802 				conv_tnc(pp, TTE8K);
9662296Sae112802 			}
9672296Sae112802 			kp->kp_refcnts--;
9682296Sae112802 			kp->kp_refcnt++;
9692296Sae112802 			pmtx = sfmmu_page_enter(pp);
9702296Sae112802 			PP_CLRKPMS(pp);
9712296Sae112802 			sfmmu_page_exit(pmtx);
9722296Sae112802 		}
9732296Sae112802 
9742296Sae112802 		if (PP_ISKPMC(pp)) {
9752296Sae112802 			if (kp->kp_refcntc < 1) {
9762296Sae112802 				panic("sfmmu_kpm_mapout: bad refcntc kp=%p",
977*7393SDonghai.Qiao@Sun.COM 				    (void *)kp);
9782296Sae112802 			}
9792296Sae112802 			pmtx = sfmmu_page_enter(pp);
9802296Sae112802 			PP_CLRKPMC(pp);
9812296Sae112802 			sfmmu_page_exit(pmtx);
9822296Sae112802 			kp->kp_refcntc--;
9832296Sae112802 		}
9842296Sae112802 
9852296Sae112802 		if (kp->kp_refcnt-- < 1)
9862296Sae112802 			panic("sfmmu_kpm_mapout: bad refcnt kp=%p", (void *)kp);
9872296Sae112802 	}
9882296Sae112802 exit:
9892296Sae112802 	mutex_exit(&kpmp->khl_mutex);
9902296Sae112802 	return;
9912296Sae112802 
9922296Sae112802 smallpages_mapout:
9932296Sae112802 	PP2KPMSPG(pp, ksp);
9942296Sae112802 	kpmsp = KPMP_SHASH(ksp);
9952296Sae112802 
9962296Sae112802 	if (PP_ISKPMC(pp) == 0) {
997*7393SDonghai.Qiao@Sun.COM 		oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
998*7393SDonghai.Qiao@Sun.COM 		    &kpmsp->kshl_lock, 0);
9992296Sae112802 
10002296Sae112802 		if (oldval != KPM_MAPPEDS) {
10012296Sae112802 			/*
10022296Sae112802 			 * When we're called after sfmmu_kpm_hme_unload,
10032296Sae112802 			 * KPM_MAPPEDSC is valid too.
10042296Sae112802 			 */
10052296Sae112802 			if (oldval != KPM_MAPPEDSC)
10062296Sae112802 				panic("sfmmu_kpm_mapout: incorrect mapping");
10072296Sae112802 		}
10082296Sae112802 
10092296Sae112802 		/* remove TSB entry */
10102296Sae112802 		sfmmu_kpm_unload_tsb(vaddr, MMU_PAGESHIFT);
10112296Sae112802 #ifdef	DEBUG
10122296Sae112802 		if (kpm_tlb_flush)
10132296Sae112802 			sfmmu_kpm_demap_tlbs(vaddr);
10142296Sae112802 #endif
10152296Sae112802 
10162296Sae112802 	} else if (PP_ISTNC(pp)) {
1017*7393SDonghai.Qiao@Sun.COM 		oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
1018*7393SDonghai.Qiao@Sun.COM 		    &kpmsp->kshl_lock, 0);
10192296Sae112802 
10202296Sae112802 		if (oldval != KPM_MAPPEDSC || PP_ISKPMC(pp) == 0)
10212296Sae112802 			panic("sfmmu_kpm_mapout: inconsistent TNC mapping");
10222296Sae112802 
10232296Sae112802 		sfmmu_kpm_demap_small(vaddr);
10242296Sae112802 
10252296Sae112802 		pmtx = sfmmu_page_enter(pp);
10262296Sae112802 		PP_CLRKPMC(pp);
10272296Sae112802 		sfmmu_page_exit(pmtx);
10282296Sae112802 
10292296Sae112802 		/*
10302296Sae112802 		 * Check if we can resume cached mode. This might be
10312296Sae112802 		 * the case if the kpm mapping was the only mapping
10322296Sae112802 		 * in conflict with other non rule compliant mappings.
10332296Sae112802 		 * The page is no more marked as kpm mapped, so the
10342296Sae112802 		 * conv_tnc path will not change the kpm state.
10352296Sae112802 		 */
10362296Sae112802 		conv_tnc(pp, TTE8K);
10372296Sae112802 
10382296Sae112802 	} else {
1039*7393SDonghai.Qiao@Sun.COM 		oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
1040*7393SDonghai.Qiao@Sun.COM 		    &kpmsp->kshl_lock, 0);
10412296Sae112802 
10422296Sae112802 		if (oldval != KPM_MAPPEDSC)
10432296Sae112802 			panic("sfmmu_kpm_mapout: inconsistent mapping");
10442296Sae112802 
10452296Sae112802 		pmtx = sfmmu_page_enter(pp);
10462296Sae112802 		PP_CLRKPMC(pp);
10472296Sae112802 		sfmmu_page_exit(pmtx);
10482296Sae112802 	}
10492296Sae112802 }
10502296Sae112802 
10512296Sae112802 #define	abs(x)  ((x) < 0 ? -(x) : (x))
10522296Sae112802 
10532296Sae112802 /*
10542296Sae112802  * Determine appropriate kpm mapping address and handle any kpm/hme
10552296Sae112802  * conflicts. Page mapping list and its vcolor parts must be protected.
10562296Sae112802  */
10572296Sae112802 static caddr_t
10582296Sae112802 sfmmu_kpm_getvaddr(page_t *pp, int *kpm_vac_rangep)
10592296Sae112802 {
10602296Sae112802 	int		vcolor, vcolor_pa;
10612296Sae112802 	caddr_t		vaddr;
10622296Sae112802 	uintptr_t	paddr;
10632296Sae112802 
10642296Sae112802 
10652296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
10662296Sae112802 
10672296Sae112802 	paddr = ptob(pp->p_pagenum);
10682296Sae112802 	vcolor_pa = addr_to_vcolor(paddr);
10692296Sae112802 
10702296Sae112802 	if (pp->p_vnode && IS_SWAPFSVP(pp->p_vnode)) {
10712296Sae112802 		vcolor = (PP_NEWPAGE(pp) || PP_ISNC(pp)) ?
10722296Sae112802 		    vcolor_pa : PP_GET_VCOLOR(pp);
10732296Sae112802 	} else {
10742296Sae112802 		vcolor = addr_to_vcolor(pp->p_offset);
10752296Sae112802 	}
10762296Sae112802 
10772296Sae112802 	vaddr = kpm_vbase + paddr;
10782296Sae112802 	*kpm_vac_rangep = 0;
10792296Sae112802 
10802296Sae112802 	if (vcolor_pa != vcolor) {
10812296Sae112802 		*kpm_vac_rangep = abs(vcolor - vcolor_pa);
10822296Sae112802 		vaddr += ((uintptr_t)(vcolor - vcolor_pa) << MMU_PAGESHIFT);
10832296Sae112802 		vaddr += (vcolor_pa > vcolor) ?
1084*7393SDonghai.Qiao@Sun.COM 		    ((uintptr_t)vcolor_pa << kpm_size_shift) :
1085*7393SDonghai.Qiao@Sun.COM 		    ((uintptr_t)(vcolor - vcolor_pa) << kpm_size_shift);
10862296Sae112802 
10872296Sae112802 		ASSERT(!PP_ISMAPPED_LARGE(pp));
10882296Sae112802 	}
10892296Sae112802 
10902296Sae112802 	if (PP_ISNC(pp))
10912296Sae112802 		return (vaddr);
10922296Sae112802 
10932296Sae112802 	if (PP_NEWPAGE(pp)) {
10942296Sae112802 		PP_SET_VCOLOR(pp, vcolor);
10952296Sae112802 		return (vaddr);
10962296Sae112802 	}
10972296Sae112802 
10982296Sae112802 	if (PP_GET_VCOLOR(pp) == vcolor)
10992296Sae112802 		return (vaddr);
11002296Sae112802 
11012296Sae112802 	ASSERT(!PP_ISMAPPED_KPM(pp));
11022296Sae112802 	sfmmu_kpm_vac_conflict(pp, vaddr);
11032296Sae112802 
11042296Sae112802 	return (vaddr);
11052296Sae112802 }
11062296Sae112802 
11072296Sae112802 /*
11082296Sae112802  * VAC conflict state bit values.
11092296Sae112802  * The following defines are used to make the handling of the
11102296Sae112802  * various input states more concise. For that the kpm states
11112296Sae112802  * per kpm_page and per page are combined in a summary state.
11122296Sae112802  * Each single state has a corresponding bit value in the
11132296Sae112802  * summary state. These defines only apply for kpm large page
11142296Sae112802  * mappings. Within comments the abbreviations "kc, c, ks, s"
11152296Sae112802  * are used as short form of the actual state, e.g. "kc" for
11162296Sae112802  * "kp_refcntc > 0", etc.
11172296Sae112802  */
11182296Sae112802 #define	KPM_KC	0x00000008	/* kpm_page: kp_refcntc > 0 */
11192296Sae112802 #define	KPM_C	0x00000004	/* page: P_KPMC set */
11202296Sae112802 #define	KPM_KS	0x00000002	/* kpm_page: kp_refcnts > 0 */
11212296Sae112802 #define	KPM_S	0x00000001	/* page: P_KPMS set */
11222296Sae112802 
11232296Sae112802 /*
11242296Sae112802  * Summary states used in sfmmu_kpm_fault (KPM_TSBM_*).
11252296Sae112802  * See also more detailed comments within in the sfmmu_kpm_fault switch.
11262296Sae112802  * Abbreviations used:
11272296Sae112802  * CONFL: VAC conflict(s) within a kpm_page.
11282296Sae112802  * MAPS:  Mapped small: Page mapped in using a regular page size kpm mapping.
11292296Sae112802  * RASM:  Re-assembling of a large page mapping possible.
11302296Sae112802  * RPLS:  Replace: TSB miss due to TSB replacement only.
11312296Sae112802  * BRKO:  Breakup Other: A large kpm mapping has to be broken because another
11322296Sae112802  *        page within the kpm_page is already involved in a VAC conflict.
11332296Sae112802  * BRKT:  Breakup This: A large kpm mapping has to be broken, this page is
11342296Sae112802  *        is involved in a VAC conflict.
11352296Sae112802  */
11362296Sae112802 #define	KPM_TSBM_CONFL_GONE	(0)
11372296Sae112802 #define	KPM_TSBM_MAPS_RASM	(KPM_KS)
11382296Sae112802 #define	KPM_TSBM_RPLS_RASM	(KPM_KS | KPM_S)
11392296Sae112802 #define	KPM_TSBM_MAPS_BRKO	(KPM_KC)
11402296Sae112802 #define	KPM_TSBM_MAPS		(KPM_KC | KPM_KS)
11412296Sae112802 #define	KPM_TSBM_RPLS		(KPM_KC | KPM_KS | KPM_S)
11422296Sae112802 #define	KPM_TSBM_MAPS_BRKT	(KPM_KC | KPM_C)
11432296Sae112802 #define	KPM_TSBM_MAPS_CONFL	(KPM_KC | KPM_C | KPM_KS)
11442296Sae112802 #define	KPM_TSBM_RPLS_CONFL	(KPM_KC | KPM_C | KPM_KS | KPM_S)
11452296Sae112802 
11462296Sae112802 /*
11472296Sae112802  * kpm fault handler for mappings with large page size.
11482296Sae112802  */
11492296Sae112802 int
11502296Sae112802 sfmmu_kpm_fault(caddr_t vaddr, struct memseg *mseg, page_t *pp)
11512296Sae112802 {
11522296Sae112802 	int		error;
11532296Sae112802 	pgcnt_t		inx;
11542296Sae112802 	kpm_page_t	*kp;
11552296Sae112802 	tte_t		tte;
11562296Sae112802 	pfn_t		pfn = pp->p_pagenum;
11572296Sae112802 	kpm_hlk_t	*kpmp;
11582296Sae112802 	kmutex_t	*pml;
11592296Sae112802 	int		alias_range;
11602296Sae112802 	int		uncached = 0;
11612296Sae112802 	kmutex_t	*pmtx;
11622296Sae112802 	int		badstate;
11632296Sae112802 	uint_t		tsbmcase;
11642296Sae112802 
11652296Sae112802 	alias_range = IS_KPM_ALIAS_RANGE(vaddr);
11662296Sae112802 
11672296Sae112802 	inx = ptokpmp(kpmptop(ptokpmp(pfn)) - mseg->kpm_pbase);
11682296Sae112802 	if (inx >= mseg->kpm_nkpmpgs) {
11692296Sae112802 		cmn_err(CE_PANIC, "sfmmu_kpm_fault: kpm overflow in memseg "
1170*7393SDonghai.Qiao@Sun.COM 		    "0x%p  pp 0x%p", (void *)mseg, (void *)pp);
11712296Sae112802 	}
11722296Sae112802 
11732296Sae112802 	kp = &mseg->kpm_pages[inx];
11742296Sae112802 	kpmp = KPMP_HASH(kp);
11752296Sae112802 
11762296Sae112802 	pml = sfmmu_mlist_enter(pp);
11772296Sae112802 
11782296Sae112802 	if (!PP_ISMAPPED_KPM(pp)) {
11792296Sae112802 		sfmmu_mlist_exit(pml);
11802296Sae112802 		return (EFAULT);
11812296Sae112802 	}
11822296Sae112802 
11832296Sae112802 	mutex_enter(&kpmp->khl_mutex);
11842296Sae112802 
11852296Sae112802 	if (alias_range) {
11862296Sae112802 		ASSERT(!PP_ISMAPPED_LARGE(pp));
11872296Sae112802 		if (kp->kp_refcnta > 0) {
11882296Sae112802 			if (PP_ISKPMC(pp)) {
11892296Sae112802 				pmtx = sfmmu_page_enter(pp);
11902296Sae112802 				PP_CLRKPMC(pp);
11912296Sae112802 				sfmmu_page_exit(pmtx);
11922296Sae112802 			}
11932296Sae112802 			/*
11942296Sae112802 			 * Check for vcolor conflicts. Return here
11952296Sae112802 			 * w/ either no conflict (fast path), removed hme
11962296Sae112802 			 * mapping chains (unload conflict) or uncached
11972296Sae112802 			 * (uncache conflict). VACaches are cleaned and
11982296Sae112802 			 * p_vcolor and PP_TNC are set accordingly for the
11992296Sae112802 			 * conflict cases.  Drop kpmp for uncache conflict
12002296Sae112802 			 * cases since it will be grabbed within
12012296Sae112802 			 * sfmmu_kpm_page_cache in case of an uncache
12022296Sae112802 			 * conflict.
12032296Sae112802 			 */
12042296Sae112802 			mutex_exit(&kpmp->khl_mutex);
12052296Sae112802 			sfmmu_kpm_vac_conflict(pp, vaddr);
12062296Sae112802 			mutex_enter(&kpmp->khl_mutex);
12072296Sae112802 
12082296Sae112802 			if (PP_ISNC(pp)) {
12092296Sae112802 				uncached = 1;
12102296Sae112802 				pmtx = sfmmu_page_enter(pp);
12112296Sae112802 				PP_SETKPMC(pp);
12122296Sae112802 				sfmmu_page_exit(pmtx);
12132296Sae112802 			}
12142296Sae112802 			goto smallexit;
12152296Sae112802 
12162296Sae112802 		} else {
12172296Sae112802 			/*
12182296Sae112802 			 * We got a tsbmiss on a not active kpm_page range.
12192296Sae112802 			 * Let segkpm_fault decide how to panic.
12202296Sae112802 			 */
12212296Sae112802 			error = EFAULT;
12222296Sae112802 		}
12232296Sae112802 		goto exit;
12242296Sae112802 	}
12252296Sae112802 
12262296Sae112802 	badstate = (kp->kp_refcnt < 0 || kp->kp_refcnts < 0);
12272296Sae112802 	if (kp->kp_refcntc == -1) {
12282296Sae112802 		/*
12292296Sae112802 		 * We should come here only if trap level tsb miss
12302296Sae112802 		 * handler is disabled.
12312296Sae112802 		 */
12322296Sae112802 		badstate |= (kp->kp_refcnt == 0 || kp->kp_refcnts > 0 ||
1233*7393SDonghai.Qiao@Sun.COM 		    PP_ISKPMC(pp) || PP_ISKPMS(pp) || PP_ISNC(pp));
12342296Sae112802 
12352296Sae112802 		if (badstate == 0)
12362296Sae112802 			goto largeexit;
12372296Sae112802 	}
12382296Sae112802 
12392296Sae112802 	if (badstate || kp->kp_refcntc < 0)
12402296Sae112802 		goto badstate_exit;
12412296Sae112802 
12422296Sae112802 	/*
12432296Sae112802 	 * Combine the per kpm_page and per page kpm VAC states to
12442296Sae112802 	 * a summary state in order to make the kpm fault handling
12452296Sae112802 	 * more concise.
12462296Sae112802 	 */
12472296Sae112802 	tsbmcase = (((kp->kp_refcntc > 0) ? KPM_KC : 0) |
1248*7393SDonghai.Qiao@Sun.COM 	    ((kp->kp_refcnts > 0) ? KPM_KS : 0) |
1249*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMC(pp) ? KPM_C : 0) |
1250*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMS(pp) ? KPM_S : 0));
12512296Sae112802 
12522296Sae112802 	switch (tsbmcase) {
12532296Sae112802 	case KPM_TSBM_CONFL_GONE:		/* - - - - */
12542296Sae112802 		/*
12552296Sae112802 		 * That's fine, we either have no more vac conflict in
12562296Sae112802 		 * this kpm page or someone raced in and has solved the
12572296Sae112802 		 * vac conflict for us -- call sfmmu_kpm_vac_conflict
12582296Sae112802 		 * to take care for correcting the vcolor and flushing
12592296Sae112802 		 * the dcache if required.
12602296Sae112802 		 */
12612296Sae112802 		mutex_exit(&kpmp->khl_mutex);
12622296Sae112802 		sfmmu_kpm_vac_conflict(pp, vaddr);
12632296Sae112802 		mutex_enter(&kpmp->khl_mutex);
12642296Sae112802 
12652296Sae112802 		if (PP_ISNC(pp) || kp->kp_refcnt <= 0 ||
12662296Sae112802 		    addr_to_vcolor(vaddr) != PP_GET_VCOLOR(pp)) {
12672296Sae112802 			panic("sfmmu_kpm_fault: inconsistent CONFL_GONE "
1268*7393SDonghai.Qiao@Sun.COM 			    "state, pp=%p", (void *)pp);
12692296Sae112802 		}
12702296Sae112802 		goto largeexit;
12712296Sae112802 
12722296Sae112802 	case KPM_TSBM_MAPS_RASM:		/* - - ks - */
12732296Sae112802 		/*
12742296Sae112802 		 * All conflicts in this kpm page are gone but there are
12752296Sae112802 		 * already small mappings around, so we also map this
12762296Sae112802 		 * page small. This could be the trigger case for a
12772296Sae112802 		 * small mapping reaper, if this is really needed.
12782296Sae112802 		 * For now fall thru to the KPM_TSBM_MAPS handling.
12792296Sae112802 		 */
12802296Sae112802 
12812296Sae112802 	case KPM_TSBM_MAPS:			/* kc - ks - */
12822296Sae112802 		/*
12832296Sae112802 		 * Large page mapping is already broken, this page is not
12842296Sae112802 		 * conflicting, so map it small. Call sfmmu_kpm_vac_conflict
12852296Sae112802 		 * to take care for correcting the vcolor and flushing
12862296Sae112802 		 * the dcache if required.
12872296Sae112802 		 */
12882296Sae112802 		mutex_exit(&kpmp->khl_mutex);
12892296Sae112802 		sfmmu_kpm_vac_conflict(pp, vaddr);
12902296Sae112802 		mutex_enter(&kpmp->khl_mutex);
12912296Sae112802 
12922296Sae112802 		if (PP_ISNC(pp) || kp->kp_refcnt <= 0 ||
12932296Sae112802 		    addr_to_vcolor(vaddr) != PP_GET_VCOLOR(pp)) {
12942296Sae112802 			panic("sfmmu_kpm_fault:  inconsistent MAPS state, "
1295*7393SDonghai.Qiao@Sun.COM 			    "pp=%p", (void *)pp);
12962296Sae112802 		}
12972296Sae112802 		kp->kp_refcnt--;
12982296Sae112802 		kp->kp_refcnts++;
12992296Sae112802 		pmtx = sfmmu_page_enter(pp);
13002296Sae112802 		PP_SETKPMS(pp);
13012296Sae112802 		sfmmu_page_exit(pmtx);
13022296Sae112802 		goto smallexit;
13032296Sae112802 
13042296Sae112802 	case KPM_TSBM_RPLS_RASM:		/* - - ks s */
13052296Sae112802 		/*
13062296Sae112802 		 * All conflicts in this kpm page are gone but this page
13072296Sae112802 		 * is mapped small. This could be the trigger case for a
13082296Sae112802 		 * small mapping reaper, if this is really needed.
13092296Sae112802 		 * For now we drop it in small again. Fall thru to the
13102296Sae112802 		 * KPM_TSBM_RPLS handling.
13112296Sae112802 		 */
13122296Sae112802 
13132296Sae112802 	case KPM_TSBM_RPLS:			/* kc - ks s */
13142296Sae112802 		/*
13152296Sae112802 		 * Large page mapping is already broken, this page is not
13162296Sae112802 		 * conflicting but already mapped small, so drop it in
13172296Sae112802 		 * small again.
13182296Sae112802 		 */
13192296Sae112802 		if (PP_ISNC(pp) ||
13202296Sae112802 		    addr_to_vcolor(vaddr) != PP_GET_VCOLOR(pp)) {
13212296Sae112802 			panic("sfmmu_kpm_fault:  inconsistent RPLS state, "
1322*7393SDonghai.Qiao@Sun.COM 			    "pp=%p", (void *)pp);
13232296Sae112802 		}
13242296Sae112802 		goto smallexit;
13252296Sae112802 
13262296Sae112802 	case KPM_TSBM_MAPS_BRKO:		/* kc - - - */
13272296Sae112802 		/*
13282296Sae112802 		 * The kpm page where we live in is marked conflicting
13292296Sae112802 		 * but this page is not conflicting. So we have to map it
13302296Sae112802 		 * in small. Call sfmmu_kpm_vac_conflict to take care for
13312296Sae112802 		 * correcting the vcolor and flushing the dcache if required.
13322296Sae112802 		 */
13332296Sae112802 		mutex_exit(&kpmp->khl_mutex);
13342296Sae112802 		sfmmu_kpm_vac_conflict(pp, vaddr);
13352296Sae112802 		mutex_enter(&kpmp->khl_mutex);
13362296Sae112802 
13372296Sae112802 		if (PP_ISNC(pp) || kp->kp_refcnt <= 0 ||
13382296Sae112802 		    addr_to_vcolor(vaddr) != PP_GET_VCOLOR(pp)) {
13392296Sae112802 			panic("sfmmu_kpm_fault:  inconsistent MAPS_BRKO state, "
1340*7393SDonghai.Qiao@Sun.COM 			    "pp=%p", (void *)pp);
13412296Sae112802 		}
13422296Sae112802 		kp->kp_refcnt--;
13432296Sae112802 		kp->kp_refcnts++;
13442296Sae112802 		pmtx = sfmmu_page_enter(pp);
13452296Sae112802 		PP_SETKPMS(pp);
13462296Sae112802 		sfmmu_page_exit(pmtx);
13472296Sae112802 		goto smallexit;
13482296Sae112802 
13492296Sae112802 	case KPM_TSBM_MAPS_BRKT:		/* kc c - - */
13502296Sae112802 	case KPM_TSBM_MAPS_CONFL:		/* kc c ks - */
13512296Sae112802 		if (!PP_ISMAPPED(pp)) {
13522296Sae112802 			/*
13532296Sae112802 			 * We got a tsbmiss on kpm large page range that is
13542296Sae112802 			 * marked to contain vac conflicting pages introduced
13552296Sae112802 			 * by hme mappings. The hme mappings are all gone and
13562296Sae112802 			 * must have bypassed the kpm alias prevention logic.
13572296Sae112802 			 */
13582296Sae112802 			panic("sfmmu_kpm_fault: stale VAC conflict, pp=%p",
1359*7393SDonghai.Qiao@Sun.COM 			    (void *)pp);
13602296Sae112802 		}
13612296Sae112802 
13622296Sae112802 		/*
13632296Sae112802 		 * Check for vcolor conflicts. Return here w/ either no
13642296Sae112802 		 * conflict (fast path), removed hme mapping chains
13652296Sae112802 		 * (unload conflict) or uncached (uncache conflict).
13662296Sae112802 		 * Dcache is cleaned and p_vcolor and P_TNC are set
13672296Sae112802 		 * accordingly. Drop kpmp for uncache conflict cases
13682296Sae112802 		 * since it will be grabbed within sfmmu_kpm_page_cache
13692296Sae112802 		 * in case of an uncache conflict.
13702296Sae112802 		 */
13712296Sae112802 		mutex_exit(&kpmp->khl_mutex);
13722296Sae112802 		sfmmu_kpm_vac_conflict(pp, vaddr);
13732296Sae112802 		mutex_enter(&kpmp->khl_mutex);
13742296Sae112802 
13752296Sae112802 		if (kp->kp_refcnt <= 0)
13762296Sae112802 			panic("sfmmu_kpm_fault: bad refcnt kp=%p", (void *)kp);
13772296Sae112802 
13782296Sae112802 		if (PP_ISNC(pp)) {
13792296Sae112802 			uncached = 1;
13802296Sae112802 		} else {
13812296Sae112802 			/*
13822296Sae112802 			 * When an unload conflict is solved and there are
13832296Sae112802 			 * no other small mappings around, we can resume
13842296Sae112802 			 * largepage mode. Otherwise we have to map or drop
13852296Sae112802 			 * in small. This could be a trigger for a small
13862296Sae112802 			 * mapping reaper when this was the last conflict
13872296Sae112802 			 * within the kpm page and when there are only
13882296Sae112802 			 * other small mappings around.
13892296Sae112802 			 */
13902296Sae112802 			ASSERT(addr_to_vcolor(vaddr) == PP_GET_VCOLOR(pp));
13912296Sae112802 			ASSERT(kp->kp_refcntc > 0);
13922296Sae112802 			kp->kp_refcntc--;
13932296Sae112802 			pmtx = sfmmu_page_enter(pp);
13942296Sae112802 			PP_CLRKPMC(pp);
13952296Sae112802 			sfmmu_page_exit(pmtx);
13962296Sae112802 			ASSERT(PP_ISKPMS(pp) == 0);
13972296Sae112802 			if (kp->kp_refcntc == 0 && kp->kp_refcnts == 0)
13982296Sae112802 				goto largeexit;
13992296Sae112802 		}
14002296Sae112802 
14012296Sae112802 		kp->kp_refcnt--;
14022296Sae112802 		kp->kp_refcnts++;
14032296Sae112802 		pmtx = sfmmu_page_enter(pp);
14042296Sae112802 		PP_SETKPMS(pp);
14052296Sae112802 		sfmmu_page_exit(pmtx);
14062296Sae112802 		goto smallexit;
14072296Sae112802 
14082296Sae112802 	case KPM_TSBM_RPLS_CONFL:		/* kc c ks s */
14092296Sae112802 		if (!PP_ISMAPPED(pp)) {
14102296Sae112802 			/*
14112296Sae112802 			 * We got a tsbmiss on kpm large page range that is
14122296Sae112802 			 * marked to contain vac conflicting pages introduced
14132296Sae112802 			 * by hme mappings. They are all gone and must have
14142296Sae112802 			 * somehow bypassed the kpm alias prevention logic.
14152296Sae112802 			 */
14162296Sae112802 			panic("sfmmu_kpm_fault: stale VAC conflict, pp=%p",
1417*7393SDonghai.Qiao@Sun.COM 			    (void *)pp);
14182296Sae112802 		}
14192296Sae112802 
14202296Sae112802 		/*
14212296Sae112802 		 * This state is only possible for an uncached mapping.
14222296Sae112802 		 */
14232296Sae112802 		if (!PP_ISNC(pp)) {
14242296Sae112802 			panic("sfmmu_kpm_fault: page not uncached, pp=%p",
1425*7393SDonghai.Qiao@Sun.COM 			    (void *)pp);
14262296Sae112802 		}
14272296Sae112802 		uncached = 1;
14282296Sae112802 		goto smallexit;
14292296Sae112802 
14302296Sae112802 	default:
14312296Sae112802 badstate_exit:
14322296Sae112802 		panic("sfmmu_kpm_fault: inconsistent VAC state, vaddr=%p kp=%p "
1433*7393SDonghai.Qiao@Sun.COM 		    "pp=%p", (void *)vaddr, (void *)kp, (void *)pp);
14342296Sae112802 	}
14352296Sae112802 
14362296Sae112802 smallexit:
14372296Sae112802 	/* tte assembly */
14382296Sae112802 	if (uncached == 0)
14392296Sae112802 		KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
14402296Sae112802 	else
14412296Sae112802 		KPM_TTE_VUNCACHED(tte.ll, pfn, TTE8K);
14422296Sae112802 
14432296Sae112802 	/* tsb dropin */
14442296Sae112802 	sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
14452296Sae112802 
14462296Sae112802 	error = 0;
14472296Sae112802 	goto exit;
14482296Sae112802 
14492296Sae112802 largeexit:
14502296Sae112802 	if (kp->kp_refcnt > 0) {
14512296Sae112802 
14522296Sae112802 		/* tte assembly */
14532296Sae112802 		KPM_TTE_VCACHED(tte.ll, pfn, TTE4M);
14542296Sae112802 
14552296Sae112802 		/* tsb dropin */
14562296Sae112802 		sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT4M);
14572296Sae112802 
14582296Sae112802 		if (kp->kp_refcntc == 0) {
14592296Sae112802 			/* Set "go" flag for TL tsbmiss handler */
14602296Sae112802 			sfmmu_kpm_tsbmtl(&kp->kp_refcntc, &kpmp->khl_lock,
1461*7393SDonghai.Qiao@Sun.COM 			    KPMTSBM_START);
14622296Sae112802 		}
14632296Sae112802 		ASSERT(kp->kp_refcntc == -1);
14642296Sae112802 		error = 0;
14652296Sae112802 
14662296Sae112802 	} else
14672296Sae112802 		error = EFAULT;
14682296Sae112802 exit:
14692296Sae112802 	mutex_exit(&kpmp->khl_mutex);
14702296Sae112802 	sfmmu_mlist_exit(pml);
14712296Sae112802 	return (error);
14722296Sae112802 }
14732296Sae112802 
14742296Sae112802 /*
14752296Sae112802  * kpm fault handler for mappings with small page size.
14762296Sae112802  */
14772296Sae112802 int
14782296Sae112802 sfmmu_kpm_fault_small(caddr_t vaddr, struct memseg *mseg, page_t *pp)
14792296Sae112802 {
14802296Sae112802 	int		error = 0;
14812296Sae112802 	pgcnt_t		inx;
14822296Sae112802 	kpm_spage_t	*ksp;
14832296Sae112802 	kpm_shlk_t	*kpmsp;
14842296Sae112802 	kmutex_t	*pml;
14852296Sae112802 	pfn_t		pfn = pp->p_pagenum;
14862296Sae112802 	tte_t		tte;
14872296Sae112802 	kmutex_t	*pmtx;
14882296Sae112802 	int		oldval;
14892296Sae112802 
14902296Sae112802 	inx = pfn - mseg->kpm_pbase;
14912296Sae112802 	ksp = &mseg->kpm_spages[inx];
14922296Sae112802 	kpmsp = KPMP_SHASH(ksp);
14932296Sae112802 
14942296Sae112802 	pml = sfmmu_mlist_enter(pp);
14952296Sae112802 
14962296Sae112802 	if (!PP_ISMAPPED_KPM(pp)) {
14972296Sae112802 		sfmmu_mlist_exit(pml);
14982296Sae112802 		return (EFAULT);
14992296Sae112802 	}
15002296Sae112802 
15012296Sae112802 	/*
15022296Sae112802 	 * kp_mapped lookup protected by mlist mutex
15032296Sae112802 	 */
15042296Sae112802 	if (ksp->kp_mapped == KPM_MAPPEDS) {
15052296Sae112802 		/*
15062296Sae112802 		 * Fast path tsbmiss
15072296Sae112802 		 */
15082296Sae112802 		ASSERT(!PP_ISKPMC(pp));
15092296Sae112802 		ASSERT(!PP_ISNC(pp));
15102296Sae112802 
15112296Sae112802 		/* tte assembly */
15122296Sae112802 		KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
15132296Sae112802 
15142296Sae112802 		/* tsb dropin */
15152296Sae112802 		sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
15162296Sae112802 
15172296Sae112802 	} else if (ksp->kp_mapped == KPM_MAPPEDSC) {
15182296Sae112802 		/*
15192296Sae112802 		 * Got here due to existing or gone kpm/hme VAC conflict.
15202296Sae112802 		 * Recheck for vcolor conflicts. Return here w/ either
15212296Sae112802 		 * no conflict, removed hme mapping chain (unload
15222296Sae112802 		 * conflict) or uncached (uncache conflict). VACaches
15232296Sae112802 		 * are cleaned and p_vcolor and PP_TNC are set accordingly
15242296Sae112802 		 * for the conflict cases.
15252296Sae112802 		 */
15262296Sae112802 		sfmmu_kpm_vac_conflict(pp, vaddr);
15272296Sae112802 
15282296Sae112802 		if (PP_ISNC(pp)) {
15292296Sae112802 			/* ASSERT(pp->p_share); XXX use hat_page_getshare */
15302296Sae112802 
15312296Sae112802 			/* tte assembly */
15322296Sae112802 			KPM_TTE_VUNCACHED(tte.ll, pfn, TTE8K);
15332296Sae112802 
15342296Sae112802 			/* tsb dropin */
15352296Sae112802 			sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
15362296Sae112802 
1537*7393SDonghai.Qiao@Sun.COM 			oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
1538*7393SDonghai.Qiao@Sun.COM 			    &kpmsp->kshl_lock, (KPM_MAPPED_GO | KPM_MAPPEDSC));
1539*7393SDonghai.Qiao@Sun.COM 
1540*7393SDonghai.Qiao@Sun.COM 			if (oldval != KPM_MAPPEDSC)
1541*7393SDonghai.Qiao@Sun.COM 				panic("sfmmu_kpm_fault_small: "
1542*7393SDonghai.Qiao@Sun.COM 				    "stale smallpages mapping");
15432296Sae112802 		} else {
15442296Sae112802 			if (PP_ISKPMC(pp)) {
15452296Sae112802 				pmtx = sfmmu_page_enter(pp);
15462296Sae112802 				PP_CLRKPMC(pp);
15472296Sae112802 				sfmmu_page_exit(pmtx);
15482296Sae112802 			}
15492296Sae112802 
15502296Sae112802 			/* tte assembly */
15512296Sae112802 			KPM_TTE_VCACHED(tte.ll, pfn, TTE8K);
15522296Sae112802 
15532296Sae112802 			/* tsb dropin */
15542296Sae112802 			sfmmu_kpm_load_tsb(vaddr, &tte, MMU_PAGESHIFT);
15552296Sae112802 
1556*7393SDonghai.Qiao@Sun.COM 			oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
1557*7393SDonghai.Qiao@Sun.COM 			    &kpmsp->kshl_lock, (KPM_MAPPED_GO | KPM_MAPPEDS));
15582296Sae112802 
15592296Sae112802 			if (oldval != KPM_MAPPEDSC)
15602296Sae112802 				panic("sfmmu_kpm_fault_small: "
1561*7393SDonghai.Qiao@Sun.COM 				    "stale smallpages mapping");
15622296Sae112802 		}
15632296Sae112802 
15642296Sae112802 	} else {
15652296Sae112802 		/*
15662296Sae112802 		 * We got a tsbmiss on a not active kpm_page range.
15672296Sae112802 		 * Let decide segkpm_fault how to panic.
15682296Sae112802 		 */
15692296Sae112802 		error = EFAULT;
15702296Sae112802 	}
15712296Sae112802 
15722296Sae112802 	sfmmu_mlist_exit(pml);
15732296Sae112802 	return (error);
15742296Sae112802 }
15752296Sae112802 
15762296Sae112802 /*
15772296Sae112802  * Check/handle potential hme/kpm mapping conflicts
15782296Sae112802  */
15792296Sae112802 static void
15802296Sae112802 sfmmu_kpm_vac_conflict(page_t *pp, caddr_t vaddr)
15812296Sae112802 {
15822296Sae112802 	int		vcolor;
15832296Sae112802 	struct sf_hment	*sfhmep;
15842296Sae112802 	struct hat	*tmphat;
15852296Sae112802 	struct sf_hment	*tmphme = NULL;
15862296Sae112802 	struct hme_blk	*hmeblkp;
15872296Sae112802 	tte_t		tte;
15882296Sae112802 
15892296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
15902296Sae112802 
15912296Sae112802 	if (PP_ISNC(pp))
15922296Sae112802 		return;
15932296Sae112802 
15942296Sae112802 	vcolor = addr_to_vcolor(vaddr);
15952296Sae112802 	if (PP_GET_VCOLOR(pp) == vcolor)
15962296Sae112802 		return;
15972296Sae112802 
15982296Sae112802 	/*
15992296Sae112802 	 * There could be no vcolor conflict between a large cached
16002296Sae112802 	 * hme page and a non alias range kpm page (neither large nor
16012296Sae112802 	 * small mapped). So if a hme conflict already exists between
16022296Sae112802 	 * a constituent page of a large hme mapping and a shared small
16032296Sae112802 	 * conflicting hme mapping, both mappings must be already
16042296Sae112802 	 * uncached at this point.
16052296Sae112802 	 */
16062296Sae112802 	ASSERT(!PP_ISMAPPED_LARGE(pp));
16072296Sae112802 
16082296Sae112802 	if (!PP_ISMAPPED(pp)) {
16092296Sae112802 		/*
16102296Sae112802 		 * Previous hme user of page had a different color
16112296Sae112802 		 * but since there are no current users
16122296Sae112802 		 * we just flush the cache and change the color.
16132296Sae112802 		 */
16142296Sae112802 		SFMMU_STAT(sf_pgcolor_conflict);
16152296Sae112802 		sfmmu_cache_flush(pp->p_pagenum, PP_GET_VCOLOR(pp));
16162296Sae112802 		PP_SET_VCOLOR(pp, vcolor);
16172296Sae112802 		return;
16182296Sae112802 	}
16192296Sae112802 
16202296Sae112802 	/*
16212296Sae112802 	 * If we get here we have a vac conflict with a current hme
16222296Sae112802 	 * mapping. This must have been established by forcing a wrong
16232296Sae112802 	 * colored mapping, e.g. by using mmap(2) with MAP_FIXED.
16242296Sae112802 	 */
16252296Sae112802 
16262296Sae112802 	/*
16272296Sae112802 	 * Check if any mapping is in same as or if it is locked
16282296Sae112802 	 * since in that case we need to uncache.
16292296Sae112802 	 */
16302296Sae112802 	for (sfhmep = pp->p_mapping; sfhmep; sfhmep = tmphme) {
16312296Sae112802 		tmphme = sfhmep->hme_next;
16325075Spaulsan 		if (IS_PAHME(sfhmep))
16335075Spaulsan 			continue;
16342296Sae112802 		hmeblkp = sfmmu_hmetohblk(sfhmep);
16352296Sae112802 		if (hmeblkp->hblk_xhat_bit)
16362296Sae112802 			continue;
16372296Sae112802 		tmphat = hblktosfmmu(hmeblkp);
16382296Sae112802 		sfmmu_copytte(&sfhmep->hme_tte, &tte);
16392296Sae112802 		ASSERT(TTE_IS_VALID(&tte));
16402296Sae112802 		if ((tmphat == ksfmmup) || hmeblkp->hblk_lckcnt) {
16412296Sae112802 			/*
16422296Sae112802 			 * We have an uncache conflict
16432296Sae112802 			 */
16442296Sae112802 			SFMMU_STAT(sf_uncache_conflict);
16452296Sae112802 			sfmmu_page_cache_array(pp, HAT_TMPNC, CACHE_FLUSH, 1);
16462296Sae112802 			return;
16472296Sae112802 		}
16482296Sae112802 	}
16492296Sae112802 
16502296Sae112802 	/*
16512296Sae112802 	 * We have an unload conflict
16522296Sae112802 	 */
16532296Sae112802 	SFMMU_STAT(sf_unload_conflict);
16542296Sae112802 
16552296Sae112802 	for (sfhmep = pp->p_mapping; sfhmep; sfhmep = tmphme) {
16562296Sae112802 		tmphme = sfhmep->hme_next;
16575075Spaulsan 		if (IS_PAHME(sfhmep))
16585075Spaulsan 			continue;
16592296Sae112802 		hmeblkp = sfmmu_hmetohblk(sfhmep);
16602296Sae112802 		if (hmeblkp->hblk_xhat_bit)
16612296Sae112802 			continue;
16622296Sae112802 		(void) sfmmu_pageunload(pp, sfhmep, TTE8K);
16632296Sae112802 	}
16642296Sae112802 
16652296Sae112802 	/*
16662296Sae112802 	 * Unloads only does tlb flushes so we need to flush the
16672296Sae112802 	 * dcache vcolor here.
16682296Sae112802 	 */
16692296Sae112802 	sfmmu_cache_flush(pp->p_pagenum, PP_GET_VCOLOR(pp));
16702296Sae112802 	PP_SET_VCOLOR(pp, vcolor);
16712296Sae112802 }
16722296Sae112802 
16732296Sae112802 /*
16742296Sae112802  * Remove all kpm mappings using kpme's for pp and check that
16752296Sae112802  * all kpm mappings (w/ and w/o kpme's) are gone.
16762296Sae112802  */
16772296Sae112802 void
16782296Sae112802 sfmmu_kpm_pageunload(page_t *pp)
16792296Sae112802 {
16802296Sae112802 	caddr_t		vaddr;
16812296Sae112802 	struct kpme	*kpme, *nkpme;
16822296Sae112802 
16832296Sae112802 	ASSERT(pp != NULL);
16842296Sae112802 	ASSERT(pp->p_kpmref);
16852296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
16862296Sae112802 
16872296Sae112802 	vaddr = hat_kpm_page2va(pp, 1);
16882296Sae112802 
16892296Sae112802 	for (kpme = pp->p_kpmelist; kpme; kpme = nkpme) {
16902296Sae112802 		ASSERT(kpme->kpe_page == pp);
16912296Sae112802 
16922296Sae112802 		if (pp->p_kpmref == 0)
16932296Sae112802 			panic("sfmmu_kpm_pageunload: stale p_kpmref pp=%p "
1694*7393SDonghai.Qiao@Sun.COM 			    "kpme=%p", (void *)pp, (void *)kpme);
16952296Sae112802 
16962296Sae112802 		nkpme = kpme->kpe_next;
16972296Sae112802 
16982296Sae112802 		/* Add instance callback here here if needed later */
16992296Sae112802 		sfmmu_kpme_sub(kpme, pp);
17002296Sae112802 	}
17012296Sae112802 
17022296Sae112802 	/*
17032296Sae112802 	 * Also correct after mixed kpme/nonkpme mappings. If nonkpme
17042296Sae112802 	 * segkpm clients have unlocked the page and forgot to mapout
17052296Sae112802 	 * we panic here.
17062296Sae112802 	 */
17072296Sae112802 	if (pp->p_kpmref != 0)
17082296Sae112802 		panic("sfmmu_kpm_pageunload: bad refcnt pp=%p", (void *)pp);
17092296Sae112802 
17102296Sae112802 	sfmmu_kpm_mapout(pp, vaddr);
17112296Sae112802 }
17122296Sae112802 
17132296Sae112802 /*
17142296Sae112802  * Remove a large kpm mapping from kernel TSB and all TLB's.
17152296Sae112802  */
17162296Sae112802 static void
17172296Sae112802 sfmmu_kpm_demap_large(caddr_t vaddr)
17182296Sae112802 {
17192296Sae112802 	sfmmu_kpm_unload_tsb(vaddr, MMU_PAGESHIFT4M);
17202296Sae112802 	sfmmu_kpm_demap_tlbs(vaddr);
17212296Sae112802 }
17222296Sae112802 
17232296Sae112802 /*
17242296Sae112802  * Remove a small kpm mapping from kernel TSB and all TLB's.
17252296Sae112802  */
17262296Sae112802 static void
17272296Sae112802 sfmmu_kpm_demap_small(caddr_t vaddr)
17282296Sae112802 {
17292296Sae112802 	sfmmu_kpm_unload_tsb(vaddr, MMU_PAGESHIFT);
17302296Sae112802 	sfmmu_kpm_demap_tlbs(vaddr);
17312296Sae112802 }
17322296Sae112802 
17332296Sae112802 /*
17342296Sae112802  * Demap a kpm mapping in all TLB's.
17352296Sae112802  */
17362296Sae112802 static void
17372296Sae112802 sfmmu_kpm_demap_tlbs(caddr_t vaddr)
17382296Sae112802 {
17392296Sae112802 	cpuset_t cpuset;
17402296Sae112802 
17412296Sae112802 	kpreempt_disable();
17422296Sae112802 	cpuset = ksfmmup->sfmmu_cpusran;
17432296Sae112802 	CPUSET_AND(cpuset, cpu_ready_set);
17442296Sae112802 	CPUSET_DEL(cpuset, CPU->cpu_id);
17452296Sae112802 	SFMMU_XCALL_STATS(ksfmmup);
17462296Sae112802 
17472296Sae112802 	xt_some(cpuset, vtag_flushpage_tl1, (uint64_t)vaddr,
17482296Sae112802 	    (uint64_t)ksfmmup);
17492296Sae112802 	vtag_flushpage(vaddr, (uint64_t)ksfmmup);
17502296Sae112802 
17512296Sae112802 	kpreempt_enable();
17522296Sae112802 }
17532296Sae112802 
17542296Sae112802 /*
17552296Sae112802  * Summary states used in sfmmu_kpm_vac_unload (KPM_VUL__*).
17562296Sae112802  * See also more detailed comments within in the sfmmu_kpm_vac_unload switch.
17572296Sae112802  * Abbreviations used:
17582296Sae112802  * BIG:   Large page kpm mapping in use.
17592296Sae112802  * CONFL: VAC conflict(s) within a kpm_page.
17602296Sae112802  * INCR:  Count of conflicts within a kpm_page is going to be incremented.
17612296Sae112802  * DECR:  Count of conflicts within a kpm_page is going to be decremented.
17622296Sae112802  * UNMAP_SMALL: A small (regular page size) mapping is going to be unmapped.
17632296Sae112802  * TNC:   Temporary non cached: a kpm mapped page is mapped in TNC state.
17642296Sae112802  */
17652296Sae112802 #define	KPM_VUL_BIG		(0)
17662296Sae112802 #define	KPM_VUL_CONFL_INCR1	(KPM_KS)
17672296Sae112802 #define	KPM_VUL_UNMAP_SMALL1	(KPM_KS | KPM_S)
17682296Sae112802 #define	KPM_VUL_CONFL_INCR2	(KPM_KC)
17692296Sae112802 #define	KPM_VUL_CONFL_INCR3	(KPM_KC | KPM_KS)
17702296Sae112802 #define	KPM_VUL_UNMAP_SMALL2	(KPM_KC | KPM_KS | KPM_S)
17712296Sae112802 #define	KPM_VUL_CONFL_DECR1	(KPM_KC | KPM_C)
17722296Sae112802 #define	KPM_VUL_CONFL_DECR2	(KPM_KC | KPM_C | KPM_KS)
17732296Sae112802 #define	KPM_VUL_TNC		(KPM_KC | KPM_C | KPM_KS | KPM_S)
17742296Sae112802 
17752296Sae112802 /*
17762296Sae112802  * Handle VAC unload conflicts introduced by hme mappings or vice
17772296Sae112802  * versa when a hme conflict mapping is replaced by a non conflict
17782296Sae112802  * one. Perform actions and state transitions according to the
17792296Sae112802  * various page and kpm_page entry states. VACache flushes are in
17802296Sae112802  * the responsibiliy of the caller. We still hold the mlist lock.
17812296Sae112802  */
17822296Sae112802 void
17832296Sae112802 sfmmu_kpm_vac_unload(page_t *pp, caddr_t vaddr)
17842296Sae112802 {
17852296Sae112802 	kpm_page_t	*kp;
17862296Sae112802 	kpm_hlk_t	*kpmp;
17872296Sae112802 	caddr_t		kpmvaddr = hat_kpm_page2va(pp, 1);
17882296Sae112802 	int		newcolor;
17892296Sae112802 	kmutex_t	*pmtx;
17902296Sae112802 	uint_t		vacunlcase;
17912296Sae112802 	int		badstate = 0;
17922296Sae112802 	kpm_spage_t	*ksp;
17932296Sae112802 	kpm_shlk_t	*kpmsp;
17942296Sae112802 
17952296Sae112802 	ASSERT(PAGE_LOCKED(pp));
17962296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
17972296Sae112802 	ASSERT(!PP_ISNC(pp));
17982296Sae112802 
17992296Sae112802 	newcolor = addr_to_vcolor(kpmvaddr) != addr_to_vcolor(vaddr);
18002296Sae112802 	if (kpm_smallpages)
18012296Sae112802 		goto smallpages_vac_unload;
18022296Sae112802 
18032296Sae112802 	PP2KPMPG(pp, kp);
18042296Sae112802 	kpmp = KPMP_HASH(kp);
18052296Sae112802 	mutex_enter(&kpmp->khl_mutex);
18062296Sae112802 
18072296Sae112802 	if (IS_KPM_ALIAS_RANGE(kpmvaddr)) {
18082296Sae112802 		if (kp->kp_refcnta < 1) {
18092296Sae112802 			panic("sfmmu_kpm_vac_unload: bad refcnta kpm_page=%p\n",
1810*7393SDonghai.Qiao@Sun.COM 			    (void *)kp);
18112296Sae112802 		}
18122296Sae112802 
18132296Sae112802 		if (PP_ISKPMC(pp) == 0) {
18142296Sae112802 			if (newcolor == 0)
18152296Sae112802 				goto exit;
18162296Sae112802 			sfmmu_kpm_demap_small(kpmvaddr);
18172296Sae112802 			pmtx = sfmmu_page_enter(pp);
18182296Sae112802 			PP_SETKPMC(pp);
18192296Sae112802 			sfmmu_page_exit(pmtx);
18202296Sae112802 
18212296Sae112802 		} else if (newcolor == 0) {
18222296Sae112802 			pmtx = sfmmu_page_enter(pp);
18232296Sae112802 			PP_CLRKPMC(pp);
18242296Sae112802 			sfmmu_page_exit(pmtx);
18252296Sae112802 
18262296Sae112802 		} else {
18272296Sae112802 			badstate++;
18282296Sae112802 		}
18292296Sae112802 
18302296Sae112802 		goto exit;
18312296Sae112802 	}
18322296Sae112802 
18332296Sae112802 	badstate = (kp->kp_refcnt < 0 || kp->kp_refcnts < 0);
18342296Sae112802 	if (kp->kp_refcntc == -1) {
18352296Sae112802 		/*
18362296Sae112802 		 * We should come here only if trap level tsb miss
18372296Sae112802 		 * handler is disabled.
18382296Sae112802 		 */
18392296Sae112802 		badstate |= (kp->kp_refcnt == 0 || kp->kp_refcnts > 0 ||
1840*7393SDonghai.Qiao@Sun.COM 		    PP_ISKPMC(pp) || PP_ISKPMS(pp) || PP_ISNC(pp));
18412296Sae112802 	} else {
18422296Sae112802 		badstate |= (kp->kp_refcntc < 0);
18432296Sae112802 	}
18442296Sae112802 
18452296Sae112802 	if (badstate)
18462296Sae112802 		goto exit;
18472296Sae112802 
18482296Sae112802 	if (PP_ISKPMC(pp) == 0 && newcolor == 0) {
18492296Sae112802 		ASSERT(PP_ISKPMS(pp) == 0);
18502296Sae112802 		goto exit;
18512296Sae112802 	}
18522296Sae112802 
18532296Sae112802 	/*
18542296Sae112802 	 * Combine the per kpm_page and per page kpm VAC states
18552296Sae112802 	 * to a summary state in order to make the vac unload
18562296Sae112802 	 * handling more concise.
18572296Sae112802 	 */
18582296Sae112802 	vacunlcase = (((kp->kp_refcntc > 0) ? KPM_KC : 0) |
1859*7393SDonghai.Qiao@Sun.COM 	    ((kp->kp_refcnts > 0) ? KPM_KS : 0) |
1860*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMC(pp) ? KPM_C : 0) |
1861*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMS(pp) ? KPM_S : 0));
18622296Sae112802 
18632296Sae112802 	switch (vacunlcase) {
18642296Sae112802 	case KPM_VUL_BIG:				/* - - - - */
18652296Sae112802 		/*
18662296Sae112802 		 * Have to breakup the large page mapping to be
18672296Sae112802 		 * able to handle the conflicting hme vaddr.
18682296Sae112802 		 */
18692296Sae112802 		if (kp->kp_refcntc == -1) {
18702296Sae112802 			/* remove go indication */
18712296Sae112802 			sfmmu_kpm_tsbmtl(&kp->kp_refcntc,
1872*7393SDonghai.Qiao@Sun.COM 			    &kpmp->khl_lock, KPMTSBM_STOP);
18732296Sae112802 		}
18742296Sae112802 		sfmmu_kpm_demap_large(kpmvaddr);
18752296Sae112802 
18762296Sae112802 		ASSERT(kp->kp_refcntc == 0);
18772296Sae112802 		kp->kp_refcntc++;
18782296Sae112802 		pmtx = sfmmu_page_enter(pp);
18792296Sae112802 		PP_SETKPMC(pp);
18802296Sae112802 		sfmmu_page_exit(pmtx);
18812296Sae112802 		break;
18822296Sae112802 
18832296Sae112802 	case KPM_VUL_UNMAP_SMALL1:			/* -  - ks s */
18842296Sae112802 	case KPM_VUL_UNMAP_SMALL2:			/* kc - ks s */
18852296Sae112802 		/*
18862296Sae112802 		 * New conflict w/ an active kpm page, actually mapped
18872296Sae112802 		 * in by small TSB/TLB entries. Remove the mapping and
18882296Sae112802 		 * update states.
18892296Sae112802 		 */
18902296Sae112802 		ASSERT(newcolor);
18912296Sae112802 		sfmmu_kpm_demap_small(kpmvaddr);
18922296Sae112802 		kp->kp_refcnts--;
18932296Sae112802 		kp->kp_refcnt++;
18942296Sae112802 		kp->kp_refcntc++;
18952296Sae112802 		pmtx = sfmmu_page_enter(pp);
18962296Sae112802 		PP_CLRKPMS(pp);
18972296Sae112802 		PP_SETKPMC(pp);
18982296Sae112802 		sfmmu_page_exit(pmtx);
18992296Sae112802 		break;
19002296Sae112802 
19012296Sae112802 	case KPM_VUL_CONFL_INCR1:			/* -  - ks - */
19022296Sae112802 	case KPM_VUL_CONFL_INCR2:			/* kc - -  - */
19032296Sae112802 	case KPM_VUL_CONFL_INCR3:			/* kc - ks - */
19042296Sae112802 		/*
19052296Sae112802 		 * New conflict on a active kpm mapped page not yet in
19062296Sae112802 		 * TSB/TLB. Mark page and increment the kpm_page conflict
19072296Sae112802 		 * count.
19082296Sae112802 		 */
19092296Sae112802 		ASSERT(newcolor);
19102296Sae112802 		kp->kp_refcntc++;
19112296Sae112802 		pmtx = sfmmu_page_enter(pp);
19122296Sae112802 		PP_SETKPMC(pp);
19132296Sae112802 		sfmmu_page_exit(pmtx);
19142296Sae112802 		break;
19152296Sae112802 
19162296Sae112802 	case KPM_VUL_CONFL_DECR1:			/* kc c -  - */
19172296Sae112802 	case KPM_VUL_CONFL_DECR2:			/* kc c ks - */
19182296Sae112802 		/*
19192296Sae112802 		 * A conflicting hme mapping is removed for an active
19202296Sae112802 		 * kpm page not yet in TSB/TLB. Unmark page and decrement
19212296Sae112802 		 * the kpm_page conflict count.
19222296Sae112802 		 */
19232296Sae112802 		ASSERT(newcolor == 0);
19242296Sae112802 		kp->kp_refcntc--;
19252296Sae112802 		pmtx = sfmmu_page_enter(pp);
19262296Sae112802 		PP_CLRKPMC(pp);
19272296Sae112802 		sfmmu_page_exit(pmtx);
19282296Sae112802 		break;
19292296Sae112802 
19302296Sae112802 	case KPM_VUL_TNC:				/* kc c ks s */
19312296Sae112802 		cmn_err(CE_NOTE, "sfmmu_kpm_vac_unload: "
1932*7393SDonghai.Qiao@Sun.COM 		    "page not in NC state");
19332296Sae112802 		/* FALLTHRU */
19342296Sae112802 
19352296Sae112802 	default:
19362296Sae112802 		badstate++;
19372296Sae112802 	}
19382296Sae112802 exit:
19392296Sae112802 	if (badstate) {
19402296Sae112802 		panic("sfmmu_kpm_vac_unload: inconsistent VAC state, "
1941*7393SDonghai.Qiao@Sun.COM 		    "kpmvaddr=%p kp=%p pp=%p",
1942*7393SDonghai.Qiao@Sun.COM 		    (void *)kpmvaddr, (void *)kp, (void *)pp);
19432296Sae112802 	}
19442296Sae112802 	mutex_exit(&kpmp->khl_mutex);
19452296Sae112802 
19462296Sae112802 	return;
19472296Sae112802 
19482296Sae112802 smallpages_vac_unload:
19492296Sae112802 	if (newcolor == 0)
19502296Sae112802 		return;
19512296Sae112802 
19522296Sae112802 	PP2KPMSPG(pp, ksp);
19532296Sae112802 	kpmsp = KPMP_SHASH(ksp);
19542296Sae112802 
19552296Sae112802 	if (PP_ISKPMC(pp) == 0) {
19562296Sae112802 		if (ksp->kp_mapped == KPM_MAPPEDS) {
19572296Sae112802 			/*
19582296Sae112802 			 * Stop TL tsbmiss handling
19592296Sae112802 			 */
1960*7393SDonghai.Qiao@Sun.COM 			(void) sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag,
1961*7393SDonghai.Qiao@Sun.COM 			    &kpmsp->kshl_lock, KPM_MAPPEDSC);
19622296Sae112802 
19632296Sae112802 			sfmmu_kpm_demap_small(kpmvaddr);
19642296Sae112802 
19652296Sae112802 		} else if (ksp->kp_mapped != KPM_MAPPEDSC) {
19662296Sae112802 			panic("sfmmu_kpm_vac_unload: inconsistent mapping");
19672296Sae112802 		}
19682296Sae112802 
19692296Sae112802 		pmtx = sfmmu_page_enter(pp);
19702296Sae112802 		PP_SETKPMC(pp);
19712296Sae112802 		sfmmu_page_exit(pmtx);
19722296Sae112802 
19732296Sae112802 	} else {
19742296Sae112802 		if (ksp->kp_mapped != KPM_MAPPEDSC)
19752296Sae112802 			panic("sfmmu_kpm_vac_unload: inconsistent mapping");
19762296Sae112802 	}
19772296Sae112802 }
19782296Sae112802 
19792296Sae112802 /*
19802296Sae112802  * Page is marked to be in VAC conflict to an existing kpm mapping
19812296Sae112802  * or is kpm mapped using only the regular pagesize. Called from
19822296Sae112802  * sfmmu_hblk_unload when a mlist is completely removed.
19832296Sae112802  */
19842296Sae112802 void
19852296Sae112802 sfmmu_kpm_hme_unload(page_t *pp)
19862296Sae112802 {
19872296Sae112802 	/* tte assembly */
19882296Sae112802 	kpm_page_t	*kp;
19892296Sae112802 	kpm_hlk_t	*kpmp;
19902296Sae112802 	caddr_t		vaddr;
19912296Sae112802 	kmutex_t	*pmtx;
19922296Sae112802 	uint_t		flags;
19932296Sae112802 	kpm_spage_t	*ksp;
19942296Sae112802 
19952296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
19962296Sae112802 	ASSERT(PP_ISMAPPED_KPM(pp));
19972296Sae112802 
19982296Sae112802 	flags = pp->p_nrm & (P_KPMC | P_KPMS);
19992296Sae112802 	if (kpm_smallpages)
20002296Sae112802 		goto smallpages_hme_unload;
20012296Sae112802 
20022296Sae112802 	if (flags == (P_KPMC | P_KPMS)) {
20032296Sae112802 		panic("sfmmu_kpm_hme_unload: page should be uncached");
20042296Sae112802 
20052296Sae112802 	} else if (flags == P_KPMS) {
20062296Sae112802 		/*
20072296Sae112802 		 * Page mapped small but not involved in VAC conflict
20082296Sae112802 		 */
20092296Sae112802 		return;
20102296Sae112802 	}
20112296Sae112802 
20122296Sae112802 	vaddr = hat_kpm_page2va(pp, 1);
20132296Sae112802 
20142296Sae112802 	PP2KPMPG(pp, kp);
20152296Sae112802 	kpmp = KPMP_HASH(kp);
20162296Sae112802 	mutex_enter(&kpmp->khl_mutex);
20172296Sae112802 
20182296Sae112802 	if (IS_KPM_ALIAS_RANGE(vaddr)) {
20192296Sae112802 		if (kp->kp_refcnta < 1) {
20202296Sae112802 			panic("sfmmu_kpm_hme_unload: bad refcnta kpm_page=%p\n",
2021*7393SDonghai.Qiao@Sun.COM 			    (void *)kp);
20222296Sae112802 		}
20232296Sae112802 	} else {
20242296Sae112802 		if (kp->kp_refcntc < 1) {
20252296Sae112802 			panic("sfmmu_kpm_hme_unload: bad refcntc kpm_page=%p\n",
2026*7393SDonghai.Qiao@Sun.COM 			    (void *)kp);
20272296Sae112802 		}
20282296Sae112802 		kp->kp_refcntc--;
20292296Sae112802 	}
20302296Sae112802 
20312296Sae112802 	pmtx = sfmmu_page_enter(pp);
20322296Sae112802 	PP_CLRKPMC(pp);
20332296Sae112802 	sfmmu_page_exit(pmtx);
20342296Sae112802 
20352296Sae112802 	mutex_exit(&kpmp->khl_mutex);
20362296Sae112802 	return;
20372296Sae112802 
20382296Sae112802 smallpages_hme_unload:
20392296Sae112802 	if (flags != P_KPMC)
20402296Sae112802 		panic("sfmmu_kpm_hme_unload: page should be uncached");
20412296Sae112802 
20422296Sae112802 	vaddr = hat_kpm_page2va(pp, 1);
20432296Sae112802 	PP2KPMSPG(pp, ksp);
20442296Sae112802 
20452296Sae112802 	if (ksp->kp_mapped != KPM_MAPPEDSC)
20462296Sae112802 		panic("sfmmu_kpm_hme_unload: inconsistent mapping");
20472296Sae112802 
20482296Sae112802 	/*
20492296Sae112802 	 * Keep KPM_MAPPEDSC until the next kpm tsbmiss where it
20502296Sae112802 	 * prevents TL tsbmiss handling and force a hat_kpm_fault.
20512296Sae112802 	 * There we can start over again.
20522296Sae112802 	 */
20532296Sae112802 
20542296Sae112802 	pmtx = sfmmu_page_enter(pp);
20552296Sae112802 	PP_CLRKPMC(pp);
20562296Sae112802 	sfmmu_page_exit(pmtx);
20572296Sae112802 }
20582296Sae112802 
20592296Sae112802 /*
20602296Sae112802  * Special hooks for sfmmu_page_cache_array() when changing the
20612296Sae112802  * cacheability of a page. It is used to obey the hat_kpm lock
20622296Sae112802  * ordering (mlist -> kpmp -> spl, and back).
20632296Sae112802  */
20642296Sae112802 kpm_hlk_t *
20652296Sae112802 sfmmu_kpm_kpmp_enter(page_t *pp, pgcnt_t npages)
20662296Sae112802 {
20672296Sae112802 	kpm_page_t	*kp;
20682296Sae112802 	kpm_hlk_t	*kpmp;
20692296Sae112802 
20702296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
20712296Sae112802 
20722296Sae112802 	if (kpm_smallpages || PP_ISMAPPED_KPM(pp) == 0)
20732296Sae112802 		return (NULL);
20742296Sae112802 
20752296Sae112802 	ASSERT(npages <= kpmpnpgs);
20762296Sae112802 
20772296Sae112802 	PP2KPMPG(pp, kp);
20782296Sae112802 	kpmp = KPMP_HASH(kp);
20792296Sae112802 	mutex_enter(&kpmp->khl_mutex);
20802296Sae112802 
20812296Sae112802 	return (kpmp);
20822296Sae112802 }
20832296Sae112802 
20842296Sae112802 void
20852296Sae112802 sfmmu_kpm_kpmp_exit(kpm_hlk_t *kpmp)
20862296Sae112802 {
20872296Sae112802 	if (kpm_smallpages || kpmp == NULL)
20882296Sae112802 		return;
20892296Sae112802 
20902296Sae112802 	mutex_exit(&kpmp->khl_mutex);
20912296Sae112802 }
20922296Sae112802 
20932296Sae112802 /*
20942296Sae112802  * Summary states used in sfmmu_kpm_page_cache (KPM_*).
20952296Sae112802  * See also more detailed comments within in the sfmmu_kpm_page_cache switch.
20962296Sae112802  * Abbreviations used:
20972296Sae112802  * UNC:     Input state for an uncache request.
20982296Sae112802  *   BIG:     Large page kpm mapping in use.
20992296Sae112802  *   SMALL:   Page has a small kpm mapping within a kpm_page range.
21002296Sae112802  *   NODEMAP: No demap needed.
21012296Sae112802  *   NOP:     No operation needed on this input state.
21022296Sae112802  * CACHE:   Input state for a re-cache request.
21032296Sae112802  *   MAPS:    Page is in TNC and kpm VAC conflict state and kpm mapped small.
21042296Sae112802  *   NOMAP:   Page is in TNC and kpm VAC conflict state, but not small kpm
21052296Sae112802  *            mapped.
21062296Sae112802  *   NOMAPO:  Page is in TNC and kpm VAC conflict state, but not small kpm
21072296Sae112802  *            mapped. There are also other small kpm mappings within this
21082296Sae112802  *            kpm_page.
21092296Sae112802  */
21102296Sae112802 #define	KPM_UNC_BIG		(0)
21112296Sae112802 #define	KPM_UNC_NODEMAP1	(KPM_KS)
21122296Sae112802 #define	KPM_UNC_SMALL1		(KPM_KS | KPM_S)
21132296Sae112802 #define	KPM_UNC_NODEMAP2	(KPM_KC)
21142296Sae112802 #define	KPM_UNC_NODEMAP3	(KPM_KC | KPM_KS)
21152296Sae112802 #define	KPM_UNC_SMALL2		(KPM_KC | KPM_KS | KPM_S)
21162296Sae112802 #define	KPM_UNC_NOP1		(KPM_KC | KPM_C)
21172296Sae112802 #define	KPM_UNC_NOP2		(KPM_KC | KPM_C | KPM_KS)
21182296Sae112802 #define	KPM_CACHE_NOMAP		(KPM_KC | KPM_C)
21192296Sae112802 #define	KPM_CACHE_NOMAPO	(KPM_KC | KPM_C | KPM_KS)
21202296Sae112802 #define	KPM_CACHE_MAPS		(KPM_KC | KPM_C | KPM_KS | KPM_S)
21212296Sae112802 
21222296Sae112802 /*
21232296Sae112802  * This function is called when the virtual cacheability of a page
21242296Sae112802  * is changed and the page has an actice kpm mapping. The mlist mutex,
21252296Sae112802  * the spl hash lock and the kpmp mutex (if needed) are already grabbed.
21262296Sae112802  */
21272296Sae112802 /*ARGSUSED2*/
21282296Sae112802 void
21292296Sae112802 sfmmu_kpm_page_cache(page_t *pp, int flags, int cache_flush_tag)
21302296Sae112802 {
21312296Sae112802 	kpm_page_t	*kp;
21322296Sae112802 	kpm_hlk_t	*kpmp;
21332296Sae112802 	caddr_t		kpmvaddr;
21342296Sae112802 	int		badstate = 0;
21352296Sae112802 	uint_t		pgcacase;
21362296Sae112802 	kpm_spage_t	*ksp;
21372296Sae112802 	kpm_shlk_t	*kpmsp;
21382296Sae112802 	int		oldval;
21392296Sae112802 
21402296Sae112802 	ASSERT(PP_ISMAPPED_KPM(pp));
21412296Sae112802 	ASSERT(sfmmu_mlist_held(pp));
21422296Sae112802 	ASSERT(sfmmu_page_spl_held(pp));
21432296Sae112802 
21442296Sae112802 	if (flags != HAT_TMPNC && flags != HAT_CACHE)
21452296Sae112802 		panic("sfmmu_kpm_page_cache: bad flags");
21462296Sae112802 
21472296Sae112802 	kpmvaddr = hat_kpm_page2va(pp, 1);
21482296Sae112802 
21492296Sae112802 	if (flags == HAT_TMPNC && cache_flush_tag == CACHE_FLUSH) {
21502296Sae112802 		pfn_t pfn = pp->p_pagenum;
21512296Sae112802 		int vcolor = addr_to_vcolor(kpmvaddr);
21522296Sae112802 		cpuset_t cpuset = cpu_ready_set;
21532296Sae112802 
21542296Sae112802 		/* Flush vcolor in DCache */
21552296Sae112802 		CPUSET_DEL(cpuset, CPU->cpu_id);
21562296Sae112802 		SFMMU_XCALL_STATS(ksfmmup);
21572296Sae112802 		xt_some(cpuset, vac_flushpage_tl1, pfn, vcolor);
21582296Sae112802 		vac_flushpage(pfn, vcolor);
21592296Sae112802 	}
21602296Sae112802 
21612296Sae112802 	if (kpm_smallpages)
21622296Sae112802 		goto smallpages_page_cache;
21632296Sae112802 
21642296Sae112802 	PP2KPMPG(pp, kp);
21652296Sae112802 	kpmp = KPMP_HASH(kp);
21662296Sae112802 	ASSERT(MUTEX_HELD(&kpmp->khl_mutex));
21672296Sae112802 
21682296Sae112802 	if (IS_KPM_ALIAS_RANGE(kpmvaddr)) {
21692296Sae112802 		if (kp->kp_refcnta < 1) {
21702296Sae112802 			panic("sfmmu_kpm_page_cache: bad refcnta "
2171*7393SDonghai.Qiao@Sun.COM 			    "kpm_page=%p\n", (void *)kp);
21722296Sae112802 		}
21732296Sae112802 		sfmmu_kpm_demap_small(kpmvaddr);
21742296Sae112802 		if (flags == HAT_TMPNC) {
21752296Sae112802 			PP_SETKPMC(pp);
21762296Sae112802 			ASSERT(!PP_ISKPMS(pp));
21772296Sae112802 		} else {
21782296Sae112802 			ASSERT(PP_ISKPMC(pp));
21792296Sae112802 			PP_CLRKPMC(pp);
21802296Sae112802 		}
21812296Sae112802 		goto exit;
21822296Sae112802 	}
21832296Sae112802 
21842296Sae112802 	badstate = (kp->kp_refcnt < 0 || kp->kp_refcnts < 0);
21852296Sae112802 	if (kp->kp_refcntc == -1) {
21862296Sae112802 		/*
21872296Sae112802 		 * We should come here only if trap level tsb miss
21882296Sae112802 		 * handler is disabled.
21892296Sae112802 		 */
21902296Sae112802 		badstate |= (kp->kp_refcnt == 0 || kp->kp_refcnts > 0 ||
2191*7393SDonghai.Qiao@Sun.COM 		    PP_ISKPMC(pp) || PP_ISKPMS(pp) || PP_ISNC(pp));
21922296Sae112802 	} else {
21932296Sae112802 		badstate |= (kp->kp_refcntc < 0);
21942296Sae112802 	}
21952296Sae112802 
21962296Sae112802 	if (badstate)
21972296Sae112802 		goto exit;
21982296Sae112802 
21992296Sae112802 	/*
22002296Sae112802 	 * Combine the per kpm_page and per page kpm VAC states to
22012296Sae112802 	 * a summary state in order to make the VAC cache/uncache
22022296Sae112802 	 * handling more concise.
22032296Sae112802 	 */
22042296Sae112802 	pgcacase = (((kp->kp_refcntc > 0) ? KPM_KC : 0) |
2205*7393SDonghai.Qiao@Sun.COM 	    ((kp->kp_refcnts > 0) ? KPM_KS : 0) |
2206*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMC(pp) ? KPM_C : 0) |
2207*7393SDonghai.Qiao@Sun.COM 	    (PP_ISKPMS(pp) ? KPM_S : 0));
22082296Sae112802 
22092296Sae112802 	if (flags == HAT_CACHE) {
22102296Sae112802 		switch (pgcacase) {
22112296Sae112802 		case KPM_CACHE_MAPS:			/* kc c ks s */
22122296Sae112802 			sfmmu_kpm_demap_small(kpmvaddr);
22132296Sae112802 			if (kp->kp_refcnts < 1) {
22142296Sae112802 				panic("sfmmu_kpm_page_cache: bad refcnts "
22152296Sae112802 				"kpm_page=%p\n", (void *)kp);
22162296Sae112802 			}
22172296Sae112802 			kp->kp_refcnts--;
22182296Sae112802 			kp->kp_refcnt++;
22192296Sae112802 			PP_CLRKPMS(pp);
22202296Sae112802 			/* FALLTHRU */
22212296Sae112802 
22222296Sae112802 		case KPM_CACHE_NOMAP:			/* kc c -  - */
22232296Sae112802 		case KPM_CACHE_NOMAPO:			/* kc c ks - */
22242296Sae112802 			kp->kp_refcntc--;
22252296Sae112802 			PP_CLRKPMC(pp);
22262296Sae112802 			break;
22272296Sae112802 
22282296Sae112802 		default:
22292296Sae112802 			badstate++;
22302296Sae112802 		}
22312296Sae112802 		goto exit;
22322296Sae112802 	}
22332296Sae112802 
22342296Sae112802 	switch (pgcacase) {
22352296Sae112802 	case KPM_UNC_BIG:				/* - - - - */
22362296Sae112802 		if (kp->kp_refcnt < 1) {
22372296Sae112802 			panic("sfmmu_kpm_page_cache: bad refcnt "
2238*7393SDonghai.Qiao@Sun.COM 			    "kpm_page=%p\n", (void *)kp);
22392296Sae112802 		}
22402296Sae112802 
22412296Sae112802 		/*
22422296Sae112802 		 * Have to breakup the large page mapping in preparation
22432296Sae112802 		 * to the upcoming TNC mode handled by small mappings.
22442296Sae112802 		 * The demap can already be done due to another conflict
22452296Sae112802 		 * within the kpm_page.
22462296Sae112802 		 */
22472296Sae112802 		if (kp->kp_refcntc == -1) {
22482296Sae112802 			/* remove go indication */
22492296Sae112802 			sfmmu_kpm_tsbmtl(&kp->kp_refcntc,
2250*7393SDonghai.Qiao@Sun.COM 			    &kpmp->khl_lock, KPMTSBM_STOP);
22512296Sae112802 		}
22522296Sae112802 		ASSERT(kp->kp_refcntc == 0);
22532296Sae112802 		sfmmu_kpm_demap_large(kpmvaddr);
22542296Sae112802 		kp->kp_refcntc++;
22552296Sae112802 		PP_SETKPMC(pp);
22562296Sae112802 		break;
22572296Sae112802 
22582296Sae112802 	case KPM_UNC_SMALL1:				/* -  - ks s */
22592296Sae112802 	case KPM_UNC_SMALL2:				/* kc - ks s */
22602296Sae112802 		/*
22612296Sae112802 		 * Have to demap an already small kpm mapping in preparation
22622296Sae112802 		 * to the upcoming TNC mode. The demap can already be done
22632296Sae112802 		 * due to another conflict within the kpm_page.
22642296Sae112802 		 */
22652296Sae112802 		sfmmu_kpm_demap_small(kpmvaddr);
22662296Sae112802 		kp->kp_refcntc++;
22672296Sae112802 		kp->kp_refcnts--;
22682296Sae112802 		kp->kp_refcnt++;
22692296Sae112802 		PP_CLRKPMS(pp);
22702296Sae112802 		PP_SETKPMC(pp);
22712296Sae112802 		break;
22722296Sae112802 
22732296Sae112802 	case KPM_UNC_NODEMAP1:				/* -  - ks - */
22742296Sae112802 		/* fallthru */
22752296Sae112802 
22762296Sae112802 	case KPM_UNC_NODEMAP2:				/* kc - -  - */
22772296Sae112802 	case KPM_UNC_NODEMAP3:				/* kc - ks - */
22782296Sae112802 		kp->kp_refcntc++;
22792296Sae112802 		PP_SETKPMC(pp);
22802296Sae112802 		break;
22812296Sae112802 
22822296Sae112802 	case KPM_UNC_NOP1:				/* kc c -  - */
22832296Sae112802 	case KPM_UNC_NOP2:				/* kc c ks - */
22842296Sae112802 		break;
22852296Sae112802 
22862296Sae112802 	default:
22872296Sae112802 		badstate++;
22882296Sae112802 	}
22892296Sae112802 exit:
22902296Sae112802 	if (badstate) {
22912296Sae112802 		panic("sfmmu_kpm_page_cache: inconsistent VAC state "
2292*7393SDonghai.Qiao@Sun.COM 		    "kpmvaddr=%p kp=%p pp=%p", (void *)kpmvaddr,
2293*7393SDonghai.Qiao@Sun.COM 		    (void *)kp, (void *)pp);
22942296Sae112802 	}
22952296Sae112802 	return;
22962296Sae112802 
22972296Sae112802 smallpages_page_cache:
22982296Sae112802 	PP2KPMSPG(pp, ksp);
22992296Sae112802 	kpmsp = KPMP_SHASH(ksp);
23002296Sae112802 
2301*7393SDonghai.Qiao@Sun.COM 	/*
2302*7393SDonghai.Qiao@Sun.COM 	 * marked as nogo for we will fault in and resolve it
2303*7393SDonghai.Qiao@Sun.COM 	 * through sfmmu_kpm_fault_small
2304*7393SDonghai.Qiao@Sun.COM 	 */
2305*7393SDonghai.Qiao@Sun.COM 	oldval = sfmmu_kpm_stsbmtl(&ksp->kp_mapped_flag, &kpmsp->kshl_lock,
2306*7393SDonghai.Qiao@Sun.COM 	    KPM_MAPPEDSC);
23072296Sae112802 
23082296Sae112802 	if (!(oldval == KPM_MAPPEDS || oldval == KPM_MAPPEDSC))
23092296Sae112802 		panic("smallpages_page_cache: inconsistent mapping");
23102296Sae112802 
23112296Sae112802 	sfmmu_kpm_demap_small(kpmvaddr);
23122296Sae112802 
23132296Sae112802 	if (flags == HAT_TMPNC) {
23142296Sae112802 		PP_SETKPMC(pp);
23152296Sae112802 		ASSERT(!PP_ISKPMS(pp));
23162296Sae112802 
23172296Sae112802 	} else {
23182296Sae112802 		ASSERT(PP_ISKPMC(pp));
23192296Sae112802 		PP_CLRKPMC(pp);
23202296Sae112802 	}
23212296Sae112802 
23222296Sae112802 	/*
23232296Sae112802 	 * Keep KPM_MAPPEDSC until the next kpm tsbmiss where it
23242296Sae112802 	 * prevents TL tsbmiss handling and force a hat_kpm_fault.
23252296Sae112802 	 * There we can start over again.
23262296Sae112802 	 */
23272296Sae112802 }
2328