#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/atomic.h>
#include <sys/bitmap.h>
#include <sys/machparam.h>
#include <sys/machsystm.h>
#include <sys/mman.h>
#include <sys/systm.h>
#include <sys/cpuvar.h>
#include <sys/thread.h>
#include <sys/proc.h>
#include <sys/cpu.h>
#include <sys/kmem.h>
#include <sys/disp.h>
#include <sys/vmem.h>
#include <sys/vmsystm.h>
#include <sys/promif.h>
#include <sys/var.h>
#include <sys/x86_archext.h>
#include <sys/archsystm.h>
#include <sys/bootconf.h>
#include <sys/dumphdr.h>
#include <vm/seg_kmem.h>
#include <vm/seg_kpm.h>
#include <vm/hat.h>
#include <vm/hat_i86.h>
#include <sys/cmn_err.h>
#include <sys/panic.h>
#ifdef __xpv
#include <sys/hypervisor.h>
#include <sys/xpv_panic.h>
#endif
#include <sys/bootinfo.h>
#include <vm/kboot_mmu.h>
static void x86pte_zero(htable_t *dest, uint_t entry, uint_t count);
kmem_cache_t *htable_cache;
#define HTABLE_RESERVE_AMOUNT (200)
uint_t htable_reserve_amount = HTABLE_RESERVE_AMOUNT;
kmutex_t htable_reserve_mutex;
uint_t htable_reserve_cnt;
htable_t *htable_reserve_pool;
#ifdef DEBUG
ulong_t force_steal = 0;
ulong_t ptable_cnt = 0;
#endif
uint_t htable_steal_passes = 8;
#define NUM_HTABLE_MUTEX 128
kmutex_t htable_mutex[NUM_HTABLE_MUTEX];
#define HTABLE_MUTEX_HASH(h) ((h) & (NUM_HTABLE_MUTEX - 1))
#define HTABLE_ENTER(h) mutex_enter(&htable_mutex[HTABLE_MUTEX_HASH(h)]);
#define HTABLE_EXIT(h) mutex_exit(&htable_mutex[HTABLE_MUTEX_HASH(h)]);
static void link_ptp(htable_t *higher, htable_t *new, uintptr_t vaddr);
static void unlink_ptp(htable_t *higher, htable_t *old, uintptr_t vaddr);
static void htable_free(htable_t *ht);
static x86pte_t *x86pte_access_pagetable(htable_t *ht, uint_t index);
static void x86pte_release_pagetable(htable_t *ht);
static x86pte_t x86pte_cas(htable_t *ht, uint_t entry, x86pte_t old,
x86pte_t new);
uint32_t htable_dont_cache = 0;
static uint32_t active_ptables = 0;
#ifdef __xpv
void
xen_flush_va(caddr_t va)
{
struct mmuext_op t;
uint_t count;
if (IN_XPV_PANIC()) {
mmu_flush_tlb_page((uintptr_t)va);
} else {
t.cmd = MMUEXT_INVLPG_LOCAL;
t.arg1.linear_addr = (uintptr_t)va;
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
}
void
xen_gflush_va(caddr_t va, cpuset_t cpus)
{
struct mmuext_op t;
uint_t count;
if (IN_XPV_PANIC()) {
mmu_flush_tlb_page((uintptr_t)va);
return;
}
t.cmd = MMUEXT_INVLPG_MULTI;
t.arg1.linear_addr = (uintptr_t)va;
set_xen_guest_handle(t.arg2.vcpumask, &cpus);
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
void
xen_flush_tlb()
{
struct mmuext_op t;
uint_t count;
if (IN_XPV_PANIC()) {
xpv_panic_reload_cr3();
} else {
t.cmd = MMUEXT_TLB_FLUSH_LOCAL;
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
}
void
xen_gflush_tlb(cpuset_t cpus)
{
struct mmuext_op t;
uint_t count;
ASSERT(!IN_XPV_PANIC());
t.cmd = MMUEXT_TLB_FLUSH_MULTI;
set_xen_guest_handle(t.arg2.vcpumask, &cpus);
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
int
xen_kpm_page(pfn_t pfn, uint_t how)
{
paddr_t pa = mmu_ptob((paddr_t)pfn);
x86pte_t pte = PT_NOCONSIST | PT_REF | PT_MOD;
if (kpm_vbase == NULL)
return (0);
if (how)
pte |= pa_to_ma(pa) | how;
else
pte = 0;
return (HYPERVISOR_update_va_mapping((uintptr_t)kpm_vbase + pa,
pte, UVMF_INVLPG | UVMF_ALL));
}
void
xen_pin(pfn_t pfn, level_t lvl)
{
struct mmuext_op t;
uint_t count;
t.cmd = MMUEXT_PIN_L1_TABLE + lvl;
t.arg1.mfn = pfn_to_mfn(pfn);
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
void
xen_unpin(pfn_t pfn)
{
struct mmuext_op t;
uint_t count;
t.cmd = MMUEXT_UNPIN_TABLE;
t.arg1.mfn = pfn_to_mfn(pfn);
if (HYPERVISOR_mmuext_op(&t, 1, &count, DOMID_SELF) < 0)
panic("HYPERVISOR_mmuext_op() failed");
ASSERT(count == 1);
}
static void
xen_map(uint64_t pte, caddr_t va)
{
if (HYPERVISOR_update_va_mapping((uintptr_t)va, pte,
UVMF_INVLPG | UVMF_LOCAL))
panic("HYPERVISOR_update_va_mapping() failed");
}
#endif
static pfn_t
ptable_alloc(uintptr_t seed)
{
pfn_t pfn;
page_t *pp;
pfn = PFN_INVALID;
if (!NOMEMWAIT() && freemem <= throttlefree + 1)
return (PFN_INVALID);
#ifdef DEBUG
if (proc_pageout != NULL && force_steal > 1 &&
++ptable_cnt > force_steal) {
ptable_cnt = 0;
return (PFN_INVALID);
}
#endif
pp = page_get_physical(seed);
if (pp == NULL)
return (PFN_INVALID);
ASSERT(PAGE_SHARED(pp));
pfn = pp->p_pagenum;
if (pfn == PFN_INVALID)
panic("ptable_alloc(): Invalid PFN!!");
atomic_inc_32(&active_ptables);
HATSTAT_INC(hs_ptable_allocs);
return (pfn);
}
static void
ptable_free(pfn_t pfn)
{
page_t *pp = page_numtopp_nolock(pfn);
ASSERT(pfn != PFN_INVALID);
HATSTAT_INC(hs_ptable_frees);
atomic_dec_32(&active_ptables);
if (pp == NULL)
panic("ptable_free(): no page for pfn!");
ASSERT(PAGE_SHARED(pp));
ASSERT(pfn == pp->p_pagenum);
ASSERT(!IN_XPV_PANIC());
if (!page_tryupgrade(pp)) {
u_offset_t off = pp->p_offset;
page_unlock(pp);
pp = page_lookup(&kvp, off, SE_EXCL);
if (pp == NULL)
panic("page not found");
}
#ifdef __xpv
if (kpm_vbase && xen_kpm_page(pfn, PT_VALID | PT_WRITABLE) < 0)
panic("failure making kpm r/w pfn=0x%lx", pfn);
#endif
page_hashout(pp, NULL);
page_free(pp, 1);
page_unresv(1);
}
static void
htable_put_reserve(htable_t *ht)
{
ht->ht_hat = NULL;
ASSERT(ht->ht_pfn == PFN_INVALID);
HATSTAT_INC(hs_htable_rputs);
mutex_enter(&htable_reserve_mutex);
ht->ht_next = htable_reserve_pool;
htable_reserve_pool = ht;
++htable_reserve_cnt;
mutex_exit(&htable_reserve_mutex);
}
static htable_t *
htable_get_reserve(void)
{
htable_t *ht = NULL;
mutex_enter(&htable_reserve_mutex);
if (htable_reserve_cnt != 0) {
ht = htable_reserve_pool;
ASSERT(ht != NULL);
ASSERT(ht->ht_pfn == PFN_INVALID);
htable_reserve_pool = ht->ht_next;
--htable_reserve_cnt;
HATSTAT_INC(hs_htable_rgets);
}
mutex_exit(&htable_reserve_mutex);
return (ht);
}
void
htable_initial_reserve(uint_t count)
{
htable_t *ht;
count += HTABLE_RESERVE_AMOUNT;
while (count > 0) {
ht = kmem_cache_alloc(htable_cache, KM_NOSLEEP);
ASSERT(ht != NULL);
ASSERT(use_boot_reserve);
ht->ht_pfn = PFN_INVALID;
htable_put_reserve(ht);
--count;
}
}
void
htable_adjust_reserve()
{
htable_t *ht;
while (htable_reserve_cnt > htable_reserve_amount &&
!USE_HAT_RESERVES()) {
ht = htable_get_reserve();
if (ht == NULL)
return;
ASSERT(ht->ht_pfn == PFN_INVALID);
kmem_cache_free(htable_cache, ht);
}
}
static void
htable_steal_active(hat_t *hat, uint_t cnt, uint_t threshold,
uint_t *stolen, htable_t **list)
{
static uint_t h_seed = 0;
htable_t *higher, *ht;
uint_t h, e, h_start;
uintptr_t va;
x86pte_t pte;
h = h_start = h_seed++ % hat->hat_num_hash;
do {
higher = NULL;
HTABLE_ENTER(h);
for (ht = hat->hat_ht_hash[h]; ht; ht = ht->ht_next) {
if (ht->ht_busy != 0 ||
(ht->ht_flags & HTABLE_SHARED_PFN) ||
ht->ht_level > 0 || ht->ht_valid_cnt > threshold ||
ht->ht_lock_cnt != 0)
continue;
++ht->ht_busy;
HTABLE_EXIT(h);
for (e = 0, va = ht->ht_vaddr;
e < HTABLE_NUM_PTES(ht) && ht->ht_valid_cnt > 0 &&
ht->ht_busy == 1 && ht->ht_lock_cnt == 0;
++e, va += MMU_PAGESIZE) {
pte = x86pte_get(ht, e);
if (!PTE_ISVALID(pte))
continue;
hat_pte_unmap(ht, e, HAT_UNLOAD, pte, NULL,
B_TRUE);
}
HTABLE_ENTER(h);
if (ht->ht_busy != 1 || ht->ht_valid_cnt != 0 ||
ht->ht_lock_cnt != 0) {
--ht->ht_busy;
continue;
}
higher = ht->ht_parent;
unlink_ptp(higher, ht, ht->ht_vaddr);
if (ht->ht_next)
ht->ht_next->ht_prev = ht->ht_prev;
if (ht->ht_prev) {
ht->ht_prev->ht_next = ht->ht_next;
} else {
ASSERT(hat->hat_ht_hash[h] == ht);
hat->hat_ht_hash[h] = ht->ht_next;
}
ht->ht_next = *list;
*list = ht;
++*stolen;
break;
}
HTABLE_EXIT(h);
if (higher != NULL)
htable_release(higher);
if (++h == hat->hat_num_hash)
h = 0;
} while (*stolen < cnt && h != h_start);
}
static void
move_victim(hat_t *hat)
{
ASSERT(MUTEX_HELD(&hat_list_lock));
if (hat->hat_prev)
hat->hat_prev->hat_next = hat->hat_next;
else
kas.a_hat->hat_next = hat->hat_next;
if (hat->hat_next)
hat->hat_next->hat_prev = hat->hat_prev;
else
kas.a_hat->hat_prev = hat->hat_prev;
hat->hat_next = NULL;
hat->hat_prev = kas.a_hat->hat_prev;
if (hat->hat_prev)
hat->hat_prev->hat_next = hat;
else
kas.a_hat->hat_next = hat;
kas.a_hat->hat_prev = hat;
}
static htable_t *
htable_steal(uint_t cnt, boolean_t reap)
{
hat_t *hat = kas.a_hat;
htable_t *list = NULL;
htable_t *ht;
uint_t stolen = 0;
uint_t pass, passes;
uint_t threshold;
if (htable_steal_passes == 0)
htable_steal_passes = 1;
if (htable_steal_passes > mmu.ptes_per_table)
htable_steal_passes = mmu.ptes_per_table;
passes = reap ? 0 : htable_steal_passes;
atomic_inc_32(&htable_dont_cache);
for (pass = 0; pass <= passes && stolen < cnt; ++pass) {
threshold = pass * mmu.ptes_per_table / htable_steal_passes;
mutex_enter(&hat_list_lock);
hat = kas.a_hat->hat_next;
for (;;) {
while (hat != NULL && (hat->hat_flags &
(HAT_VICTIM | HAT_SHARED | HAT_FREEING |
HAT_PCP)) != 0) {
hat = hat->hat_next;
}
if (hat == NULL)
break;
hat->hat_flags |= HAT_VICTIM;
mutex_exit(&hat_list_lock);
hat_enter(hat);
while ((ht = hat->hat_ht_cached) != NULL &&
stolen < cnt) {
hat->hat_ht_cached = ht->ht_next;
ht->ht_next = list;
list = ht;
++stolen;
}
hat_exit(hat);
if (pass != 0 && (stolen < cnt))
htable_steal_active(hat, cnt, threshold,
&stolen, &list);
for (ht = list; (ht) && (reap); ht = ht->ht_next) {
if (ht->ht_hat == NULL)
continue;
ASSERT(ht->ht_hat == hat);
#if defined(__xpv)
ASSERT(!(ht->ht_flags & HTABLE_COPIED));
if (ht->ht_level == mmu.max_level) {
ptable_free(hat->hat_user_ptable);
hat->hat_user_ptable = PFN_INVALID;
}
#endif
ht->ht_hat = NULL;
}
mutex_enter(&hat_list_lock);
if (stolen == cnt) {
if (pass >= 1 && cnt == 1 &&
kas.a_hat->hat_prev != hat)
move_victim(hat);
}
if (hat->hat_flags & HAT_VICTIM) {
ASSERT(hat != kas.a_hat);
hat->hat_flags &= ~HAT_VICTIM;
cv_broadcast(&hat_list_cv);
}
hat = hat->hat_next;
}
mutex_exit(&hat_list_lock);
}
ASSERT(!MUTEX_HELD(&hat_list_lock));
atomic_dec_32(&htable_dont_cache);
return (list);
}
static void
htable_reap(void *handle)
{
uint_t reap_cnt;
htable_t *list;
htable_t *ht;
HATSTAT_INC(hs_reap_attempts);
if (!can_steal_post_boot)
return;
reap_cnt = MAX(MIN(physmem / 20, active_ptables / 20), 10);
atomic_inc_32(&htable_dont_cache);
XPV_DISALLOW_MIGRATE();
list = htable_steal(reap_cnt, B_TRUE);
XPV_ALLOW_MIGRATE();
while ((ht = list) != NULL) {
list = ht->ht_next;
HATSTAT_INC(hs_reaped);
htable_free(ht);
}
atomic_dec_32(&htable_dont_cache);
htable_adjust_reserve();
hment_adjust_reserve();
}
static htable_t *
htable_alloc(
hat_t *hat,
uintptr_t vaddr,
level_t level,
htable_t *shared)
{
htable_t *ht = NULL;
uint_t is_copied;
uint_t is_bare = 0;
uint_t need_to_zero = 1;
int kmflags = (can_steal_post_boot ? KM_NOSLEEP : KM_SLEEP);
if (level < 0 || level > TOP_LEVEL(hat))
panic("htable_alloc(): level %d out of range\n", level);
is_copied = (hat->hat_flags & HAT_COPIED) &&
level == hat->hat_max_level;
if (is_copied || shared != NULL)
is_bare = 1;
if (hat->hat_ht_cached != NULL && !is_bare) {
hat_enter(hat);
ht = hat->hat_ht_cached;
if (ht != NULL) {
hat->hat_ht_cached = ht->ht_next;
need_to_zero = 0;
ASSERT(ht->ht_pfn != PFN_INVALID);
}
hat_exit(hat);
}
if (ht == NULL) {
if (USE_HAT_RESERVES()) {
ht = htable_get_reserve();
} else {
for (;;) {
ht = kmem_cache_alloc(htable_cache, kmflags);
if (ht == NULL)
break;
ht->ht_pfn = PFN_INVALID;
if (USE_HAT_RESERVES() ||
htable_reserve_cnt >= htable_reserve_amount)
break;
htable_put_reserve(ht);
}
}
if (ht != NULL && !is_bare) {
ht->ht_hat = hat;
ht->ht_pfn = ptable_alloc((uintptr_t)ht);
if (ht->ht_pfn == PFN_INVALID) {
if (USE_HAT_RESERVES())
htable_put_reserve(ht);
else
kmem_cache_free(htable_cache, ht);
ht = NULL;
}
}
}
while (ht == NULL && can_steal_post_boot) {
kmem_reap();
ht = htable_steal(1, B_FALSE);
HATSTAT_INC(hs_steals);
if (ht != NULL) {
if (is_bare) {
ptable_free(ht->ht_pfn);
ht->ht_pfn = PFN_INVALID;
#if defined(__xpv)
} else if (kpm_vbase && xen_kpm_page(ht->ht_pfn,
PT_VALID | PT_WRITABLE) < 0) {
panic("failure making kpm r/w pfn=0x%lx",
ht->ht_pfn);
#endif
}
}
}
if (ht == NULL)
panic("htable_alloc(): couldn't steal\n");
#if defined(__xpv)
if (level == mmu.max_level) {
for (;;) {
htable_t *stolen;
hat->hat_user_ptable = ptable_alloc((uintptr_t)ht + 1);
if (hat->hat_user_ptable != PFN_INVALID)
break;
stolen = htable_steal(1, B_FALSE);
if (stolen == NULL)
panic("2nd steal ptable failed\n");
htable_free(stolen);
}
block_zero_no_xmm(kpm_vbase + pfn_to_pa(hat->hat_user_ptable),
MMU_PAGESIZE);
}
#endif
ht->ht_flags = 0;
if (shared != NULL) {
ASSERT(shared->ht_valid_cnt > 0);
ht->ht_flags |= HTABLE_SHARED_PFN;
ht->ht_pfn = shared->ht_pfn;
ht->ht_lock_cnt = 0;
ht->ht_valid_cnt = 0;
ht->ht_shares = shared;
need_to_zero = 0;
} else {
ht->ht_shares = NULL;
ht->ht_lock_cnt = 0;
ht->ht_valid_cnt = 0;
}
if (is_copied) {
ht->ht_flags |= HTABLE_COPIED;
ASSERT(ht->ht_pfn == PFN_INVALID);
need_to_zero = 0;
}
ht->ht_hat = hat;
ht->ht_parent = NULL;
ht->ht_vaddr = vaddr;
ht->ht_level = level;
ht->ht_busy = 1;
ht->ht_next = NULL;
ht->ht_prev = NULL;
if (need_to_zero)
x86pte_zero(ht, 0, mmu.ptes_per_table);
#if defined(__xpv)
if (!is_bare && kpm_vbase) {
(void) xen_kpm_page(ht->ht_pfn, PT_VALID);
if (level == mmu.max_level)
(void) xen_kpm_page(hat->hat_user_ptable, PT_VALID);
}
#endif
return (ht);
}
static void
htable_free(htable_t *ht)
{
hat_t *hat = ht->ht_hat;
if (hat != NULL &&
!(ht->ht_flags & HTABLE_SHARED_PFN) &&
(use_boot_reserve ||
(!(hat->hat_flags & HAT_FREEING) && !htable_dont_cache))) {
ASSERT((ht->ht_flags & HTABLE_COPIED) == 0);
ASSERT(ht->ht_pfn != PFN_INVALID);
hat_enter(hat);
ht->ht_next = hat->hat_ht_cached;
hat->hat_ht_cached = ht;
hat_exit(hat);
return;
}
if (ht->ht_flags & HTABLE_SHARED_PFN) {
ASSERT(ht->ht_pfn != PFN_INVALID);
} else if (!(ht->ht_flags & HTABLE_COPIED)) {
ptable_free(ht->ht_pfn);
#if defined(__xpv)
if (ht->ht_level == mmu.max_level && hat != NULL) {
ptable_free(hat->hat_user_ptable);
hat->hat_user_ptable = PFN_INVALID;
}
#endif
}
ht->ht_pfn = PFN_INVALID;
if (USE_HAT_RESERVES() || htable_reserve_cnt < htable_reserve_amount) {
htable_put_reserve(ht);
} else {
kmem_cache_free(htable_cache, ht);
htable_adjust_reserve();
}
}
void
htable_purge_hat(hat_t *hat)
{
htable_t *ht;
int h;
if (!(hat->hat_flags & HAT_FREEING)) {
atomic_inc_32(&htable_dont_cache);
for (;;) {
hat_enter(hat);
ht = hat->hat_ht_cached;
if (ht == NULL) {
hat_exit(hat);
break;
}
hat->hat_ht_cached = ht->ht_next;
hat_exit(hat);
htable_free(ht);
}
atomic_dec_32(&htable_dont_cache);
return;
}
while ((ht = hat->hat_ht_cached) != NULL) {
hat->hat_ht_cached = ht->ht_next;
htable_free(ht);
}
for (h = 0; h < hat->hat_num_hash; ++h) {
while ((ht = hat->hat_ht_hash[h]) != NULL) {
if (ht->ht_next)
ht->ht_next->ht_prev = ht->ht_prev;
if (ht->ht_prev) {
ht->ht_prev->ht_next = ht->ht_next;
} else {
ASSERT(hat->hat_ht_hash[h] == ht);
hat->hat_ht_hash[h] = ht->ht_next;
}
htable_free(ht);
}
}
}
static void
unlink_ptp(htable_t *higher, htable_t *old, uintptr_t vaddr)
{
uint_t entry = htable_va2entry(vaddr, higher);
x86pte_t expect = MAKEPTP(old->ht_pfn, old->ht_level);
x86pte_t found;
hat_t *hat = old->ht_hat;
ASSERT(higher->ht_busy > 0);
ASSERT(higher->ht_valid_cnt > 0);
ASSERT(old->ht_valid_cnt == 0);
found = x86pte_cas(higher, entry, expect, 0);
#ifdef __xpv
if (found != expect && found != 0)
#else
if (found != expect)
#endif
panic("Bad PTP found=" FMT_PTE ", expected=" FMT_PTE,
found, expect);
if (!(hat->hat_flags & HAT_FREEING)) {
hat_tlb_inval(hat, (higher->ht_flags & HTABLE_COPIED) ?
DEMAP_ALL_ADDR : old->ht_vaddr);
}
HTABLE_DEC(higher->ht_valid_cnt);
}
static void
link_ptp(htable_t *higher, htable_t *new, uintptr_t vaddr)
{
uint_t entry = htable_va2entry(vaddr, higher);
x86pte_t newptp = MAKEPTP(new->ht_pfn, new->ht_level);
x86pte_t found;
ASSERT(higher->ht_busy > 0);
ASSERT(new->ht_level != mmu.max_level);
HTABLE_INC(higher->ht_valid_cnt);
found = x86pte_cas(higher, entry, 0, newptp);
if ((found & ~PT_REF) != 0)
panic("HAT: ptp not 0, found=" FMT_PTE, found);
if ((higher->ht_flags & HTABLE_COPIED) != 0)
hat_tlb_inval(higher->ht_hat, DEMAP_ALL_ADDR);
}
void
htable_release(htable_t *ht)
{
uint_t hashval;
htable_t *shared;
htable_t *higher;
hat_t *hat;
uintptr_t va;
level_t level;
while (ht != NULL) {
shared = NULL;
for (;;) {
hat = ht->ht_hat;
va = ht->ht_vaddr;
level = ht->ht_level;
hashval = HTABLE_HASH(hat, va, level);
HTABLE_ENTER(hashval);
ASSERT(ht->ht_valid_cnt >= 0);
ASSERT(ht->ht_busy > 0);
if (ht->ht_valid_cnt > 0)
break;
if (ht->ht_busy > 1)
break;
ASSERT(ht->ht_lock_cnt == 0);
#if !defined(__xpv)
if (!(ht->ht_flags & HTABLE_SHARED_PFN)) {
if (hat->hat_flags & HAT_FREEING)
break;
if (level >= mmu.max_page_level &&
(hat != kas.a_hat || va >= kernelbase))
break;
}
#endif
if (ht->ht_flags & HTABLE_SHARED_PFN) {
ASSERT(shared == NULL);
shared = ht->ht_shares;
HATSTAT_INC(hs_htable_unshared);
}
higher = ht->ht_parent;
ASSERT(higher != NULL);
unlink_ptp(higher, ht, va);
if (ht->ht_next)
ht->ht_next->ht_prev = ht->ht_prev;
if (ht->ht_prev) {
ht->ht_prev->ht_next = ht->ht_next;
} else {
ASSERT(hat->hat_ht_hash[hashval] == ht);
hat->hat_ht_hash[hashval] = ht->ht_next;
}
HTABLE_EXIT(hashval);
htable_free(ht);
ht = higher;
}
ASSERT(ht->ht_busy >= 1);
--ht->ht_busy;
HTABLE_EXIT(hashval);
ht = shared;
}
}
htable_t *
htable_lookup(hat_t *hat, uintptr_t vaddr, level_t level)
{
uintptr_t base;
uint_t hashval;
htable_t *ht = NULL;
ASSERT(level >= 0);
ASSERT(level <= TOP_LEVEL(hat));
if (level == TOP_LEVEL(hat)) {
if ((hat->hat_flags & HAT_COPIED_32) &&
vaddr >= ((uint64_t)1 << 32))
return (NULL);
base = 0;
} else {
base = vaddr & LEVEL_MASK(level + 1);
}
hashval = HTABLE_HASH(hat, base, level);
HTABLE_ENTER(hashval);
for (ht = hat->hat_ht_hash[hashval]; ht; ht = ht->ht_next) {
if (ht->ht_hat == hat &&
ht->ht_vaddr == base &&
ht->ht_level == level)
break;
}
if (ht)
++ht->ht_busy;
HTABLE_EXIT(hashval);
return (ht);
}
void
htable_acquire(htable_t *ht)
{
hat_t *hat = ht->ht_hat;
level_t level = ht->ht_level;
uintptr_t base = ht->ht_vaddr;
uint_t hashval = HTABLE_HASH(hat, base, level);
HTABLE_ENTER(hashval);
#ifdef DEBUG
{
htable_t *h;
for (h = hat->hat_ht_hash[hashval];
h && h != ht;
h = h->ht_next)
;
ASSERT(h == ht);
}
#endif
++ht->ht_busy;
HTABLE_EXIT(hashval);
}
htable_t *
htable_create(
hat_t *hat,
uintptr_t vaddr,
level_t level,
htable_t *shared)
{
uint_t h;
level_t l;
uintptr_t base;
htable_t *ht;
htable_t *higher = NULL;
htable_t *new = NULL;
if (level < 0 || level > TOP_LEVEL(hat))
panic("htable_create(): level %d out of range\n", level);
ht = NULL;
for (l = TOP_LEVEL(hat); l >= level; --l) {
new = NULL;
if (l == TOP_LEVEL(hat))
base = 0;
else
base = vaddr & LEVEL_MASK(l + 1);
h = HTABLE_HASH(hat, base, l);
try_again:
HTABLE_ENTER(h);
if (l == TOP_LEVEL(hat)) {
ht = hat->hat_htable;
} else {
for (ht = hat->hat_ht_hash[h]; ht; ht = ht->ht_next) {
ASSERT(ht->ht_hat == hat);
if (ht->ht_vaddr == base &&
ht->ht_level == l)
break;
}
}
if (ht != NULL) {
if (l == level && shared && ht->ht_shares &&
ht->ht_shares != shared) {
panic("htable shared from wrong place "
"found htable=%p shared=%p",
(void *)ht, (void *)shared);
}
++ht->ht_busy;
HTABLE_EXIT(h);
if (new)
htable_free(new);
if (higher != NULL)
htable_release(higher);
higher = ht;
} else if (new == NULL) {
HTABLE_EXIT(h);
new = htable_alloc(hat, base, l,
l == level ? shared : NULL);
goto try_again;
} else {
ht = new;
if (higher != NULL) {
link_ptp(higher, ht, base);
ht->ht_parent = higher;
}
ht->ht_next = hat->hat_ht_hash[h];
ASSERT(ht->ht_prev == NULL);
if (hat->hat_ht_hash[h])
hat->hat_ht_hash[h]->ht_prev = ht;
hat->hat_ht_hash[h] = ht;
HTABLE_EXIT(h);
higher = ht;
if (l == level && shared) {
(void) htable_lookup(shared->ht_hat,
shared->ht_vaddr, shared->ht_level);
HATSTAT_INC(hs_htable_shared);
}
}
}
return (ht);
}
void
htable_attach(
hat_t *hat,
uintptr_t base,
level_t level,
htable_t *parent,
pfn_t pfn)
{
htable_t *ht;
uint_t h;
uint_t i;
x86pte_t pte;
x86pte_t *ptep;
page_t *pp;
extern page_t *boot_claim_page(pfn_t);
ht = htable_get_reserve();
if (level == mmu.max_level)
kas.a_hat->hat_htable = ht;
ht->ht_hat = hat;
ht->ht_parent = parent;
ht->ht_vaddr = base;
ht->ht_level = level;
ht->ht_busy = 1;
ht->ht_next = NULL;
ht->ht_prev = NULL;
ht->ht_flags = 0;
ht->ht_pfn = pfn;
ht->ht_lock_cnt = 0;
ht->ht_valid_cnt = 0;
if (parent != NULL)
++parent->ht_busy;
h = HTABLE_HASH(hat, base, level);
HTABLE_ENTER(h);
ht->ht_next = hat->hat_ht_hash[h];
ASSERT(ht->ht_prev == NULL);
if (hat->hat_ht_hash[h])
hat->hat_ht_hash[h]->ht_prev = ht;
hat->hat_ht_hash[h] = ht;
HTABLE_EXIT(h);
if (page_resv(1, KM_NOSLEEP) == 0)
panic("page_resv() failed in ptable alloc");
pp = boot_claim_page(pfn);
ASSERT(pp != NULL);
if (pp->p_vnode == NULL) {
u_offset_t offset = (uintptr_t)ht;
if (offset > kernelbase)
offset -= kernelbase;
offset <<= MMU_PAGESHIFT;
offset += mmu.hole_start;
ASSERT(page_exists(&kvp, offset) == NULL);
(void) page_hashin(pp, &kvp, offset, NULL);
}
page_downgrade(pp);
#if defined(__xpv)
if (kpm_vbase)
pp->p_index = 1;
#endif
ptep = kbm_remap_window(pfn_to_pa(pfn), 0);
for (i = 0; i < HTABLE_NUM_PTES(ht); ++i) {
if (mmu.pae_hat)
pte = ptep[i];
else
pte = ((x86pte32_t *)ptep)[i];
if (!IN_HYPERVISOR_VA(base) && PTE_ISVALID(pte)) {
++ht->ht_valid_cnt;
if (!PTE_ISPAGE(pte, level)) {
htable_attach(hat, base, level - 1,
ht, PTE2PFN(pte, level));
ptep = kbm_remap_window(pfn_to_pa(pfn), 0);
}
}
base += LEVEL_SIZE(level);
if (base == mmu.hole_start)
base = (mmu.hole_end + MMU_PAGEOFFSET) & MMU_PAGEMASK;
}
if (base < kernelbase)
htable_release(ht);
}
static x86pte_t
htable_scan(htable_t *ht, uintptr_t *vap, uintptr_t eaddr)
{
uint_t e;
x86pte_t found_pte = (x86pte_t)0;
caddr_t pte_ptr;
caddr_t end_pte_ptr;
int l = ht->ht_level;
uintptr_t va = *vap & LEVEL_MASK(l);
size_t pgsize = LEVEL_SIZE(l);
ASSERT(va >= ht->ht_vaddr);
ASSERT(va <= HTABLE_LAST_PAGE(ht));
e = htable_va2entry(va, ht);
pte_ptr = (caddr_t)x86pte_access_pagetable(ht, 0);
end_pte_ptr = (caddr_t)PT_INDEX_PTR(pte_ptr, HTABLE_NUM_PTES(ht));
pte_ptr = (caddr_t)PT_INDEX_PTR((x86pte_t *)pte_ptr, e);
while (!PTE_ISVALID(*pte_ptr)) {
va += pgsize;
if (va >= eaddr)
break;
pte_ptr += mmu.pte_size;
ASSERT(pte_ptr <= end_pte_ptr);
if (pte_ptr == end_pte_ptr)
break;
}
if (va < eaddr && pte_ptr != end_pte_ptr)
found_pte = GET_PTE((x86pte_t *)pte_ptr);
x86pte_release_pagetable(ht);
if (l == mmu.max_level && va >= mmu.hole_start && va <= mmu.hole_end)
va = mmu.hole_end + va - mmu.hole_start;
*vap = va;
return (found_pte);
}
x86pte_t
htable_walk(
struct hat *hat,
htable_t **htp,
uintptr_t *vaddr,
uintptr_t eaddr)
{
uintptr_t va = *vaddr;
htable_t *ht;
htable_t *prev = *htp;
level_t l;
level_t max_mapped_level;
x86pte_t pte;
ASSERT(eaddr > va);
ASSERT(hat == kas.a_hat || eaddr <= kernelbase ||
eaddr == HTABLE_WALK_TO_END);
if (hat != kas.a_hat && eaddr == HTABLE_WALK_TO_END)
eaddr = kernelbase;
if (prev) {
ASSERT(prev->ht_busy > 0);
ASSERT(prev->ht_vaddr <= va);
l = prev->ht_level;
if (va <= HTABLE_LAST_PAGE(prev)) {
pte = htable_scan(prev, &va, eaddr);
if (PTE_ISPAGE(pte, l)) {
*vaddr = va;
*htp = prev;
return (pte);
}
}
htable_release(prev);
}
if (hat->hat_ism_pgcnt > 0) {
max_mapped_level = mmu.umax_page_level;
} else {
max_mapped_level = 0;
for (l = 1; l <= mmu.max_page_level; ++l)
if (hat->hat_pages_mapped[l] != 0)
max_mapped_level = l;
}
while (va < eaddr && va >= *vaddr) {
for (l = 0; l <= TOP_LEVEL(hat); ++l) {
ht = htable_lookup(hat, va, l);
if (ht != NULL) {
pte = htable_scan(ht, &va, eaddr);
if (PTE_ISPAGE(pte, l)) {
VERIFY(!IN_VA_HOLE(va));
*vaddr = va;
*htp = ht;
return (pte);
}
htable_release(ht);
break;
}
ASSERT(l < TOP_LEVEL(hat));
if (l >= max_mapped_level) {
va = NEXT_ENTRY_VA(va, l + 1);
if (va >= eaddr)
break;
}
}
}
*vaddr = 0;
*htp = NULL;
return (0);
}
htable_t *
htable_getpte(
struct hat *hat,
uintptr_t vaddr,
uint_t *entry,
x86pte_t *pte,
level_t level)
{
htable_t *ht;
level_t l;
uint_t e;
ASSERT(level <= mmu.max_page_level);
for (l = 0; l <= level; ++l) {
ht = htable_lookup(hat, vaddr, l);
if (ht == NULL)
continue;
e = htable_va2entry(vaddr, ht);
if (entry != NULL)
*entry = e;
if (pte != NULL)
*pte = x86pte_get(ht, e);
return (ht);
}
return (NULL);
}
htable_t *
htable_getpage(struct hat *hat, uintptr_t vaddr, uint_t *entry)
{
htable_t *ht;
uint_t e;
x86pte_t pte;
ht = htable_getpte(hat, vaddr, &e, &pte, mmu.max_page_level);
if (ht == NULL)
return (NULL);
if (entry)
*entry = e;
if (PTE_ISPAGE(pte, ht->ht_level))
return (ht);
htable_release(ht);
return (NULL);
}
void
htable_init()
{
int kmem_flags = KMC_NOHASH;
htable_cache = kmem_cache_create("htable_t",
sizeof (htable_t), 0, NULL, NULL,
htable_reap, NULL, hat_memload_arena, kmem_flags);
}
uint_t
htable_va2entry(uintptr_t va, htable_t *ht)
{
level_t l = ht->ht_level;
ASSERT(va >= ht->ht_vaddr);
ASSERT(va <= HTABLE_LAST_PAGE(ht));
return ((va >> LEVEL_SHIFT(l)) & (HTABLE_NUM_PTES(ht) - 1));
}
uintptr_t
htable_e2va(htable_t *ht, uint_t entry)
{
level_t l = ht->ht_level;
uintptr_t va;
ASSERT(entry < HTABLE_NUM_PTES(ht));
va = ht->ht_vaddr + ((uintptr_t)entry << LEVEL_SHIFT(l));
if (ht->ht_level == mmu.max_level && va >= mmu.hole_start)
va += ((mmu.hole_end - mmu.hole_start) + 1);
return (va);
}
void
x86pte_cpu_init(cpu_t *cpu)
{
struct hat_cpu_info *hci;
hci = kmem_zalloc(sizeof (*hci), KM_SLEEP);
mutex_init(&hci->hci_mutex, NULL, MUTEX_DEFAULT, NULL);
cpu->cpu_hat_info = hci;
}
void
x86pte_cpu_fini(cpu_t *cpu)
{
struct hat_cpu_info *hci = cpu->cpu_hat_info;
kmem_free(hci, sizeof (*hci));
cpu->cpu_hat_info = NULL;
}
static x86pte_t *
x86pte_access_pagetable(htable_t *ht, uint_t index)
{
if (ht->ht_flags & HTABLE_COPIED) {
ASSERT3U(index, <, ht->ht_hat->hat_num_copied);
return (PT_INDEX_PTR(ht->ht_hat->hat_copied_ptes, index));
}
return (x86pte_mapin(ht->ht_pfn, index, ht));
}
x86pte_t *
x86pte_mapin(pfn_t pfn, uint_t index, htable_t *ht)
{
x86pte_t *pteptr;
x86pte_t pte = 0;
x86pte_t newpte;
int x;
ASSERT(pfn != PFN_INVALID);
if (!khat_running) {
caddr_t va = kbm_remap_window(pfn_to_pa(pfn), 1);
return (PT_INDEX_PTR(va, index));
}
if (kpm_vbase)
return (PT_INDEX_PTR(hat_kpm_pfn2va(pfn), index));
kpreempt_disable();
ASSERT(CPU->cpu_hat_info != NULL);
ASSERT(!(getcr4() & CR4_PCIDE));
mutex_enter(&CPU->cpu_hat_info->hci_mutex);
x = PWIN_TABLE(CPU->cpu_id);
pteptr = (x86pte_t *)PWIN_PTE_VA(x);
#ifndef __xpv
if (mmu.pae_hat)
pte = *pteptr;
else
pte = *(x86pte32_t *)pteptr;
#endif
newpte = MAKEPTE(pfn, 0) | mmu.pt_global | mmu.pt_nx;
#ifdef __xpv
if (IN_XPV_PANIC())
#endif
newpte |= PT_WRITABLE;
if (!PTE_EQUIV(newpte, pte)) {
#ifdef __xpv
if (!IN_XPV_PANIC()) {
xen_map(newpte, PWIN_VA(x));
} else
#endif
{
XPV_ALLOW_PAGETABLE_UPDATES();
if (mmu.pae_hat)
*pteptr = newpte;
else
*(x86pte32_t *)pteptr = newpte;
XPV_DISALLOW_PAGETABLE_UPDATES();
mmu_flush_tlb_kpage((uintptr_t)PWIN_VA(x));
}
}
return (PT_INDEX_PTR(PWIN_VA(x), index));
}
static void
x86pte_release_pagetable(htable_t *ht)
{
if (ht->ht_flags & HTABLE_COPIED)
return;
x86pte_mapout();
}
void
x86pte_mapout(void)
{
if (kpm_vbase != NULL || !khat_running)
return;
#ifdef __xpv
if (!IN_XPV_PANIC()) {
uintptr_t va;
va = (uintptr_t)PWIN_VA(PWIN_TABLE(CPU->cpu_id));
(void) HYPERVISOR_update_va_mapping(va, 0,
UVMF_INVLPG | UVMF_LOCAL);
}
#endif
mutex_exit(&CPU->cpu_hat_info->hci_mutex);
kpreempt_enable();
}
x86pte_t
x86pte_get(htable_t *ht, uint_t entry)
{
x86pte_t pte;
x86pte_t *ptep;
ASSERT(entry < mmu.ptes_per_table);
ptep = x86pte_access_pagetable(ht, entry);
pte = GET_PTE(ptep);
x86pte_release_pagetable(ht);
return (pte);
}
x86pte_t
x86pte_set(htable_t *ht, uint_t entry, x86pte_t new, void *ptr)
{
x86pte_t old;
x86pte_t prev;
x86pte_t *ptep;
level_t l = ht->ht_level;
x86pte_t pfn_mask = (l != 0) ? PT_PADDR_LGPG : PT_PADDR;
x86pte_t n;
uintptr_t addr = htable_e2va(ht, entry);
hat_t *hat = ht->ht_hat;
ASSERT(new != 0);
ASSERT(!(ht->ht_flags & HTABLE_SHARED_PFN));
if (ptr == NULL)
ptep = x86pte_access_pagetable(ht, entry);
else
ptep = ptr;
do {
prev = GET_PTE(ptep);
n = new;
if (PTE_ISVALID(n) && (prev & pfn_mask) == (new & pfn_mask))
n |= prev & (PT_REF | PT_MOD);
if (prev == n) {
old = new;
#ifdef __xpv
if (!IN_XPV_PANIC())
xen_flush_va((caddr_t)addr);
else
#endif
mmu_flush_tlb_page(addr);
goto done;
}
if (l > 0 && (prev & PT_VALID) && !(prev & PT_PAGESIZE)) {
old = LPAGE_ERROR;
goto done;
}
XPV_ALLOW_PAGETABLE_UPDATES();
old = CAS_PTE(ptep, prev, n);
XPV_DISALLOW_PAGETABLE_UPDATES();
} while (old != prev);
if (old & PT_REF)
hat_tlb_inval(hat, addr);
done:
if (ptr == NULL)
x86pte_release_pagetable(ht);
return (old);
}
x86pte_t
x86pte_cas(htable_t *ht, uint_t entry, x86pte_t old, x86pte_t new)
{
x86pte_t pte;
x86pte_t *ptep;
#ifdef __xpv
mmu_update_t t[2];
int cnt = 1;
int count;
maddr_t ma;
if (!IN_XPV_PANIC()) {
ASSERT(!(ht->ht_flags & HTABLE_COPIED));
ma = pa_to_ma(PT_INDEX_PHYSADDR(pfn_to_pa(ht->ht_pfn), entry));
t[0].ptr = ma | MMU_NORMAL_PT_UPDATE;
t[0].val = new;
if (ht->ht_level == mmu.max_level && ht->ht_hat != kas.a_hat) {
ma = pa_to_ma(PT_INDEX_PHYSADDR(pfn_to_pa(
ht->ht_hat->hat_user_ptable), entry));
t[1].ptr = ma | MMU_NORMAL_PT_UPDATE;
t[1].val = new;
++cnt;
}
if (HYPERVISOR_mmu_update(t, cnt, &count, DOMID_SELF))
panic("HYPERVISOR_mmu_update() failed");
ASSERT(count == cnt);
return (old);
}
#endif
ptep = x86pte_access_pagetable(ht, entry);
XPV_ALLOW_PAGETABLE_UPDATES();
pte = CAS_PTE(ptep, old, new);
XPV_DISALLOW_PAGETABLE_UPDATES();
x86pte_release_pagetable(ht);
return (pte);
}
x86pte_t
x86pte_inval(
htable_t *ht,
uint_t entry,
x86pte_t expect,
x86pte_t *pte_ptr,
boolean_t tlb)
{
x86pte_t *ptep;
x86pte_t oldpte;
x86pte_t found;
ASSERT(!(ht->ht_flags & HTABLE_SHARED_PFN));
ASSERT(ht->ht_level <= mmu.max_page_level);
if (pte_ptr != NULL)
ptep = pte_ptr;
else
ptep = x86pte_access_pagetable(ht, entry);
#if defined(__xpv)
if ((ht->ht_hat->hat_flags & HAT_FREEING) && !IN_XPV_PANIC()) {
int count;
mmu_update_t t[1];
maddr_t ma;
oldpte = GET_PTE(ptep);
if (expect != 0 && (oldpte & PT_PADDR) != (expect & PT_PADDR))
goto done;
ma = pa_to_ma(PT_INDEX_PHYSADDR(pfn_to_pa(ht->ht_pfn), entry));
t[0].ptr = ma | MMU_NORMAL_PT_UPDATE;
t[0].val = 0;
if (HYPERVISOR_mmu_update(t, 1, &count, DOMID_SELF))
panic("HYPERVISOR_mmu_update() failed");
ASSERT(count == 1);
goto done;
}
#endif
do {
oldpte = GET_PTE(ptep);
if (expect != 0 && (oldpte & PT_PADDR) != (expect & PT_PADDR))
goto done;
XPV_ALLOW_PAGETABLE_UPDATES();
found = CAS_PTE(ptep, oldpte, 0);
XPV_DISALLOW_PAGETABLE_UPDATES();
} while (found != oldpte);
if (tlb && (oldpte & (PT_REF | PT_MOD)))
hat_tlb_inval(ht->ht_hat, htable_e2va(ht, entry));
done:
if (pte_ptr == NULL)
x86pte_release_pagetable(ht);
return (oldpte);
}
x86pte_t
x86pte_update(
htable_t *ht,
uint_t entry,
x86pte_t expect,
x86pte_t new)
{
x86pte_t *ptep;
x86pte_t found;
ASSERT(new != 0);
ASSERT(!(ht->ht_flags & HTABLE_SHARED_PFN));
ASSERT(ht->ht_level <= mmu.max_page_level);
ptep = x86pte_access_pagetable(ht, entry);
XPV_ALLOW_PAGETABLE_UPDATES();
found = CAS_PTE(ptep, expect, new);
XPV_DISALLOW_PAGETABLE_UPDATES();
if (found == expect) {
hat_tlb_inval(ht->ht_hat, htable_e2va(ht, entry));
if ((expect & (PT_WRITABLE | PT_MOD)) == PT_WRITABLE &&
(new & (PT_WRITABLE | PT_MOD)) == 0 &&
(GET_PTE(ptep) & PT_MOD) != 0) {
do {
found = GET_PTE(ptep);
XPV_ALLOW_PAGETABLE_UPDATES();
found =
CAS_PTE(ptep, found, found | PT_WRITABLE);
XPV_DISALLOW_PAGETABLE_UPDATES();
} while ((found & PT_WRITABLE) == 0);
}
}
x86pte_release_pagetable(ht);
return (found);
}
#ifndef __xpv
void
x86pte_copy(htable_t *src, htable_t *dest, uint_t entry, uint_t count)
{
caddr_t src_va;
caddr_t dst_va;
size_t size;
x86pte_t *pteptr;
x86pte_t pte;
ASSERT(khat_running);
ASSERT(!(dest->ht_flags & HTABLE_COPIED));
ASSERT(!(src->ht_flags & HTABLE_COPIED));
ASSERT(!(src->ht_flags & HTABLE_SHARED_PFN));
ASSERT(!(dest->ht_flags & HTABLE_SHARED_PFN));
dst_va = (caddr_t)x86pte_access_pagetable(dest, entry);
if (kpm_vbase) {
src_va = (caddr_t)
PT_INDEX_PTR(hat_kpm_pfn2va(src->ht_pfn), entry);
} else {
uint_t x = PWIN_SRC(CPU->cpu_id);
ASSERT(!(getcr4() & CR4_PCIDE));
src_va = (caddr_t)PT_INDEX_PTR(PWIN_VA(x), entry);
pte = MAKEPTE(src->ht_pfn, 0) | mmu.pt_global | mmu.pt_nx;
pteptr = (x86pte_t *)PWIN_PTE_VA(x);
if (mmu.pae_hat)
*pteptr = pte;
else
*(x86pte32_t *)pteptr = pte;
mmu_flush_tlb_kpage((uintptr_t)PWIN_VA(x));
}
size = count << mmu.pte_size_shift;
bcopy(src_va, dst_va, size);
x86pte_release_pagetable(dest);
}
#else
void
x86pte_copy(htable_t *src, htable_t *dest, uint_t entry, uint_t count)
{
caddr_t src_va;
x86pte_t pte;
ASSERT(!IN_XPV_PANIC());
src_va = (caddr_t)x86pte_access_pagetable(src, entry);
while (count) {
if (mmu.pae_hat)
pte = *(x86pte_t *)src_va;
else
pte = *(x86pte32_t *)src_va;
if (pte != 0) {
set_pteval(pfn_to_pa(dest->ht_pfn), entry,
dest->ht_level, pte);
if (dest->ht_level == mmu.max_level &&
htable_e2va(dest, entry) < HYPERVISOR_VIRT_END)
set_pteval(
pfn_to_pa(dest->ht_hat->hat_user_ptable),
entry, dest->ht_level, pte);
}
--count;
++entry;
src_va += mmu.pte_size;
}
x86pte_release_pagetable(src);
}
#endif
static void
x86pte_zero(htable_t *dest, uint_t entry, uint_t count)
{
caddr_t dst_va;
size_t size;
#ifdef __xpv
int x = 0;
x86pte_t newpte;
#endif
ASSERT(!(dest->ht_flags & HTABLE_SHARED_PFN));
ASSERT(!(dest->ht_flags & HTABLE_COPIED));
#ifdef __xpv
if (kpm_vbase == NULL) {
kpreempt_disable();
ASSERT(CPU->cpu_hat_info != NULL);
mutex_enter(&CPU->cpu_hat_info->hci_mutex);
x = PWIN_TABLE(CPU->cpu_id);
newpte = MAKEPTE(dest->ht_pfn, 0) | PT_WRITABLE;
xen_map(newpte, PWIN_VA(x));
dst_va = (caddr_t)PT_INDEX_PTR(PWIN_VA(x), entry);
} else
#endif
dst_va = (caddr_t)x86pte_access_pagetable(dest, entry);
size = count << mmu.pte_size_shift;
ASSERT(size > BLOCKZEROALIGN);
block_zero_no_xmm(dst_va, size);
#ifdef __xpv
if (kpm_vbase == NULL) {
xen_map(0, PWIN_VA(x));
mutex_exit(&CPU->cpu_hat_info->hci_mutex);
kpreempt_enable();
} else
#endif
x86pte_release_pagetable(dest);
}
void
hat_dump(void)
{
hat_t *hat;
uint_t h;
htable_t *ht;
for (hat = kas.a_hat; hat != NULL; hat = hat->hat_next) {
for (h = 0; h < hat->hat_num_hash; ++h) {
for (ht = hat->hat_ht_hash[h]; ht; ht = ht->ht_next) {
if ((ht->ht_flags & HTABLE_COPIED) == 0)
dump_page(ht->ht_pfn);
}
}
}
}