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