/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Kernel Physical Mapping (segkpm) hat interface routines for sun4v. */ #include #include #include #include #include #include #include #include #include extern pfn_t memseg_get_start(struct memseg *); /* * Kernel Physical Mapping (kpm) facility */ void mach_kpm_init() { uintptr_t start, end; struct memlist *pmem; /* * Map each of the memsegs into the kpm segment, coalesing * adjacent memsegs to allow mapping with the largest * possible pages. */ pmem = phys_install; start = pmem->ml_address; end = start + pmem->ml_size; for (;;) { if (pmem == NULL || pmem->ml_address > end) { hat_devload(kas.a_hat, kpm_vbase + start, end - start, mmu_btop(start), PROT_READ | PROT_WRITE, HAT_LOAD | HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); if (pmem == NULL) break; start = pmem->ml_address; } end = pmem->ml_address + pmem->ml_size; pmem = pmem->ml_next; } } /* -- hat_kpm interface section -- */ /* * Mapin a locked page and return the vaddr. */ /*ARGSUSED*/ caddr_t hat_kpm_mapin(struct page *pp, struct kpme *kpme) { caddr_t vaddr; if (kpm_enable == 0) { cmn_err(CE_WARN, "hat_kpm_mapin: kpm_enable not set"); return ((caddr_t)NULL); } if (pp == NULL || PAGE_LOCKED(pp) == 0) { cmn_err(CE_WARN, "hat_kpm_mapin: pp zero or not locked"); return ((caddr_t)NULL); } vaddr = hat_kpm_page2va(pp, 1); return (vaddr); } /* * Mapout a locked page. */ /*ARGSUSED*/ void hat_kpm_mapout(struct page *pp, struct kpme *kpme, caddr_t vaddr) { #ifdef DEBUG if (kpm_enable == 0) { cmn_err(CE_WARN, "hat_kpm_mapout: kpm_enable not set"); return; } if (IS_KPM_ADDR(vaddr) == 0) { cmn_err(CE_WARN, "hat_kpm_mapout: no kpm address"); return; } if (pp == NULL || PAGE_LOCKED(pp) == 0) { cmn_err(CE_WARN, "hat_kpm_mapout: page zero or not locked"); return; } #endif } /* * hat_kpm_mapin_pfn is used to obtain a kpm mapping for physical * memory addresses that are not described by a page_t. It can * also be used for normal pages that are not locked, but beware * this is dangerous - no locking is performed, so the identity of * the page could change. hat_kpm_mapin_pfn is not supported when * vac_colors > 1, because the chosen va depends on the page identity, * which could change. * The caller must only pass pfn's for valid physical addresses; violation * of this rule will cause panic. */ caddr_t hat_kpm_mapin_pfn(pfn_t pfn) { caddr_t paddr, vaddr; if (kpm_enable == 0) return ((caddr_t)NULL); paddr = (caddr_t)ptob(pfn); vaddr = (uintptr_t)kpm_vbase + paddr; return ((caddr_t)vaddr); } /*ARGSUSED*/ void hat_kpm_mapout_pfn(pfn_t pfn) { /* empty */ } /* * Return the kpm virtual address for the page at pp. */ /*ARGSUSED*/ caddr_t hat_kpm_page2va(struct page *pp, int checkswap) { uintptr_t paddr, vaddr; ASSERT(kpm_enable); paddr = ptob(pp->p_pagenum); vaddr = (uintptr_t)kpm_vbase + paddr; return ((caddr_t)vaddr); } /* * Return the page for the kpm virtual address vaddr. * Caller is responsible for the kpm mapping and lock * state of the page. */ page_t * hat_kpm_vaddr2page(caddr_t vaddr) { uintptr_t paddr; pfn_t pfn; ASSERT(IS_KPM_ADDR(vaddr)); SFMMU_KPM_VTOP(vaddr, paddr); pfn = (pfn_t)btop(paddr); return (page_numtopp_nolock(pfn)); } /*ARGSUSED*/ /* * hat_kpm_fault is called from segkpm_fault when a kpm tsbmiss occurred. * This should never happen on sun4v. */ int hat_kpm_fault(struct hat *hat, caddr_t vaddr) { /* * Return FC_NOMAP for sun4v to allow the t_lofault_handler * to handle this fault if one is installed */ return (FC_NOMAP); } /*ARGSUSED*/ void hat_kpm_mseghash_clear(int nentries) {} /*ARGSUSED*/ void hat_kpm_mseghash_update(pgcnt_t inx, struct memseg *msp) {} /*ARGSUSED*/ void hat_kpm_addmem_mseg_update(struct memseg *msp, pgcnt_t nkpmpgs, offset_t kpm_pages_off) { pfn_t base, end; /* * kphysm_add_memory_dynamic() does not set nkpmpgs * when page_t memory is externally allocated. That * code must properly calculate nkpmpgs in all cases * if nkpmpgs needs to be used at some point. */ /* * The meta (page_t) pages for dynamically added memory are allocated * either from the incoming memory itself or from existing memory. * In the former case the base of the incoming pages will be different * than the base of the dynamic segment so call memseg_get_start() to * get the actual base of the incoming memory for each case. */ base = memseg_get_start(msp); end = msp->pages_end; hat_devload(kas.a_hat, kpm_vbase + mmu_ptob(base), mmu_ptob(end - base), base, PROT_READ | PROT_WRITE, HAT_LOAD | HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); } /* * Return end of metadata for an already setup memseg. */ caddr_t hat_kpm_mseg_reuse(struct memseg *msp) { return ((caddr_t)msp->epages); } /*ARGSUSED*/ void hat_kpm_addmem_mseg_insert(struct memseg *msp) {} /*ARGSUSED*/ void hat_kpm_addmem_memsegs_update(struct memseg *msp) {} /*ARGSUSED*/ void hat_kpm_delmem_mseg_update(struct memseg *msp, struct memseg **mspp) { pfn_t base, end; /* * The meta (page_t) pages for dynamically added memory are allocated * either from the incoming memory itself or from existing memory. * In the former case the base of the incoming pages will be different * than the base of the dynamic segment so call memseg_get_start() to * get the actual base of the incoming memory for each case. */ base = memseg_get_start(msp); end = msp->pages_end; hat_unload(kas.a_hat, kpm_vbase + mmu_ptob(base), mmu_ptob(end - base), HAT_UNLOAD | HAT_UNLOAD_UNLOCK | HAT_UNLOAD_UNMAP); } /*ARGSUSED*/ void hat_kpm_split_mseg_update(struct memseg *msp, struct memseg **mspp, struct memseg *lo, struct memseg *mid, struct memseg *hi) {} /* * Walk the memsegs chain, applying func to each memseg span and vcolor. */ void hat_kpm_walk(void (*func)(void *, void *, size_t), void *arg) { pfn_t pbase, pend; void *base; size_t size; struct memseg *msp; for (msp = memsegs; msp; msp = msp->next) { pbase = msp->pages_base; pend = msp->pages_end; base = ptob(pbase) + kpm_vbase; size = ptob(pend - pbase); func(arg, base, size); } } /* -- sfmmu_kpm internal section -- */ /* * Return the page frame number if a valid segkpm mapping exists * for vaddr, otherwise return PFN_INVALID. No locks are grabbed. * Should only be used by other sfmmu routines. */ pfn_t sfmmu_kpm_vatopfn(caddr_t vaddr) { uintptr_t paddr; pfn_t pfn; page_t *pp; ASSERT(kpm_enable && IS_KPM_ADDR(vaddr)); SFMMU_KPM_VTOP(vaddr, paddr); pfn = (pfn_t)btop(paddr); pp = page_numtopp_nolock(pfn); if (pp) return (pfn); else return ((pfn_t)PFN_INVALID); }