hat_i86.c revision 7dacfc4494f6d14358974ef2830b5cd8c66a84de
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* VM - Hardware Address Translation management for i386 and amd64
*
*
* Nearly all the details of how the hardware is managed should not be
* visible outside this layer except for misc. machine specific functions
* that work in conjunction with this code.
*
*/
#include <sys/machparam.h>
#include <sys/machsystm.h>
#include <sys/sysmacros.h>
#include <sys/machparam.h>
#include <sys/x86_archext.h>
#include <sys/controlregs.h>
#include <sys/bootconf.h>
#include <sys/bootsvcs.h>
#include <sys/bootinfo.h>
#include <sys/archsystm.h>
#include <vm/seg_kmem.h>
#include <vm/kboot_mmu.h>
/*
* Basic parameters for hat operation.
*/
struct hat_mmu_info mmu;
/*
* The page that is the kernel's top level pagetable.
*
* For 32 bit VLP support, the kernel hat will use the 1st 4 entries
* on this 4K page for its top level page table. The remaining groups of
* 4 entries are used for per processor copies of user VLP pagetables for
* running threads. See hat_switch() and reload_pae32() for details.
*
* vlp_page[0] - 0th level==2 PTE for kernel HAT (will be zero)
* vlp_page[1] - 1st level==2 PTE for kernel HAT (will be zero)
* vlp_page[2] - 2nd level==2 PTE for kernel HAT (zero for small memory)
* vlp_page[3] - 3rd level==2 PTE for kernel
*
* vlp_page[4] - 0th level==2 PTE for user thread on cpu 0
* vlp_page[5] - 1st level==2 PTE for user thread on cpu 0
* vlp_page[6] - 2nd level==2 PTE for user thread on cpu 0
* vlp_page[7] - probably copy of kernel PTE
*
* vlp_page[8] - 0th level==2 PTE for user thread on cpu 1
* vlp_page[9] - 1st level==2 PTE for user thread on cpu 1
* vlp_page[10] - 2nd level==2 PTE for user thread on cpu 1
* vlp_page[11] - probably copy of kernel PTE
* ...
*
* when / where the kernel PTE's are (entry 2 or 3 or none) depends
* on kernelbase.
*/
/*
* forward declaration of internal utility routines
*/
/*
* The kernel address space exists in all HATs. To implement this the
* kernel reserves a fixed number of entries in every topmost level page
* table. The values are setup in hat_init() and then copied to every hat
* created by hat_alloc(). This means that kernelbase must be:
*
* 4Meg aligned for 32 bit kernels
* 512Gig aligned for x86_64 64 bit kernel
*
* The PAE 32 bit hat is handled as a special case. Otherwise requiring 1Gig
* alignment would use too much VA for the kernel.
*
*/
#if defined(__i386)
static uint_t khat_pae32_start;
static uint_t khat_pae32_entries;
#endif
/*
* A cpuset for all cpus. This is used for kernel address cross calls, since
* the kernel addresses apply to all cpus.
*/
/*
* management stuff for hat structures
*/
/*
* Simple statistics
*/
/*
*/
/*
* kmem cache constructor for struct hat
*/
/*ARGSUSED*/
static int
{
hat->hat_ism_pgcnt = 0;
return (0);
}
/*
* Allocate a hat structure for as. We also create the top level
* htable and initialize it to contain the kernel hat entries.
*/
hat_t *
{
/*
* Once we start creating user process HATs we can enable
* the htable_steal() code.
*/
if (can_steal_post_boot == 0)
can_steal_post_boot = 1;
/*
* a 32 bit process uses a VLP style hat when using PAE
*/
#if defined(__amd64)
#endif
if (use_vlp) {
}
/*
* Allocate the htable hash
*/
} else {
}
/*
* Initialize Kernel HAT entries at the top of the top level page
* table for the new hat.
*
* Note that we don't call htable_release() for the top level, that
* happens when the hat is destroyed in hat_free_end()
*/
#if defined(__i386)
else if (khat_entries > 0)
khat_entries * sizeof (x86pte_t));
#endif
#if defined(__i386)
/*
* PAE32 HAT alignment is less restrictive than the others to keep
* the kernel from using too much VA. Because of this we may need
* one layer further down when kernelbase isn't 1Gig aligned.
* See hat_free_end() for the htable_release() that goes with this
* htable_create()
*/
if (khat_pae32_htable != NULL) {
}
#endif
/*
* Put it at the start of the global list of all hats (used by stealing)
*
* kas.a_hat is not in the list but is instead used to find the
* first and last items in the list.
*
* - kas.a_hat->hat_next points to the start of the user hats.
* The list ends where hat->hat_next == NULL
*
* - kas.a_hat->hat_prev points to the last of the user hats.
* The list begins where hat->hat_prev == NULL
*/
else
return (hat);
}
/*
* process has finished executing but as has not been cleaned up yet.
*/
/*ARGSUSED*/
void
{
/*
* If the hat is currently a stealing victim, wait for the stealing
* to finish. Once we mark it as HAT_FREEING, htable_steal()
* won't look at its pagetables anymore.
*/
}
/*
* An address space is being destroyed, so we destroy the associated hat.
*/
void
{
int i;
#ifdef DEBUG
for (i = 0; i <= mmu.max_page_level; i++)
#endif
/*
* must not be running on the given hat
*/
/*
* Remove it from the list of HATs
*/
else
else
/*
* Make a pass through the htables freeing them all up.
*/
/*
* Decide which kmem cache the hash table came from, then free it.
*/
else
}
/*
* round kernelbase down to a supported value to use for _userlimit
*
* userlimit must be aligned down to an entry in the top level htable.
* The one exception is for 32 bit HAT's running PAE.
*/
{
#if defined(__i386)
#endif
if (IN_VA_HOLE(va))
return (va);
}
/*
* Initialize hat data structures based on processor MMU information.
*/
void
mmu_init(void)
{
int i;
/*
* If CPU enabled the page table global bit, use it for the kernel
* This is bit 7 in CR4 (PGE - Page Global Enable).
*/
/*
* Detect NX and PAE usage.
*/
if (kbm_nx_support)
else
/*
* Use CPU info to set various MMU parameters
*/
} else {
}
#if defined(OPTERON_ERRATUM_121)
/*
* If erratum 121 has already been detected at this time, hole_start
* contains the value to be subtracted from mmu.hole_start.
*/
#else
#endif
} else {
}
panic("Processor does not support PAE");
if ((x86_feature & X86_CX8) == 0)
panic("Processor does not support cmpxchg8b instruction");
/*
* Initialize parameters based on the 64 or 32 bit kernels and
* for the 32 bit kernel decide if we should use PAE.
*/
else
mmu.max_page_level = 0;
#if defined(__amd64)
} else {
}
#endif /* __i386 */
}
for (i = 0; i <= mmu.max_page_level; ++i) {
if (i > 0)
}
/*
* NOTE Legacy 32 bit PAE mode only has the P_VALID bit at top level.
*/
#if defined(__i386)
#endif
/*
* Compute how many hash table entries to have per process for htables.
* We start with 1 page's worth of entries.
*
* If physical memory is small, reduce the amount need to cover it.
*/
#if defined(__amd64)
/*
* If running in 64 bits and physical memory is large,
* increase the size of the cache to cover all of memory for
* a 64 bit process.
*/
#define HASH_MAX_LENGTH 4
#endif
}
/*
* initialize hat data structures
*/
void
hat_init()
{
#if defined(__i386)
/*
* _userlimit must be aligned correctly
*/
prom_printf("hat_init(): _userlimit=%p, not aligned at %p\n",
halt("hat_init(): Unable to continue");
}
#endif
/*
* initialize kmem caches
*/
htable_init();
hment_init();
NULL, 0, 0);
NULL, 0, 0);
/*
* VLP hats can use a smaller hash table size on large memroy machines
*/
} else {
NULL, 0, 0);
}
/*
* Set up the kernel's hat
*/
/*
* The kernel hat's next pointer serves as the head of the hat list .
* The kernel hat's prev pointer tracks the last hat on the list for
* htable_steal() to use.
*/
/*
* Allocate an htable hash bucket for the kernel
* XX64 - tune for 64 bit procs
*/
/*
* zero out the top level and cached htable pointers
*/
/*
* Pre-allocate hrm_hashtab before enabling the collection of
* refmod statistics. Allocating on the fly would mean us
* running the risk of suffering recursive mutex enters or
* deadlocks.
*/
KM_SLEEP);
}
/*
* Prepare CPU specific pagetables for VLP processes on 64 bit kernels.
*
* Each CPU has a set of 2 pagetables that are reused for any 32 bit
* process it runs. They are the top level pagetable, hci_vlp_l3ptes, and
* the next to top level table for the bottom 512 Gig, hci_vlp_l2ptes.
*/
/*ARGSUSED*/
static void
{
#if defined(__amd64)
/*
* allocate the level==2 page table for the bottom most
* 512Gig of address space (this is where 32 bit apps live)
*/
/*
* Allocate a top level pagetable and copy the kernel's
* entries into it. Then link in hci_vlp_l2ptes in the 1st entry.
*/
hci->hci_vlp_pfn =
khat_entries * sizeof (x86pte_t));
#endif /* __amd64 */
}
/*ARGSUSED*/
static void
{
#if defined(__amd64)
struct hat_cpu_info *hci;
return;
if (hci->hci_vlp_l2ptes)
if (hci->hci_vlp_l3ptes)
#endif /* __amd64 */
}
/*
* Finish filling in the kernel hat.
* Pre fill in all top level kernel page table entries for the kernel's
* part of the address range. From this point on we can't use any new
* kernel large pages if they need PTE's at max_level
*
* create the kmap mappings.
*/
void
hat_init_finish(void)
{
uint_t e;
#if defined(__i386)
/*
* Deal with kernelbase not 1Gig aligned for 32 bit PAE hats.
*/
} else {
if (PTE_ISVALID(pte))
continue;
NULL);
}
}
#endif
/*
* The kernel hat will need fixed values in the highest level
* ptable for copying to all other hat's. This implies
* alignment restrictions on _userlimit.
*
* Note we don't htable_release() these htables. This keeps them
* from ever being stolen or free'd.
*
* top_level_count is used instead of ptes_per_table, since
* on 32-bit PAE we only have 4 usable entries at the top level ptable.
*/
if (va == 0)
else
if (IN_HYPERVISOR_VA(va))
continue;
if (PTE_ISVALID(pte))
continue;
}
/*
* We are now effectively running on the kernel hat.
* Clearing use_boot_reserve shuts off using the pre-allocated boot
* reserve for all HAT allocations. From here on, the reserves are
* only used when mapping in memory for the hat's own allocations.
*/
use_boot_reserve = 0;
/*
* 32 bit kernels use only 4 of the 512 entries in its top level
* pagetable. We'll use the remainder for the "per CPU" page tables
* for VLP processes.
*
* We also map the top level kernel pagetable into the kernel to make
* it easy to use bcopy to initialize new address spaces.
*/
}
/*
* Create kmap (cached mappings of kernel PTEs)
* for 32 bit we map from segmap_start .. ekernelheap
* for 64 bit we map from segmap_start .. segmap_start + segmapsize;
*/
#if defined(__i386)
size = segmapsize;
#endif
}
/*
* On 32 bit PAE mode, PTE's are 64 bits, but ordinary atomic memory references
* are 32 bit, so for safety we must use cas64() to install these.
*/
#ifdef __i386
static void
{
int i;
/*
* Load the 4 entries of the level 2 page table into this
* cpu's range of the vlp_page and point cr3 at them.
*/
for (i = 0; i < VLP_NUM_PTES; ++i) {
for (;;) {
break;
break;
}
}
}
#endif
/*
* Switch to a new active hat, maintaining bit masks to track active CPUs.
*/
void
{
/*
* set up this information first, so we don't miss any cross calls
*/
return;
}
/*
* Add this CPU to the active set for this HAT.
*/
}
/*
* now go ahead and load cr3
*/
#if defined(__amd64)
#endif
} else {
}
}
/*
* Utility to return a valid x86pte_t from protections, pfn, and level number
*/
static x86pte_t
{
if (attr & PROT_WRITE)
/*
*/
if (flags & HAT_LOAD_NOCONSIST)
else if (attr & HAT_NOSYNC)
/*
* Set the caching attributes in the PTE. The combination
* of attributes are poorly defined, so we pay attention
* to them in the given order.
*
* The test for HAT_STRICTORDER is different because it's defined
* as "0" - which was a stupid thing to do, but is too late to change!
*/
if (cache_attr == HAT_STRICTORDER) {
/*LINTED [Lint hates empty ifs, but it's the obvious way to do this] */
/* nothing to set */;
if (x86_feature & X86_PAT)
else
} else {
}
return (pte);
}
/*
* Duplicate address translations of the parent to the child.
* This function really isn't used anymore.
*/
/*ARGSUSED*/
int
{
return (0);
}
/*
* Allocate any hat resources required for a process being swapped in.
*/
/*ARGSUSED*/
void
{
/* do nothing - we let everything fault back in */
}
/*
* Unload all translations associated with an address space of a process
* that is being swapped out.
*/
void
{
level_t l;
/*
* We can't just call hat_unload(hat, 0, _userlimit...) here, because
* seg_spt and shared pagetables can't be swapped out.
* Take a look at segspt_shmswapout() - it's a big no-op.
*
* Instead we'll walk through all the address space and unload
* any mappings which we are sure are not shared, not locked.
*/
break;
/*
* If the page table is shared skip its entire range.
* This code knows that only level 0 page tables are shared
*/
ASSERT(l == 0);
continue;
}
/*
* If the page table has no locked entries, unload this one.
*/
if (ht->ht_lock_cnt == 0)
/*
* If we have a level 0 page table with locked entries,
* skip the entire page table, otherwise skip just one entry.
*/
if (ht->ht_lock_cnt > 0 && l == 0)
else
vaddr += LEVEL_SIZE(l);
}
if (ht)
/*
* We're in swapout because the system is low on memory, so
* go back and flush all the htables off the cached list.
*/
}
/*
* returns number of bytes that have valid mappings in hat.
*/
{
int l;
for (l = 0; l <= mmu.max_page_level; l++)
return (total);
}
/*
*/
int
{
return (1);
}
void
{
}
/*
* We must be holding the mapping list lock when this is called.
*/
static void
{
return;
if (rm == 0)
return;
/*
* sync to all constituent pages of a large page
*/
/*
* hat_page_demote() can't decrease
* pszc below this mapping size
* since this large mapping existed after we
* took mlist lock.
*/
++pp;
}
}
/*
* This the set of PTE bits for PFN, permissions and caching
* that require a TLB flush (hat_tlb_inval) if changed on a HAT_LOAD_REMAP
*/
#define PT_REMAP_BITS \
/*
* Do the low-level work to get a mapping entered into a HAT's pagetables
* and in the mapping list of the associated page_t.
*/
static int
int flags,
void *pte_ptr)
{
int rv = 0;
/*
* Is this a consistant (ie. need mapping list lock) mapping?
*/
/*
* Track locked mapping count in the htable. Do this first,
* as we track locking even if there already is a mapping present.
*/
/*
* Acquire the page's mapping list lock and get an hment to use.
* Note that hment_prepare() might return NULL.
*/
if (is_consist) {
}
/*
* Set the new pte, retrieving the old one at the same time.
*/
/*
* did we get a large page / page table collision?
*/
if (old_pte == LPAGE_ERROR) {
rv = -1;
goto done;
}
/*
* If the mapping didn't change there is nothing more to do.
*/
goto done;
/*
* Install a new mapping in the page's mapping list
*/
if (!PTE_ISVALID(old_pte)) {
if (is_consist) {
} else {
}
return (rv);
}
/*
* Remap's are more complicated:
* - HAT_LOAD_REMAP must be specified if changing the pfn.
* We also require that NOCONSIST be specified.
* - Otherwise only permission or caching bits may change.
*/
if (!PTE_ISPAGE(old_pte, l))
}
/*
* We only let remaps change the bits for PFNs, permissions
* or caching type.
*/
/*
* We don't create any mapping list entries on a remap, so release
* any allocated hment after we drop the mapping list lock.
*/
done:
if (is_consist) {
hment_free(hm);
}
return (rv);
}
/*
* Internal routine to load a single page table entry. This only fails if
* we attempt to overwrite a page table link with a large page.
*/
static int
{
int rv = 0;
/*
* The number 16 is arbitrary and here to catch a recursion problem
* early before we blow out the kernel stack.
*/
++curthread->t_hatdepth;
if (flags & HAT_LOAD_SHARE)
/*
* Find the page table that maps this page if it already exists.
*/
/*
* We must have HAT_LOAD_NOCONSIST if page_t is NULL.
*/
}
/*
* a bunch of paranoid error checking
*/
/*
* construct the new PTE
*/
/*
* establish the mapping
*/
/*
* release the htable and any reserves
*/
--curthread->t_hatdepth;
return (rv);
}
/*
* special case of hat_memload to deal with some kernel addrs for performance
*/
static void
{
void *pte_ptr;
/*
* construct the requested PTE
*/
/*
* Figure out the pte_ptr and htable and use common code to finish up
*/
else
LEVEL_SHIFT(1)];
++curthread->t_hatdepth;
--curthread->t_hatdepth;
}
/*
* hat_memload() - load a translation to the given page struct
*
* Flags for hat_memload/hat_devload/hat_*attr.
*
* HAT_LOAD Default flags to load a translation to the page.
*
* HAT_LOAD_LOCK Lock down mapping resources; hat_map(), hat_memload(),
* and hat_devload().
*
* HAT_LOAD_NOCONSIST Do not add mapping to page_t mapping list.
* sets PT_NOCONSIST
*
* HAT_LOAD_SHARE A flag to hat_memload() to indicate h/w page tables
* that map some user pages (not kas) is shared by more
* than one process (eg. ISM).
*
* HAT_LOAD_REMAP Reload a valid pte with a different page frame.
*
* HAT_NO_KALLOC Do not kmem_alloc while creating the mapping; at this
* point, it's setting up mapping to allocate internal
* hat layer data structures. This flag forces hat layer
* to tap its reserves in order to prevent infinite
* recursion.
*
* The following is a protection attribute (like PROT_READ, etc.)
*
* are never cleared.
*
* Installing new valid PTE's and creation of the mapping list
* entry are controlled under the same lock. It's derived from the
* page_t being mapped.
*/
static uint_t supported_memload_flags =
void
{
/*
* kernel address special case for performance.
*/
return;
}
/*
* This is used for memory with normal caching enabled, so
* always set HAT_STORECACHING_OK.
*/
panic("unexpected hati_load_common() failure");
}
/* ARGSUSED */
void
{
}
/*
* Load the given array of page structs using large pages when possible
*/
void
{
pgcnt_t i;
/*
* memload is used for memory with full caching enabled, so
* set HAT_STORECACHING_OK.
*/
/*
* handle all pages using largest possible pagesize
*/
/*
* decide what level mapping to use (ie. pagesize)
*/
if (level == 0)
break;
continue;
/*
* To use a large mapping of this size, all the
* pages we are passed must be sequential subpages
* of the large page.
* hat_page_demote() can't change p_szc because
* all pages are locked.
*/
if (pfn + i !=
break;
level);
}
break;
}
}
/*
* Load this page mapping. If the load fails, try a smaller
* pagesize.
*/
if (level == 0)
panic("unexpected hati_load_common() failure");
--level;
}
/*
* move to next page
*/
}
}
/* ARGSUSED */
void
{
}
/*
* void hat_devload(hat, addr, len, pf, attr, flags)
*
* Advisory ordering attributes. Apply only to device mappings.
*
* HAT_STRICTORDER: the CPU must issue the references in order, as the
* programmer specified. This is the default.
* HAT_UNORDERED_OK: the CPU may reorder the references (this is all kinds
* of reordering; store or load with store or load).
* HAT_MERGING_OK: merging and batching: the CPU may merge individual stores
* to consecutive locations (for example, turn two consecutive byte
* stores into one halfword store), and it may batch individual loads
* (for example, turn two consecutive byte loads into one halfword load).
* This also implies re-ordering.
* HAT_LOADCACHING_OK: the CPU may cache the data it fetches and reuse it
* until another store occurs. The default is to fetch new data
* on every load. This also implies merging.
* HAT_STORECACHING_OK: the CPU may keep the data in the cache and push it to
* the device (perhaps with other data) at a later time. The default is
* to push the data right away. This also implies load caching.
*
* Equivalent of hat_memload(), but can be used for device memory where
* there are no page_t's and we support additional flags (write merging, etc).
* Note that we can have large page mappings with this interface.
*/
void
int flags)
{
int f; /* per PTE copy of flags - maybe modified */
uint_t a; /* per PTE copy of attr */
/*
* handle all pages
*/
/*
* decide what level mapping to use (ie. pagesize)
*/
if (level == 0)
break;
break;
}
/*
* If this is just memory then allow caching (this happens
* for the nucleus pages) - though HAT_PLAT_NOCACHE can be used
* to override that. If we don't have a page_t then make sure
* NOCONSIST is set.
*/
a = attr;
f = flags;
if (pf_is_memory(pfn)) {
if (!(a & HAT_PLAT_NOCACHE))
a |= HAT_STORECACHING_OK;
if (f & HAT_LOAD_NOCONSIST)
else
} else {
f |= HAT_LOAD_NOCONSIST;
}
/*
* load this page mapping
*/
if (level == 0)
panic("unexpected hati_load_common() failure");
--level;
}
/*
* move to next page
*/
}
}
/*
* void hat_unlock(hat, addr, len)
* unlock the mappings to a given range of addresses
*
* Locks are tracked by ht_lock_cnt in the htable.
*/
void
{
/*
* kernel entries are always locked, we don't track lock counts
*/
return;
if (eaddr > _userlimit)
panic("hat_unlock() address out of range - above _userlimit");
break;
panic("hat_unlock(): lock_cnt < 1, "
}
if (ht)
}
/* ARGSUSED */
void
{
panic("No shared region support on x86");
}
/*
* Cross call service routine to demap a virtual page on
* the current CPU or flush all mappings in TLB.
*/
/*ARGSUSED*/
static int
{
/*
* If the target hat isn't the kernel and this CPU isn't operating
* in the target hat, we can ignore the cross call.
*/
return (0);
/*
* For a normal address, we just flush one page mapping
*/
return (0);
}
/*
* Otherwise we reload cr3 to effect a complete TLB flush.
*
* A reload of cr3 on a VLP process also means we must also recopy in
* the pte values from the struct hat
*/
#if defined(__amd64)
#endif
}
reload_cr3();
return (0);
}
/*
* Flush all TLB entries, including global (ie. kernel) ones.
*/
static void
flush_all_tlb_entries(void)
{
/*
* 32 bit PAE also needs to always reload_cr3()
*/
reload_cr3();
} else {
reload_cr3();
}
}
#define TLB_CPU_HALTED (01ul)
#define TLB_INVAL_ALL (02ul)
/*
* Record that a CPU is going idle
*/
void
tlb_going_idle(void)
{
}
/*
* Service a delayed TLB flush if coming out of being idle.
*/
void
tlb_service(void)
{
/*
* Be sure interrupts are off while doing this so that
* higher level interrupts correctly wait for flushes to finish.
*/
flags = intr_clear();
/*
* We only have to do something if coming out of being idle.
*/
if (tlb_info & TLB_CPU_HALTED) {
/*
* Atomic clear and fetch of old state.
*/
SMT_PAUSE();
}
if (tlb_info & TLB_INVAL_ALL)
}
/*
* Restore interrupt enable control bit.
*/
sti();
}
/*
* Internal routine to do cross calls to invalidate a range of pages on
* all CPUs using a given hat.
*/
void
{
extern int flushes_require_xcalls; /* from mp_startup.c */
int c;
/*
* If the hat is being destroyed, there are no more users, so
* demap need not do anything.
*/
return;
/*
* If demapping from a shared pagetable, we best demap the
* entire set of user TLBs, since we don't know what addresses
* these were shared at.
*/
va = DEMAP_ALL_ADDR;
}
/*
* if not running with multiple CPUs, don't use cross calls
*/
if (panicstr || !flushes_require_xcalls) {
return;
}
/*
* Determine CPUs to shootdown. Kernel changes always do all CPUs.
* Otherwise it's just CPUs currently executing in this hat.
*/
else
/*
* If any CPUs in the set are idle, just request a delayed flush
* and avoid waking them up.
*/
if (!CPU_IN_SET(check_cpus, c))
continue;
CPUSET_DEL(check_cpus, c);
continue;
while (tlb_info == TLB_CPU_HALTED) {
SMT_PAUSE();
}
}
}
if (CPUSET_ISNULL(cpus_to_shootdown) ||
} else {
}
}
/*
* Interior routine for HAT_UNLOADs from hat_unload_callback(),
* hat_kmap_unload() OR from hat_steal() code. This routine doesn't
* handle releasing of the htables.
*/
void
void *pte_ptr)
{
/*
* We always track the locking counts, even if nothing is unmapped
*/
}
/*
* Figure out which page's mapping list lock to acquire using the PFN
* passed in "old" PTE. We then attempt to invalidate the PTE.
* If another thread, probably a hat_pageunload, has asynchronously
*/
while (PTE_ISVALID(old_pte)) {
} else {
panic("no page_t, not NOCONSIST: old_pte="
FMT_PTE " ht=%lx entry=0x%x pte_ptr=%lx",
}
}
/*
* If freeing the address space, check that the PTE
* hasn't changed, as the mappings are no longer in use by
* any thread, invalidation is unnecessary.
* If not freeing, do a full invalidate.
*/
else
/*
* If the page hadn't changed we've unmapped it and can proceed
*/
break;
/*
* Otherwise, we'll have to retry with the current old_pte.
* Drop the hment lock, since the pfn may have changed.
*/
} else {
}
}
/*
* If the old mapping wasn't valid, there's nothing more to do
*/
if (!PTE_ISVALID(old_pte)) {
return;
}
/*
*/
if (!(flags & HAT_UNLOAD_NOSYNC))
hment_free(hm);
}
/*
* Handle book keeping in the htable and hat
*/
}
/*
* very cheap unload implementation to special case some kernel addresses
*/
static void
{
/*
* Get the PTE
*/
/*
* get the htable / entry
*/
>> LEVEL_SHIFT(1)];
/*
* use mostly common code to unmap it.
*/
}
}
/*
* unload a range of virtual address space (no callback)
*/
void
{
/*
* special case for performance.
*/
} else {
}
}
/*
* Do the callbacks for ranges being unloaded.
*/
typedef struct range_info {
} range_info_t;
static void
{
/*
* do callbacks to upper level VM system
*/
--cnt;
cb->hcb_end_addr +=
}
}
/*
* Unload a given range of addresses (has optional callback)
*
* Flags:
* define HAT_UNLOAD 0x00
* define HAT_UNLOAD_NOSYNC 0x02
* define HAT_UNLOAD_UNLOCK 0x04
* define HAT_UNLOAD_OTHER 0x08 - not used
* define HAT_UNLOAD_UNMAP 0x10 - same as HAT_UNLOAD
*/
#define MAX_UNLOAD_CNT (8)
void
{
/*
* Special case a single page being unloaded for speed. This happens
* quite frequently, COW faults after a fork() for example.
*/
if (PTE_ISVALID(old_pte))
}
return;
}
break;
panic("hat_unload_callback(): unmap inside large page");
/*
* We'll do the call backs for contiguous ranges
*/
if (r_cnt == MAX_UNLOAD_CNT) {
r_cnt = 0;
}
++r_cnt;
}
/*
* Unload one mapping from the page tables.
*/
}
if (ht)
/*
* handle last range for callbacks
*/
if (r_cnt > 0)
}
/*
* synchronize mapping with software data structures
*
* This interface is currently only used by the working set monitor
* driver.
*/
/*ARGSUSED*/
void
{
break;
continue;
/*
* We need to acquire the mapping list lock to protect
* against hat_pageunload(), hat_unload(), etc.
*/
break;
goto try_again;
}
continue;
}
/*
* Need to clear ref or mod bits. We may compete with
* hardware updating the R/M bits and have to try again.
*/
if (flags == HAT_SYNC_ZERORM) {
if (pte != 0) {
goto try_again;
}
} else {
/*
* sync the PTE to the page_t
*/
}
}
if (ht)
}
/*
* void hat_map(hat, addr, len, flags)
*/
/*ARGSUSED*/
void
{
/* does nothing */
}
/*
* uint_t hat_getattr(hat, addr, *attr)
* returns attr for <hat,addr> in *attr. returns 0 if there was a
* mapping and *attr is valid, nonzero if there was no mapping and
* *attr is not valid.
*/
{
if (IN_VA_HOLE(vaddr))
return ((uint_t)-1);
return ((uint_t)-1);
return ((uint_t)-1);
}
*attr |= PROT_WRITE;
*attr |= HAT_NOSYNC;
return (0);
}
/*
* hat_updateattr() applies the given attribute change to an existing mapping
*/
#define HAT_LOAD_ATTR 1
#define HAT_SET_ATTR 2
#define HAT_CLR_ATTR 3
static void
{
break;
continue;
continue;
/*
* We found a page table entry in the desired range,
* figure out the new attributes.
*/
if ((attr & PROT_WRITE) &&
newpte |= PT_WRITABLE;
if ((attr & HAT_NOSYNC) &&
}
if (what == HAT_LOAD_ATTR) {
if (!(attr & PROT_WRITE) &&
newpte &= ~PT_WRITABLE;
if (!(attr & HAT_NOSYNC) &&
newpte &= ~PT_SOFTWARE;
}
if (what == HAT_CLR_ATTR) {
newpte &= ~PT_WRITABLE;
if ((attr & HAT_NOSYNC) &&
newpte &= ~PT_SOFTWARE;
}
/*
* x86pte_set() depends on this.
*/
/*
* what about PROT_READ or others? this code only handles:
* EXEC, WRITE, NOSYNC
*/
/*
* If new PTE really changed, update the table.
*/
if (oldpte != 0) {
goto try_again;
}
}
}
if (ht)
}
/*
* Various wrappers for hat_updateattr()
*/
void
{
}
void
{
}
void
{
}
void
{
}
/*
* size_t hat_getpagesize(hat, addr)
* returns pagesize in bytes for <hat, addr>. returns -1 of there is
* no mapping. This is an advisory call.
*/
{
if (IN_VA_HOLE(vaddr))
return (-1);
return (-1);
return (pagesize);
}
/*
* pfn_t hat_getpfnum(hat, addr)
* returns pfn for <hat, addr> or PFN_INVALID if mapping is invalid.
*/
{
if (khat_running == 0)
return (PFN_INVALID);
if (IN_VA_HOLE(vaddr))
return (PFN_INVALID);
/*
* A very common use of hat_getpfnum() is from the DDI for kernel pages.
* Use the kmap_ptes (which also covers the 32 bit heap) to speed
* this up.
*/
if (!PTE_ISVALID(pte))
return (PFN_INVALID);
/*LINTED [use of constant 0 causes a silly lint warning] */
}
return (PFN_INVALID);
return (pfn);
}
/*
* hat_getkpfnum() is an obsolete DDI routine, and its use is discouraged.
* Use hat_getpfnum(kas.a_hat, ...) instead.
*
* We'd like to return PFN_INVALID if the mappings have underlying page_t's
* but can't right now due to the fact that some software has grown to use
* this interface incorrectly. So for now when the interface is misused,
* return a warning to the user that in the future it won't work in the
* way they're abusing it, and carry on.
*
* Note that hat_getkpfnum() is never supported on amd64.
*/
#if !defined(__amd64)
{
int badcaller = 0;
if (khat_running == 0)
panic("hat_getkpfnum(): called too early\n");
return (PFN_INVALID);
badcaller = 1;
} else {
}
if (badcaller)
return (pfn);
}
#endif /* __amd64 */
/*
* int hat_probe(hat, addr)
* return 0 if no valid mapping is present. Faster version
* of hat_getattr in certain architectures.
*/
int
{
if (IN_VA_HOLE(vaddr))
return (0);
/*
* Most common use of hat_probe is from segmap. We special case it
* for performance.
*/
else
return (PTE_ISVALID(
}
return (0);
return (1);
}
/*
* Find out if the segment for hat_share()/hat_unshare() is DISM or locked ISM.
*/
static int
{
return (1);
return (0);
}
/*
* Simple implementation of ISM. hat_share() is similar to hat_memload_array(),
* except that we use the ism_hat's existing mappings to determine the pages
* and protections to use for this hat. If we find a full properly aligned
* and sized pagetable, we will attempt to share the pagetable itself.
*/
/*ARGSUSED*/
int
{
level_t l;
int is_dism;
int flags;
/*
* We might be asked to share an empty DISM hat by as_dup()
*/
return (0);
}
/*
* The SPT segment driver often passes us a size larger than there are
* valid mappings. That's because it rounds the segment size up to a
* large pagesize, even if the actual memory mapped by ism_hat is less.
*/
while (ism_addr < e_ism_addr) {
/*
* use htable_walk to get the next valid ISM mapping
*/
break;
/*
* First check to see if we already share the page table.
*/
goto shared;
goto not_shared;
}
/*
* Can't ever share top table.
*/
goto not_shared;
/*
* Avoid level mismatches later due to DISM faults.
*/
if (is_dism && l > 0)
goto not_shared;
/*
* addresses and lengths must align
* table must be fully populated
* no lower level page tables
*/
goto not_shared;
/*
* The range of address space must cover a full table.
*/
goto not_shared;
/*
* All entries in the ISM page table must be leaf PTEs.
*/
if (l > 0) {
int e;
/*
* We know the 0th is from htable_walk() above.
*/
if (!PTE_ISPAGE(pte, l))
goto not_shared;
}
}
/*
* share the page table
*/
hat->hat_ism_pgcnt +=
continue;
/*
* Unable to share the page table. Instead we will
* create new mappings from the values in the ISM mappings.
* Figure out what level size mappings to use;
*/
(vaddr & LEVEL_OFFSET(l)) == 0)
break;
}
/*
* The ISM mapping might be larger than the share area,
* be careful to truncate it if needed.
*/
} else {
l = 0;
}
while (pgcnt > 0) {
/*
* Make a new pte for the PFN for this level.
* Copy protections for the pte from the ISM pte.
*/
prot |= PROT_WRITE;
if (!is_dism)
l, pfn) != 0) {
if (l == 0)
panic("hati_load_common() failure");
--l;
}
vaddr += LEVEL_SIZE(l);
ism_addr += LEVEL_SIZE(l);
}
}
return (0);
}
/*
* hat_unshare() is similar to hat_unload_callback(), but
* we have to look for empty shared pagetables. Note that
* hat_unshare() is always invoked against an entire segment.
*/
/*ARGSUSED*/
void
{
uint_t need_demaps = 0;
int flags = HAT_UNLOAD_UNMAP;
level_t l;
/*
* First go through and remove any shared pagetables.
*
* Note that it's ok to delay the TLB shootdown till the entire range is
* finished, because if hat_pageunload() were to unload a shared
* pagetable page, its hat_tlb_inval() will do a global TLB invalidate.
*/
l = mmu.max_page_level;
--l;
for (; l >= 0; --l) {
/*
* find a pagetable that maps the current address
*/
continue;
/*
* clear page count, set valid_cnt to 0,
* let htable_release() finish the job
*/
ht->ht_valid_cnt = 0;
need_demaps = 1;
}
}
}
/*
* flush the TLBs - since we're probably dealing with MANY mappings
* we do just one CR3 reload.
*/
/*
* Now go back and clean up any unaligned mappings that
* couldn't share pagetables.
*/
}
/*
* hat_reserve() does nothing
*/
/*ARGSUSED*/
void
{
}
/*
* Called when all mappings to a page should have write permission removed.
* Mostly stolem from hat_pagesync()
*/
static void
{
/*
* walk thru the mapping list clearing write permission
*/
continue;
for (;;) {
/*
* Is this mapping of interest?
*/
break;
/*
* calls to ensure any executing TLBs see cleared bits.
*/
if (old != 0)
continue;
break;
}
}
pszc++;
goto next_size;
}
}
}
/*
* void hat_page_setattr(pp, flag)
* void hat_page_clrattr(pp, flag)
*/
void
{
int noshuffle;
return;
!noshuffle) {
}
/*
* Some File Systems examine v_pages for NULL w/o
* grabbing the vphm mutex. Must not let it become NULL when
* pp is the only page on the list.
*/
else
}
}
}
void
{
/*
* Caller is expected to hold page's io lock for VMODSORT to work
* correctly with pvn_vplist_dirty() and pvn_getdirty() when mod
* bit is cleared.
* We don't have assert to avoid tripping some existing third party
* code. The dirty page is moved back to top of the v_page list
* after IO is done in pvn_write_done().
*/
/*
* VMODSORT works by removing write permissions and getting
* a fault when a page is made dirty. At this point
* we need to remove write permission from all mappings
* to this page.
*/
}
}
/*
* If flag is specified, returns 0 if attribute is disabled
* and non zero if enabled. If flag specifes multiple attributs
* then returns 0 if ALL atriibutes are disabled. This is an advisory
* call.
*/
{
}
/*
* common code used by hat_pageunload() and hment_steal()
*/
hment_t *
{
/*
* We need to acquire a hold on the htable in order to
* do the invalidate. We know the htable must exist, since
* unmap's don't release the htable until after removing any
* hment. Having x86_hm_enter() keeps that from proceeding.
*/
/*
* Invalidate the PTE and remove the hment.
*/
" pfn being unmapped is %lx ht=0x%lx entry=0x%x",
}
/*
* Clean up all the htable information for this mapping
*/
/*
*/
/*
* Remove the mapping list entry for this page.
*/
/*
* drop the mapping list lock so that we might free the
* hment and htable.
*/
return (hm);
}
extern int vpm_enable;
/*
* Unload all translations to a page. If the page is a subpage of a large
* page, the large page mappings are also removed.
*
* The forceflags are unused.
*/
/*ARGSUSED*/
static int
{
#if defined(__amd64)
/*
* clear the vpm ref.
*/
if (vpm_enable) {
}
#endif
/*
* The loop with next_size handles pages with multiple pagesize mappings
*/
for (;;) {
/*
* Get a mapping list entry
*/
/*
* If not part of a larger page, we're done.
*/
return (0);
}
/*
* Else check the next larger page size.
* hat_page_demote() may decrease p_szc
* but that's ok we'll just take an extra
* trip discover there're no larger mappings
* and return.
*/
++pg_szcd;
goto next_size;
}
/*
* If this mapping size matches, remove it.
*/
break;
}
/*
* Remove the mapping list entry for this page.
* Note this does the x86_hm_exit() for us.
*/
hment_free(hm);
}
}
int
{
}
/*
* Unload all large mappings to pp and reduce by 1 p_szc field of every large
* page level that included pp.
*
* pp must be locked EXCL. Even though no other constituent pages are locked
* it's legal to unload large mappings to pp because all constituent pages of
* large locked mappings have to be locked SHARED. therefore if we have EXCL
* lock on one of constituent pages none of the large mappings to pp are
* locked.
*
* Change (always decrease) p_szc field starting from the last constituent
* page and ending with root constituent page so that root's pszc always shows
* the area where hat_page_demote() may be active.
*
* This mechanism is only used for file system pages where it's not always
* possible to get EXCL locks on all constituent pages to demote the size code
* (as is done for anonymous or kernel large pages).
*/
void
{
return;
/*
* all large mappings to pp are gone
* and no new can be setup since pp is locked exclusively.
*
* Lock the root to make sure there's only one hat_page_demote()
* outstanding within the area of this root's pszc.
*
* Second potential hat_page_demote() is already eliminated by upper
* VM layer via page_szc_lock() but we don't rely on it and use our
* own locking (so that upper layer locking can be changed without
* assumptions that hat depends on upper layer VM to prevent multiple
* hat_page_demote() to be issued simultaneously to the same large
* page).
*/
if (pszc == 0)
return;
/*
* If root's p_szc is different from pszc we raced with another
* hat_page_demote(). Drop the lock and try to find the root again.
* If root's p_szc is greater than pszc previous hat_page_demote() is
* not done yet. Take and release mlist lock of root's root to wait
* for previous hat_page_demote() to complete.
*/
/* p_szc of a locked non free page can't increase */
}
goto again;
}
/*
* Decrement by 1 p_szc of every constituent page of a region that
* covered pp. For example if original szc is 3 it gets changed to 2
* everywhere except in region 2 that covered pp. Region 2 that
* covered pp gets demoted to 1 everywhere except in region 1 that
* covered pp. The region 1 that covered pp is demoted to region
* 0. It's done this way because from region 3 we removed level 3
* mappings, from region 2 that covered pp we removed level 2 mappings
* and from region 1 that covered pp we removed level 1 mappings. All
* changes are done from from high pfn's to low pfn's so that roots
* are changed last allowing one to know the largest region where
* hat_page_demote() is stil active by only looking at the root page.
*
* This algorithm is implemented in 2 while loops. First loop changes
* p_szc of pages to the right of pp's level 1 region and second
* loop changes p_szc of pages of level 1 region that covers pp
* and all pages to the left of level 1 region that covers pp.
* In the first loop p_szc keeps dropping with every iteration
* and in the second loop it keeps increasing with every iteration.
*
* First loop description: Demote pages to the right of pp outside of
* level 1 region that covers pp. In every iteration of the while
* loop below find the last page of szc region and the first page of
* (szc - 1) region that is immediately to the right of (szc - 1)
* region that covers pp. From last such page to first such page
* change every page's szc to szc - 1. Decrement szc and continue
* looping until szc is 1. If pp belongs to the last (szc - 1) region
* of szc region skip to the next iteration.
*/
while (szc > 1) {
szc--;
continue;
}
lastpp--;
}
szc--;
}
/*
* Second loop description:
* First iteration changes p_szc to 0 of every
* page of level 1 region that covers pp.
* Subsequent iterations find last page of szc region
* immediately to the left of szc region that covered pp
* and first page of (szc + 1) region that covers pp.
* From last to first page change p_szc of every page to szc.
* Increment szc and continue looping until szc is pszc.
* If pp belongs to the fist szc region of (szc + 1) region
* skip to the next iteration.
*
*/
szc = 0;
if (szc == 0) {
} else {
szc++;
continue;
}
lastpp--;
}
lastpp--;
}
break;
szc++;
}
}
/*
* get hw stats from hardware into page struct and reset hw stats
* returns attributes of page
* Flags for hat_pagesync, hat_getstat, hat_sync
*
* define HAT_SYNC_ZERORM 0x01
*
* Additional flags for hat_pagesync
*
* define HAT_SYNC_STOPON_REF 0x02
* define HAT_SYNC_STOPON_MOD 0x04
* define HAT_SYNC_STOPON_RM 0x06
* define HAT_SYNC_STOPON_SHARED 0x08
*/
{
if ((flags & HAT_SYNC_ZERORM) == 0) {
if ((flags & HAT_SYNC_STOPON_SHARED) != 0 &&
}
}
/*
*/
continue;
continue;
if ((flags & HAT_SYNC_ZERORM) != 0) {
/*
* Need to clear ref or mod bits. Need to demap
* to make sure any executing TLBs see cleared bits.
*/
if (old != 0)
goto try_again;
}
/*
* Sync the PTE
*/
if (!(flags & HAT_SYNC_ZERORM) &&
/*
* can stop short if we found a ref'd or mod'd page
*/
goto done;
}
}
pszc++;
goto next_size;
}
}
done:
}
/*
* returns approx number of mappings to this pp. A return of 0 implies
* there are no mappings to the page.
*/
{
#if defined(__amd64)
cnt += 1;
}
#endif
return (cnt);
}
/*
* Return 1 the number of mappings exceeds sh_thresh. Return 0
* otherwise.
*/
int
{
}
/*
* hat_softlock isn't supported anymore
*/
/*ARGSUSED*/
struct page **page_array,
{
return (FC_NOSUPPORT);
}
/*
* Routine to expose supported HAT features to platform independent code.
*/
/*ARGSUSED*/
int
{
switch (feature) {
case HAT_SHARED_PT: /* this is really ISM */
return (1);
case HAT_DYNAMIC_ISM_UNMAP:
return (0);
case HAT_VMODSORT:
return (1);
case HAT_SHARED_REGIONS:
return (0);
default:
panic("hat_supported() - unknown feature");
}
return (0);
}
/*
* Called when a thread is exiting and has been switched to the kernel AS
*/
void
{
}
/*
* Setup the given brand new hat structure as the new HAT on this cpu's mmu.
*/
/*ARGSUSED*/
void
{
}
/*
* Prepare for a CPU private mapping for the given address.
*
* The address can only be used from a single CPU and can be remapped
* using hat_mempte_remap(). Return the address of the PTE.
*
* We do the htable_create() if necessary and increment the valid count so
* the htable can't disappear. We also hat_devload() the page table into
* kernel so that the PTE is quickly accessed.
*/
{
hat_mempte_t p;
++curthread->t_hatdepth;
}
if (PTE_ISVALID(oldpte))
panic("hat_mempte_setup(): address already mapped"
/*
* increment ht_valid_cnt so that the pagetable can't disappear
*/
/*
* return the PTE physical address to the caller.
*/
--curthread->t_hatdepth;
return (p);
}
/*
* Release a CPU private mapping for the given address.
* We decrement the htable valid count so it might be destroyed.
*/
/*ARGSUSED1*/
void
{
/*
* invalidate any left over mapping and decrement the htable valid count
*/
{
*pteptr = 0;
else
*(x86pte32_t *)pteptr = 0;
}
panic("hat_mempte_release(): invalid address");
}
/*
* Apply a temporary CPU private mapping to a page. We flush the TLB only
* on this CPU, so this ought to have been called with preemption disabled.
*/
void
{
/*
* Remap the given PTE to the new page's PFN. Invalidate only
* on this CPU.
*/
#ifdef DEBUG
#endif
{
else
}
}
/*
* Hat locking functions
* XXX - these two functions are currently being used by hatstats
* they can be removed by using a per-as mutex for hatstats.
*/
void
{
}
void
{
}
/*
* HAT part of cpu initialization.
*/
void
{
}
}
/*
* HAT part of cpu deletion.
* (currently, we only call this after the cpu is safely passivated.)
*/
void
{
}
/*
* Function called after all CPUs are brought online.
* Used to remove low address boot mappings.
*/
void
{
/*
* On 1st CPU we can unload the prom mappings, basically we blow away
* all virtual mappings under _userlimit.
*/
break;
/*
* Unload the mapping from the page tables.
*/
}
if (ht)
}
/*
* Atomically update a new translation for a single page. If the
* currently installed PTE doesn't match the value we expect to find,
* it's not updated and we return the PTE we found.
*
* If activating nosync or NOWRITE and the page was modified we need to sync
*/
static x86pte_t
{
}
return (replaced);
if (rm) {
/*
* sync to all constituent pages of a large page
*/
while (pgcnt-- > 0) {
/*
* hat_page_demote() can't decrease
* pszc below this mapping size
* since large mapping existed after we
* took mlist lock.
*/
++pp;
}
}
return (0);
}
/* ARGSUSED */
void
{
}
/* ARGSUSED */
void *r_obj,
{
panic("No shared region support on x86");
return (HAT_INVALID_REGION_COOKIE);
}
/* ARGSUSED */
void
{
panic("No shared region support on x86");
}
/* ARGSUSED */
void
{
panic("No shared region support on x86");
}
/*
* Kernel Physical Mapping (kpm) facility
*
* Most of the routines needed to support segkpm are almost no-ops on the
* x86 platform. We map in the entire segment when it is created and leave
* it mapped in, so there is no additional work required to set up and tear
* down individual mappings. All of these routines were created to support
* SPARC platforms that have to avoid aliasing in their virtually indexed
* caches.
*
* Most of the routines have sanity checks in them (e.g. verifying that the
* passed-in page is locked). We don't actually care about most of these
* checks on x86, but we leave them in place to identify problems in the
* upper levels.
*/
/*
* Map in a locked page and return the vaddr.
*/
/*ARGSUSED*/
{
#ifdef DEBUG
if (kpm_enable == 0) {
}
}
#endif
return (vaddr);
}
/*
* Mapout a locked page.
*/
/*ARGSUSED*/
void
{
#ifdef DEBUG
if (kpm_enable == 0) {
return;
}
if (IS_KPM_ADDR(vaddr) == 0) {
return;
}
return;
}
#endif
}
/*
* Return the kpm virtual address for a specific pfn
*/
{
}
/*
* Return the kpm virtual address for the page at pp.
*/
/*ARGSUSED*/
{
}
/*
* Return the page frame number for the kpm virtual address vaddr.
*/
{
return (pfn);
}
/*
* Return the page for the kpm virtual address vaddr.
*/
page_t *
{
return (page_numtopp_nolock(pfn));
}
/*
* hat_kpm_fault is called from segkpm_fault when we take a page fault on a
* KPM page. This should never happen on x86
*/
int
{
return (0);
}
/*ARGSUSED*/
void
{}
/*ARGSUSED*/
void
{}