/******************************************************************************
* arch/x86/paging.c
*
* x86 specific paging support
* Copyright (c) 2007 Advanced Micro Devices (Wei Huang)
* Copyright (c) 2007 XenSource Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* SHUTDOWN_suspend */
#include "mm-locks.h"
/* Printouts */
#define PAGING_PRINTK(_f, _a...) \
debugtrace_printk("pg: %s(): " _f, __func__, ##_a)
#define PAGING_ERROR(_f, _a...) \
printk("pg error: %s(): " _f, __func__, ##_a)
#define PAGING_DEBUG(flag, _f, _a...) \
do { \
if (PAGING_DEBUG_ ## flag) \
debugtrace_printk("pgdebug: %s(): " _f, __func__, ##_a); \
} while (0)
/* Per-CPU variable for enforcing the lock ordering */
DEFINE_PER_CPU(int, mm_lock_level);
/* Override macros from asm/page.h to make them work with mfn_t */
#undef mfn_to_page
#define mfn_to_page(_m) __mfn_to_page(mfn_x(_m))
#undef page_to_mfn
#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg))
/************************************************/
/* LOG DIRTY SUPPORT */
/************************************************/
static mfn_t paging_new_log_dirty_page(struct domain *d)
{
struct page_info *page;
page = d->arch.paging.alloc_page(d);
if ( unlikely(page == NULL) )
{
d->arch.paging.log_dirty.failed_allocs++;
return INVALID_MFN;
}
d->arch.paging.log_dirty.allocs++;
return page_to_mfn(page);
}
/* Alloc and init a new leaf node */
static mfn_t paging_new_log_dirty_leaf(struct domain *d)
{
mfn_t mfn = paging_new_log_dirty_page(d);
if ( mfn_valid(mfn) )
clear_domain_page(mfn);
return mfn;
}
/* Alloc and init a new non-leaf node */
static mfn_t paging_new_log_dirty_node(struct domain *d)
{
mfn_t mfn = paging_new_log_dirty_page(d);
if ( mfn_valid(mfn) )
{
int i;
mfn_t *node = map_domain_page(mfn);
for ( i = 0; i < LOGDIRTY_NODE_ENTRIES; i++ )
node[i] = INVALID_MFN;
unmap_domain_page(node);
}
return mfn;
}
/* get the top of the log-dirty bitmap trie */
static mfn_t *paging_map_log_dirty_bitmap(struct domain *d)
{
if ( likely(mfn_valid(d->arch.paging.log_dirty.top)) )
return map_domain_page(d->arch.paging.log_dirty.top);
return NULL;
}
static void paging_free_log_dirty_page(struct domain *d, mfn_t mfn)
{
d->arch.paging.log_dirty.allocs--;
d->arch.paging.free_page(d, mfn_to_page(mfn));
}
static int paging_free_log_dirty_bitmap(struct domain *d, int rc)
{
mfn_t *l4, *l3, *l2;
int i4, i3, i2;
paging_lock(d);
if ( !mfn_valid(d->arch.paging.log_dirty.top) )
{
paging_unlock(d);
return 0;
}
if ( !d->arch.paging.preempt.dom )
{
memset(&d->arch.paging.preempt.log_dirty, 0,
sizeof(d->arch.paging.preempt.log_dirty));
ASSERT(rc <= 0);
d->arch.paging.preempt.log_dirty.done = -rc;
}
else if ( d->arch.paging.preempt.dom != current->domain ||
d->arch.paging.preempt.op != XEN_DOMCTL_SHADOW_OP_OFF )
{
paging_unlock(d);
return -EBUSY;
}
l4 = map_domain_page(d->arch.paging.log_dirty.top);
i4 = d->arch.paging.preempt.log_dirty.i4;
i3 = d->arch.paging.preempt.log_dirty.i3;
rc = 0;
for ( ; i4 < LOGDIRTY_NODE_ENTRIES; i4++, i3 = 0 )
{
if ( !mfn_valid(l4[i4]) )
continue;
l3 = map_domain_page(l4[i4]);
for ( ; i3 < LOGDIRTY_NODE_ENTRIES; i3++ )
{
if ( !mfn_valid(l3[i3]) )
continue;
l2 = map_domain_page(l3[i3]);
for ( i2 = 0; i2 < LOGDIRTY_NODE_ENTRIES; i2++ )
if ( mfn_valid(l2[i2]) )
paging_free_log_dirty_page(d, l2[i2]);
unmap_domain_page(l2);
paging_free_log_dirty_page(d, l3[i3]);
l3[i3] = INVALID_MFN;
if ( i3 < LOGDIRTY_NODE_ENTRIES - 1 && hypercall_preempt_check() )
{
d->arch.paging.preempt.log_dirty.i3 = i3 + 1;
d->arch.paging.preempt.log_dirty.i4 = i4;
rc = -ERESTART;
break;
}
}
unmap_domain_page(l3);
if ( rc )
break;
paging_free_log_dirty_page(d, l4[i4]);
l4[i4] = INVALID_MFN;
if ( i4 < LOGDIRTY_NODE_ENTRIES - 1 && hypercall_preempt_check() )
{
d->arch.paging.preempt.log_dirty.i3 = 0;
d->arch.paging.preempt.log_dirty.i4 = i4 + 1;
rc = -ERESTART;
break;
}
}
unmap_domain_page(l4);
if ( !rc )
{
paging_free_log_dirty_page(d, d->arch.paging.log_dirty.top);
d->arch.paging.log_dirty.top = INVALID_MFN;
ASSERT(d->arch.paging.log_dirty.allocs == 0);
d->arch.paging.log_dirty.failed_allocs = 0;
rc = -d->arch.paging.preempt.log_dirty.done;
d->arch.paging.preempt.dom = NULL;
}
else
{
d->arch.paging.preempt.dom = current->domain;
d->arch.paging.preempt.op = XEN_DOMCTL_SHADOW_OP_OFF;
}
paging_unlock(d);
return rc;
}
int paging_log_dirty_enable(struct domain *d, bool_t log_global)
{
int ret;
if ( need_iommu(d) && log_global )
{
/*
* Refuse to turn on global log-dirty mode
* if the domain is using the IOMMU.
*/
return -EINVAL;
}
if ( paging_mode_log_dirty(d) )
return -EINVAL;
domain_pause(d);
ret = d->arch.paging.log_dirty.ops->enable(d, log_global);
domain_unpause(d);
return ret;
}
static int paging_log_dirty_disable(struct domain *d, bool_t resuming)
{
int ret = 1;
if ( !resuming )
{
domain_pause(d);
/* Safe because the domain is paused. */
if ( paging_mode_log_dirty(d) )
{
ret = d->arch.paging.log_dirty.ops->disable(d);
ASSERT(ret <= 0);
}
}
ret = paging_free_log_dirty_bitmap(d, ret);
if ( ret == -ERESTART )
return ret;
domain_unpause(d);
return ret;
}
/* Mark a page as dirty, with taking guest pfn as parameter */
void paging_mark_pfn_dirty(struct domain *d, pfn_t pfn)
{
bool changed;
mfn_t mfn, *l4, *l3, *l2;
unsigned long *l1;
unsigned int i1, i2, i3, i4;
if ( !paging_mode_log_dirty(d) )
return;
/* Shared MFNs should NEVER be marked dirty */
BUG_ON(paging_mode_translate(d) && SHARED_M2P(pfn_x(pfn)));
/*
* Values with the MSB set denote MFNs that aren't really part of the
* domain's pseudo-physical memory map (e.g., the shared info frame).
* Nothing to do here...
*/
if ( unlikely(!VALID_M2P(pfn_x(pfn))) )
return;
i1 = L1_LOGDIRTY_IDX(pfn);
i2 = L2_LOGDIRTY_IDX(pfn);
i3 = L3_LOGDIRTY_IDX(pfn);
i4 = L4_LOGDIRTY_IDX(pfn);
/* Recursive: this is called from inside the shadow code */
paging_lock_recursive(d);
if ( unlikely(!mfn_valid(d->arch.paging.log_dirty.top)) )
{
d->arch.paging.log_dirty.top = paging_new_log_dirty_node(d);
if ( unlikely(!mfn_valid(d->arch.paging.log_dirty.top)) )
goto out;
}
l4 = paging_map_log_dirty_bitmap(d);
mfn = l4[i4];
if ( !mfn_valid(mfn) )
l4[i4] = mfn = paging_new_log_dirty_node(d);
unmap_domain_page(l4);
if ( !mfn_valid(mfn) )
goto out;
l3 = map_domain_page(mfn);
mfn = l3[i3];
if ( !mfn_valid(mfn) )
l3[i3] = mfn = paging_new_log_dirty_node(d);
unmap_domain_page(l3);
if ( !mfn_valid(mfn) )
goto out;
l2 = map_domain_page(mfn);
mfn = l2[i2];
if ( !mfn_valid(mfn) )
l2[i2] = mfn = paging_new_log_dirty_leaf(d);
unmap_domain_page(l2);
if ( !mfn_valid(mfn) )
goto out;
l1 = map_domain_page(mfn);
changed = !__test_and_set_bit(i1, l1);
unmap_domain_page(l1);
if ( changed )
{
PAGING_DEBUG(LOGDIRTY,
"d%d: marked mfn %" PRI_mfn " (pfn %" PRI_pfn ")\n",
d->domain_id, mfn_x(mfn), pfn_x(pfn));
d->arch.paging.log_dirty.dirty_count++;
}
out:
/* We've already recorded any failed allocations */
paging_unlock(d);
return;
}
/* Mark a page as dirty */
void paging_mark_dirty(struct domain *d, mfn_t gmfn)
{
pfn_t pfn;
if ( !paging_mode_log_dirty(d) || !mfn_valid(gmfn) ||
page_get_owner(mfn_to_page(gmfn)) != d )
return;
/* We /really/ mean PFN here, even for non-translated guests. */
pfn = _pfn(get_gpfn_from_mfn(mfn_x(gmfn)));
paging_mark_pfn_dirty(d, pfn);
}
/* Is this guest page dirty? */
int paging_mfn_is_dirty(struct domain *d, mfn_t gmfn)
{
pfn_t pfn;
mfn_t mfn, *l4, *l3, *l2;
unsigned long *l1;
int rv;
ASSERT(paging_locked_by_me(d));
ASSERT(paging_mode_log_dirty(d));
/* We /really/ mean PFN here, even for non-translated guests. */
pfn = _pfn(get_gpfn_from_mfn(mfn_x(gmfn)));
/* Shared pages are always read-only; invalid pages can't be dirty. */
if ( unlikely(SHARED_M2P(pfn_x(pfn)) || !VALID_M2P(pfn_x(pfn))) )
return 0;
mfn = d->arch.paging.log_dirty.top;
if ( !mfn_valid(mfn) )
return 0;
l4 = map_domain_page(mfn);
mfn = l4[L4_LOGDIRTY_IDX(pfn)];
unmap_domain_page(l4);
if ( !mfn_valid(mfn) )
return 0;
l3 = map_domain_page(mfn);
mfn = l3[L3_LOGDIRTY_IDX(pfn)];
unmap_domain_page(l3);
if ( !mfn_valid(mfn) )
return 0;
l2 = map_domain_page(mfn);
mfn = l2[L2_LOGDIRTY_IDX(pfn)];
unmap_domain_page(l2);
if ( !mfn_valid(mfn) )
return 0;
l1 = map_domain_page(mfn);
rv = test_bit(L1_LOGDIRTY_IDX(pfn), l1);
unmap_domain_page(l1);
return rv;
}
/* Read a domain's log-dirty bitmap and stats. If the operation is a CLEAN,
* clear the bitmap and stats as well. */
static int paging_log_dirty_op(struct domain *d,
struct xen_domctl_shadow_op *sc,
bool_t resuming)
{
int rv = 0, clean = 0, peek = 1;
unsigned long pages = 0;
mfn_t *l4 = NULL, *l3 = NULL, *l2 = NULL;
unsigned long *l1 = NULL;
int i4, i3, i2;
if ( !resuming )
{
/*
* Mark dirty all currently write-mapped pages on e.g. the
* final iteration of a save operation.
*/
if ( is_hvm_domain(d) &&
(sc->mode & XEN_DOMCTL_SHADOW_LOGDIRTY_FINAL) )
hvm_mapped_guest_frames_mark_dirty(d);
domain_pause(d);
/*
* Flush dirty GFNs potentially cached by hardware. Only need to flush
* when not resuming, as domain was paused in resuming case therefore
* it's not possible to have any new dirty pages.
*/
p2m_flush_hardware_cached_dirty(d);
}
paging_lock(d);
if ( !d->arch.paging.preempt.dom )
memset(&d->arch.paging.preempt.log_dirty, 0,
sizeof(d->arch.paging.preempt.log_dirty));
else if ( d->arch.paging.preempt.dom != current->domain ||
d->arch.paging.preempt.op != sc->op )
{
paging_unlock(d);
ASSERT(!resuming);
domain_unpause(d);
return -EBUSY;
}
clean = (sc->op == XEN_DOMCTL_SHADOW_OP_CLEAN);
PAGING_DEBUG(LOGDIRTY, "log-dirty %s: dom %u faults=%u dirty=%u\n",
(clean) ? "clean" : "peek",
d->domain_id,
d->arch.paging.log_dirty.fault_count,
d->arch.paging.log_dirty.dirty_count);
sc->stats.fault_count = d->arch.paging.log_dirty.fault_count;
sc->stats.dirty_count = d->arch.paging.log_dirty.dirty_count;
if ( guest_handle_is_null(sc->dirty_bitmap) )
/* caller may have wanted just to clean the state or access stats. */
peek = 0;
if ( unlikely(d->arch.paging.log_dirty.failed_allocs) ) {
printk(XENLOG_WARNING
"%u failed page allocs while logging dirty pages of d%d\n",
d->arch.paging.log_dirty.failed_allocs, d->domain_id);
rv = -ENOMEM;
goto out;
}
l4 = paging_map_log_dirty_bitmap(d);
i4 = d->arch.paging.preempt.log_dirty.i4;
i3 = d->arch.paging.preempt.log_dirty.i3;
pages = d->arch.paging.preempt.log_dirty.done;
for ( ; (pages < sc->pages) && (i4 < LOGDIRTY_NODE_ENTRIES); i4++, i3 = 0 )
{
l3 = (l4 && mfn_valid(l4[i4])) ? map_domain_page(l4[i4]) : NULL;
for ( ; (pages < sc->pages) && (i3 < LOGDIRTY_NODE_ENTRIES); i3++ )
{
l2 = ((l3 && mfn_valid(l3[i3])) ?
map_domain_page(l3[i3]) : NULL);
for ( i2 = 0;
(pages < sc->pages) && (i2 < LOGDIRTY_NODE_ENTRIES);
i2++ )
{
unsigned int bytes = PAGE_SIZE;
l1 = ((l2 && mfn_valid(l2[i2])) ?
map_domain_page(l2[i2]) : NULL);
if ( unlikely(((sc->pages - pages + 7) >> 3) < bytes) )
bytes = (unsigned int)((sc->pages - pages + 7) >> 3);
if ( likely(peek) )
{
if ( (l1 ? copy_to_guest_offset(sc->dirty_bitmap,
pages >> 3, (uint8_t *)l1,
bytes)
: clear_guest_offset(sc->dirty_bitmap,
pages >> 3, bytes)) != 0 )
{
rv = -EFAULT;
goto out;
}
}
pages += bytes << 3;
if ( l1 )
{
if ( clean )
clear_page(l1);
unmap_domain_page(l1);
}
}
if ( l2 )
unmap_domain_page(l2);
if ( i3 < LOGDIRTY_NODE_ENTRIES - 1 && hypercall_preempt_check() )
{
d->arch.paging.preempt.log_dirty.i4 = i4;
d->arch.paging.preempt.log_dirty.i3 = i3 + 1;
rv = -ERESTART;
break;
}
}
if ( l3 )
unmap_domain_page(l3);
if ( !rv && i4 < LOGDIRTY_NODE_ENTRIES - 1 &&
hypercall_preempt_check() )
{
d->arch.paging.preempt.log_dirty.i4 = i4 + 1;
d->arch.paging.preempt.log_dirty.i3 = 0;
rv = -ERESTART;
}
if ( rv )
break;
}
if ( l4 )
unmap_domain_page(l4);
if ( !rv )
{
d->arch.paging.preempt.dom = NULL;
if ( clean )
{
d->arch.paging.log_dirty.fault_count = 0;
d->arch.paging.log_dirty.dirty_count = 0;
}
}
else
{
d->arch.paging.preempt.dom = current->domain;
d->arch.paging.preempt.op = sc->op;
d->arch.paging.preempt.log_dirty.done = pages;
}
paging_unlock(d);
if ( rv )
{
/* Never leave the domain paused on real errors. */
ASSERT(rv == -ERESTART);
return rv;
}
if ( pages < sc->pages )
sc->pages = pages;
if ( clean )
{
/* We need to further call clean_dirty_bitmap() functions of specific
* paging modes (shadow or hap). Safe because the domain is paused. */
d->arch.paging.log_dirty.ops->clean(d);
}
domain_unpause(d);
return rv;
out:
d->arch.paging.preempt.dom = NULL;
paging_unlock(d);
domain_unpause(d);
if ( l1 )
unmap_domain_page(l1);
if ( l2 )
unmap_domain_page(l2);
if ( l3 )
unmap_domain_page(l3);
if ( l4 )
unmap_domain_page(l4);
return rv;
}
void paging_log_dirty_range(struct domain *d,
unsigned long begin_pfn,
unsigned long nr,
uint8_t *dirty_bitmap)
{
struct p2m_domain *p2m = p2m_get_hostp2m(d);
int i;
unsigned long pfn;
/*
* Set l1e entries of P2M table to be read-only.
*
* On first write, it page faults, its entry is changed to read-write,
* and on retry the write succeeds.
*
* We populate dirty_bitmap by looking for entries that have been
* switched to read-write.
*/
p2m_lock(p2m);
for ( i = 0, pfn = begin_pfn; pfn < begin_pfn + nr; i++, pfn++ )
if ( !p2m_change_type_one(d, pfn, p2m_ram_rw, p2m_ram_logdirty) )
dirty_bitmap[i >> 3] |= (1 << (i & 7));
p2m_unlock(p2m);
flush_tlb_mask(d->domain_dirty_cpumask);
}
/*
* Callers must supply log_dirty_ops for the log dirty code to call. This
* function usually is invoked when paging is enabled. Check shadow_enable()
* and hap_enable() for reference.
*
* These function pointers must not be followed with the log-dirty lock held.
*/
void paging_log_dirty_init(struct domain *d, const struct log_dirty_ops *ops)
{
d->arch.paging.log_dirty.ops = ops;
}
/************************************************/
/* CODE FOR PAGING SUPPORT */
/************************************************/
/* Domain paging struct initialization. */
int paging_domain_init(struct domain *d, unsigned int domcr_flags)
{
int rc;
if ( (rc = p2m_init(d)) != 0 )
return rc;
mm_lock_init(&d->arch.paging.lock);
/* This must be initialized separately from the rest of the
* log-dirty init code as that can be called more than once and we
* don't want to leak any active log-dirty bitmaps */
d->arch.paging.log_dirty.top = INVALID_MFN;
/*
* Shadow pagetables are the default, but we will use
* hardware assistance if it's available and enabled.
*/
if ( hap_enabled(d) )
hap_domain_init(d);
else
rc = shadow_domain_init(d, domcr_flags);
return rc;
}
/* vcpu paging struct initialization goes here */
void paging_vcpu_init(struct vcpu *v)
{
if ( hap_enabled(v->domain) )
hap_vcpu_init(v);
else
shadow_vcpu_init(v);
}
int paging_domctl(struct domain *d, struct xen_domctl_shadow_op *sc,
XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl,
bool_t resuming)
{
int rc;
if ( unlikely(d == current->domain) )
{
gdprintk(XENLOG_INFO, "Tried to do a paging op on itself.\n");
return -EINVAL;
}
if ( unlikely(d->is_dying) )
{
gdprintk(XENLOG_INFO, "Ignoring paging op on dying domain %u\n",
d->domain_id);
return 0;
}
if ( unlikely(d->vcpu == NULL) || unlikely(d->vcpu[0] == NULL) )
{
gdprintk(XENLOG_DEBUG, "Paging op on a domain (%u) with no vcpus\n",
d->domain_id);
return -EINVAL;
}
if ( resuming
? (d->arch.paging.preempt.dom != current->domain ||
d->arch.paging.preempt.op != sc->op)
: (d->arch.paging.preempt.dom &&
sc->op != XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION) )
{
printk(XENLOG_G_DEBUG
"%pv: Paging op %#x on Dom%u with unfinished prior op %#x by Dom%u\n",
current, sc->op, d->domain_id, d->arch.paging.preempt.op,
d->arch.paging.preempt.dom
? d->arch.paging.preempt.dom->domain_id : DOMID_INVALID);
return -EBUSY;
}
rc = xsm_shadow_control(XSM_HOOK, d, sc->op);
if ( rc )
return rc;
/* Code to handle log-dirty. Note that some log dirty operations
* piggy-back on shadow operations. For example, when
* XEN_DOMCTL_SHADOW_OP_OFF is called, it first checks whether log dirty
* mode is enabled. If does, we disables log dirty and continues with
* shadow code. For this reason, we need to further dispatch domctl
* to next-level paging code (shadow or hap).
*/
switch ( sc->op )
{
case XEN_DOMCTL_SHADOW_OP_ENABLE:
if ( !(sc->mode & XEN_DOMCTL_SHADOW_ENABLE_LOG_DIRTY) )
break;
/* Else fall through... */
case XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY:
return paging_log_dirty_enable(d, 1);
case XEN_DOMCTL_SHADOW_OP_OFF:
if ( (rc = paging_log_dirty_disable(d, resuming)) != 0 )
return rc;
break;
case XEN_DOMCTL_SHADOW_OP_CLEAN:
case XEN_DOMCTL_SHADOW_OP_PEEK:
if ( sc->mode & ~XEN_DOMCTL_SHADOW_LOGDIRTY_FINAL )
return -EINVAL;
return paging_log_dirty_op(d, sc, resuming);
}
/* Here, dispatch domctl to the appropriate paging code */
if ( hap_enabled(d) )
return hap_domctl(d, sc, u_domctl);
else
return shadow_domctl(d, sc, u_domctl);
}
long paging_domctl_continuation(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
{
struct xen_domctl op;
struct domain *d;
int ret;
if ( copy_from_guest(&op, u_domctl, 1) )
return -EFAULT;
if ( op.interface_version != XEN_DOMCTL_INTERFACE_VERSION ||
op.cmd != XEN_DOMCTL_shadow_op )
return -EOPNOTSUPP;
d = rcu_lock_domain_by_id(op.domain);
if ( d == NULL )
return -ESRCH;
ret = xsm_domctl(XSM_OTHER, d, op.cmd);
if ( !ret )
{
if ( domctl_lock_acquire() )
{
ret = paging_domctl(d, &op.u.shadow_op, u_domctl, 1);
domctl_lock_release();
}
else
ret = -ERESTART;
}
rcu_unlock_domain(d);
if ( ret == -ERESTART )
ret = hypercall_create_continuation(__HYPERVISOR_arch_1,
"h", u_domctl);
else if ( __copy_field_to_guest(u_domctl, &op, u.shadow_op) )
ret = -EFAULT;
return ret;
}
/* Call when destroying a domain */
int paging_teardown(struct domain *d)
{
int rc;
bool preempted = false;
if ( hap_enabled(d) )
hap_teardown(d, &preempted);
else
shadow_teardown(d, &preempted);
if ( preempted )
return -ERESTART;
/* clean up log dirty resources. */
rc = paging_free_log_dirty_bitmap(d, 0);
if ( rc == -ERESTART )
return rc;
/* Move populate-on-demand cache back to domain_list for destruction */
rc = p2m_pod_empty_cache(d);
return rc;
}
/* Call once all of the references to the domain have gone away */
void paging_final_teardown(struct domain *d)
{
if ( hap_enabled(d) )
hap_final_teardown(d);
else
shadow_final_teardown(d);
p2m_final_teardown(d);
}
/* Enable an arbitrary paging-assistance mode. Call once at domain
* creation. */
int paging_enable(struct domain *d, u32 mode)
{
/* Unrecognised paging mode? */
if ( mode & ~PG_MASK )
return -EINVAL;
/* All of external|translate|refcounts, or none. */
switch ( mode & (PG_external | PG_translate | PG_refcounts) )
{
case 0:
case PG_external | PG_translate | PG_refcounts:
break;
default:
return -EINVAL;
}
if ( hap_enabled(d) )
return hap_enable(d, mode);
else
return shadow_enable(d, mode);
}
/* Called from the guest to indicate that a process is being torn down
* and therefore its pagetables will soon be discarded */
void pagetable_dying(struct domain *d, paddr_t gpa)
{
#ifdef CONFIG_SHADOW_PAGING
struct vcpu *v;
ASSERT(paging_mode_shadow(d));
v = d->vcpu[0];
v->arch.paging.mode->shadow.pagetable_dying(v, gpa);
#else
BUG();
#endif
}
/* Print paging-assistance info to the console */
void paging_dump_domain_info(struct domain *d)
{
if ( paging_mode_enabled(d) )
{
printk(" paging assistance: ");
if ( paging_mode_shadow(d) )
printk("shadow ");
if ( paging_mode_hap(d) )
printk("hap ");
if ( paging_mode_refcounts(d) )
printk("refcounts ");
if ( paging_mode_log_dirty(d) )
printk("log_dirty ");
if ( paging_mode_translate(d) )
printk("translate ");
if ( paging_mode_external(d) )
printk("external ");
printk("\n");
}
}
void paging_dump_vcpu_info(struct vcpu *v)
{
if ( paging_mode_enabled(v->domain) )
{
printk(" paging assistance: ");
if ( paging_mode_shadow(v->domain) )
{
if ( paging_get_hostmode(v) )
printk("shadowed %u-on-%u\n",
paging_get_hostmode(v)->guest_levels,
paging_get_hostmode(v)->shadow.shadow_levels);
else
printk("not shadowed\n");
}
else if ( paging_mode_hap(v->domain) && paging_get_hostmode(v) )
printk("hap, %u levels\n",
paging_get_hostmode(v)->guest_levels);
else
printk("none\n");
}
}
const struct paging_mode *paging_get_mode(struct vcpu *v)
{
if (!nestedhvm_is_n2(v))
return paging_get_hostmode(v);
return paging_get_nestedmode(v);
}
void paging_update_nestedmode(struct vcpu *v)
{
ASSERT(nestedhvm_enabled(v->domain));
if (nestedhvm_paging_mode_hap(v))
/* nested-on-nested */
v->arch.paging.nestedmode = hap_paging_get_mode(v);
else
/* TODO: shadow-on-shadow */
v->arch.paging.nestedmode = NULL;
hvm_asid_flush_vcpu(v);
}
void paging_write_p2m_entry(struct p2m_domain *p2m, unsigned long gfn,
l1_pgentry_t *p, l1_pgentry_t new,
unsigned int level)
{
struct domain *d = p2m->domain;
struct vcpu *v = current;
if ( v->domain != d )
v = d->vcpu ? d->vcpu[0] : NULL;
if ( likely(v && paging_mode_enabled(d) && paging_get_hostmode(v) != NULL) )
paging_get_hostmode(v)->write_p2m_entry(d, gfn, p, new, level);
else
safe_write_pte(p, new);
}
int paging_set_allocation(struct domain *d, unsigned int pages, bool *preempted)
{
int rc;
ASSERT(paging_mode_enabled(d));
paging_lock(d);
if ( hap_enabled(d) )
rc = hap_set_allocation(d, pages, preempted);
else
rc = shadow_set_allocation(d, pages, preempted);
paging_unlock(d);
return rc;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/