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