mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Introduce a grant_entry_v2 structure.
Signed-off-by: Steven Smith <steven.smith@citrix.com>
This commit is contained in:
1 parent
1766cdc32b
commit
a98dc13703
14 files changed
+906
-171
No files matched your search
+31
-1
@@ -558,7 +558,21 @@ int xc_gnttab_op(int xc_handle, int cmd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct grant_entry_v1 *xc_gnttab_map_table(int xc_handle, int domid, int *gnt_num)
|
||||
int xc_gnttab_get_version(int xc_handle, int domid)
|
||||
{
|
||||
struct gnttab_get_version query;
|
||||
int rc;
|
||||
|
||||
query.dom = domid;
|
||||
rc = xc_gnttab_op(xc_handle, GNTTABOP_get_version,
|
||||
&query, sizeof(query), 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
else
|
||||
return query.version;
|
||||
}
|
||||
|
||||
static void *_gnttab_map_table(int xc_handle, int domid, int *gnt_num)
|
||||
{
|
||||
int rc, i;
|
||||
struct gnttab_query_size query;
|
||||
@@ -638,6 +652,22 @@ err:
|
||||
return gnt;
|
||||
}
|
||||
|
||||
struct grant_entry_v1 *xc_gnttab_map_table_v1(int xc_handle, int domid,
|
||||
int *gnt_num)
|
||||
{
|
||||
if (xc_gnttab_get_version(xc_handle, domid) == 2)
|
||||
return NULL;
|
||||
return _gnttab_map_table(xc_handle, domid, gnt_num);
|
||||
}
|
||||
|
||||
struct grant_entry_v2 *xc_gnttab_map_table_v2(int xc_handle, int domid,
|
||||
int *gnt_num)
|
||||
{
|
||||
if (xc_gnttab_get_version(xc_handle, domid) != 2)
|
||||
return NULL;
|
||||
return _gnttab_map_table(xc_handle, domid, gnt_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
||||
@@ -132,7 +132,7 @@ int xc_query_page_offline_status(int xc, unsigned long start,
|
||||
/*
|
||||
* There should no update to the grant when domain paused
|
||||
*/
|
||||
static int xc_is_page_granted(int xc_handle, xen_pfn_t gpfn,
|
||||
static int xc_is_page_granted_v1(int xc_handle, xen_pfn_t gpfn,
|
||||
struct grant_entry_v1 *gnttab, int gnt_num)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -148,6 +148,22 @@ static int xc_is_page_granted(int xc_handle, xen_pfn_t gpfn,
|
||||
return (i != gnt_num);
|
||||
}
|
||||
|
||||
static int xc_is_page_granted_v2(int xc_handle, xen_pfn_t gpfn,
|
||||
struct grant_entry_v2 *gnttab, int gnt_num)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (!gnttab)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < gnt_num; i++)
|
||||
if ( ((gnttab[i].hdr.flags & GTF_type_mask) != GTF_invalid) &&
|
||||
(gnttab[i].frame == gpfn) )
|
||||
break;
|
||||
|
||||
return (i != gnt_num);
|
||||
}
|
||||
|
||||
static xen_pfn_t pfn_to_mfn(xen_pfn_t pfn, xen_pfn_t *p2m, int gwidth)
|
||||
{
|
||||
return ((xen_pfn_t) ((gwidth==8)?
|
||||
@@ -549,7 +565,8 @@ int xc_exchange_page(int xc_handle, int domid, xen_pfn_t mfn)
|
||||
struct domain_mem_info minfo;
|
||||
struct xc_mmu *mmu = NULL;
|
||||
struct pte_backup old_ptes = {NULL, 0, 0};
|
||||
struct grant_entry_v1 *gnttab = NULL;
|
||||
struct grant_entry_v1 *gnttab_v1 = NULL;
|
||||
struct grant_entry_v2 *gnttab_v2 = NULL;
|
||||
struct mmuext_op mops;
|
||||
int gnt_num, unpined = 0;
|
||||
void *old_p, *backup = NULL;
|
||||
@@ -588,14 +605,20 @@ int xc_exchange_page(int xc_handle, int domid, xen_pfn_t mfn)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
gnttab = xc_gnttab_map_table(xc_handle, domid, &gnt_num);
|
||||
if (!gnttab)
|
||||
gnttab_v2 = xc_gnttab_map_table_v2(xc_handle, domid, &gnt_num);
|
||||
if (!gnttab_v2)
|
||||
{
|
||||
gnttab_v1 = xc_gnttab_map_table_v1(xc_handle, domid, &gnt_num);
|
||||
if (!gnttab_v1)
|
||||
{
|
||||
ERROR("Failed to map grant table\n");
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (xc_is_page_granted(xc_handle, mfn, gnttab, gnt_num))
|
||||
if (gnttab_v1
|
||||
? xc_is_page_granted_v1(xc_handle, mfn, gnttab_v1, gnt_num)
|
||||
: xc_is_page_granted_v2(xc_handle, mfn, gnttab_v2, gnt_num))
|
||||
{
|
||||
ERROR("Page %lx is granted now\n", mfn);
|
||||
goto failed;
|
||||
@@ -755,8 +778,10 @@ failed:
|
||||
if (backup)
|
||||
free(backup);
|
||||
|
||||
if (gnttab)
|
||||
munmap(gnttab, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v1)));
|
||||
if (gnttab_v1)
|
||||
munmap(gnttab_v1, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v1)));
|
||||
if (gnttab_v2)
|
||||
munmap(gnttab_v2, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v2)));
|
||||
|
||||
close_mem_info(xc_handle, &minfo);
|
||||
|
||||
|
||||
@@ -943,7 +943,9 @@ int xc_gnttab_set_max_grants(int xcg_handle,
|
||||
int xc_gnttab_op(int xc_handle, int cmd,
|
||||
void * op, int op_size, int count);
|
||||
|
||||
struct grant_entry_v1 *xc_gnttab_map_table(int xc_handle, int domid, int *gnt_num);
|
||||
int xc_gnttab_get_version(int xc_handle, int domid);
|
||||
struct grant_entry_v1 *xc_gnttab_map_table_v1(int xc_handle, int domid, int *gnt_num);
|
||||
struct grant_entry_v2 *xc_gnttab_map_table_v2(int xc_handle, int domid, int *gnt_num);
|
||||
|
||||
int xc_physdev_map_pirq(int xc_handle,
|
||||
int domid,
|
||||
|
||||
+22
-9
@@ -2087,12 +2087,26 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
|
||||
return hvm_intblk_none;
|
||||
}
|
||||
|
||||
static int grant_table_op_is_allowed(unsigned int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case GNTTABOP_query_size:
|
||||
case GNTTABOP_setup_table:
|
||||
case GNTTABOP_set_version:
|
||||
case GNTTABOP_copy:
|
||||
case GNTTABOP_map_grant_ref:
|
||||
case GNTTABOP_unmap_grant_ref:
|
||||
return 1;
|
||||
default:
|
||||
/* all other commands need auditing */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long hvm_grant_table_op(
|
||||
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
|
||||
{
|
||||
if ( (cmd != GNTTABOP_query_size) && (cmd != GNTTABOP_setup_table) &&
|
||||
(cmd != GNTTABOP_map_grant_ref) && (cmd != GNTTABOP_unmap_grant_ref) &&
|
||||
(cmd != GNTTABOP_copy))
|
||||
if ( !grant_table_op_is_allowed(cmd) )
|
||||
return -ENOSYS; /* all other commands need auditing */
|
||||
return do_grant_table_op(cmd, uop, count);
|
||||
}
|
||||
@@ -2144,13 +2158,12 @@ static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
|
||||
|
||||
#else /* defined(__x86_64__) */
|
||||
|
||||
static long hvm_grant_table_op_compat32(
|
||||
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
|
||||
static long hvm_grant_table_op_compat32(unsigned int cmd,
|
||||
XEN_GUEST_HANDLE(void) uop,
|
||||
unsigned int count)
|
||||
{
|
||||
if ( (cmd != GNTTABOP_query_size) && (cmd != GNTTABOP_setup_table) &&
|
||||
(cmd != GNTTABOP_map_grant_ref) && (cmd != GNTTABOP_unmap_grant_ref) &&
|
||||
(cmd != GNTTABOP_copy))
|
||||
return -ENOSYS; /* all other commands need auditing */
|
||||
if ( !grant_table_op_is_allowed(cmd) )
|
||||
return -ENOSYS;
|
||||
return compat_grant_table_op(cmd, uop, count);
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -4000,12 +4000,25 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg)
|
||||
case XENMAPSPACE_grant_table:
|
||||
spin_lock(&d->grant_table->lock);
|
||||
|
||||
if ( d->grant_table->gt_version == 0 )
|
||||
d->grant_table->gt_version = 1;
|
||||
|
||||
if ( d->grant_table->gt_version == 2 &&
|
||||
(xatp.idx & XENMAPIDX_grant_table_status) )
|
||||
{
|
||||
xatp.idx &= ~XENMAPIDX_grant_table_status;
|
||||
if ( xatp.idx < nr_status_frames(d->grant_table) )
|
||||
mfn = virt_to_mfn(d->grant_table->status[xatp.idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (xatp.idx >= nr_grant_frames(d->grant_table)) &&
|
||||
(xatp.idx < max_nr_grant_frames) )
|
||||
gnttab_grow_table(d, xatp.idx + 1);
|
||||
|
||||
if ( xatp.idx < nr_grant_frames(d->grant_table) )
|
||||
mfn = virt_to_mfn(d->grant_table->shared[xatp.idx]);
|
||||
mfn = virt_to_mfn(d->grant_table->shared_raw[xatp.idx]);
|
||||
}
|
||||
|
||||
spin_unlock(&d->grant_table->lock);
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,14 @@
|
||||
CHECK_grant_entry_v1;
|
||||
#undef xen_grant_entry_v1
|
||||
|
||||
#define xen_grant_entry_header grant_entry_header
|
||||
CHECK_grant_entry_header;
|
||||
#undef xen_grant_entry_header
|
||||
|
||||
#define xen_grant_entry_v2 grant_entry_v2
|
||||
CHECK_grant_entry_v2;
|
||||
#undef xen_grant_entry_v2
|
||||
|
||||
#define xen_gnttab_map_grant_ref gnttab_map_grant_ref
|
||||
CHECK_gnttab_map_grant_ref;
|
||||
#undef xen_gnttab_map_grant_ref
|
||||
@@ -29,6 +37,16 @@ DEFINE_XEN_GUEST_HANDLE(gnttab_copy_compat_t);
|
||||
CHECK_gnttab_dump_table;
|
||||
#undef xen_gnttab_dump_table
|
||||
|
||||
#define xen_gnttab_set_version gnttab_set_version
|
||||
CHECK_gnttab_set_version;
|
||||
#undef xen_gnttab_set_version
|
||||
|
||||
DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_compat_t);
|
||||
|
||||
#define xen_gnttab_get_version gnttab_get_version
|
||||
CHECK_gnttab_get_version;
|
||||
#undef xen_gnttab_get_version
|
||||
|
||||
int compat_grant_table_op(unsigned int cmd,
|
||||
XEN_GUEST_HANDLE(void) cmp_uop,
|
||||
unsigned int count)
|
||||
@@ -76,6 +94,10 @@ int compat_grant_table_op(unsigned int cmd,
|
||||
CASE(dump_table);
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_gnttab_get_status_frames
|
||||
CASE(get_status_frames);
|
||||
#endif
|
||||
|
||||
#undef CASE
|
||||
default:
|
||||
return do_grant_table_op(cmd, cmp_uop, count);
|
||||
@@ -92,11 +114,13 @@ int compat_grant_table_op(unsigned int cmd,
|
||||
struct gnttab_setup_table *setup;
|
||||
struct gnttab_transfer *xfer;
|
||||
struct gnttab_copy *copy;
|
||||
struct gnttab_get_status_frames *get_status;
|
||||
} nat;
|
||||
union {
|
||||
struct compat_gnttab_setup_table setup;
|
||||
struct compat_gnttab_transfer xfer;
|
||||
struct compat_gnttab_copy copy;
|
||||
struct compat_gnttab_get_status_frames get_status;
|
||||
} cmp;
|
||||
|
||||
set_xen_guest_handle(nat.uop, COMPAT_ARG_XLAT_VIRT_BASE);
|
||||
@@ -233,6 +257,63 @@ int compat_grant_table_op(unsigned int cmd,
|
||||
}
|
||||
break;
|
||||
|
||||
case GNTTABOP_get_status_frames: {
|
||||
unsigned int max_frame_list_size_in_pages =
|
||||
(COMPAT_ARG_XLAT_SIZE - sizeof(*nat.get_status)) /
|
||||
sizeof(*nat.get_status->frame_list.p);
|
||||
if ( count != 1)
|
||||
{
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if ( unlikely(__copy_from_guest(&cmp.get_status, cmp_uop, 1) ||
|
||||
!compat_handle_okay(cmp.get_status.frame_list,
|
||||
cmp.get_status.nr_frames)) )
|
||||
{
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if ( max_frame_list_size_in_pages <
|
||||
grant_to_status_frames(max_nr_grant_frames) )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING,
|
||||
"grant_to_status_frames(max_nr_grant_frames) is too large (%u,%u)\n",
|
||||
grant_to_status_frames(max_nr_grant_frames),
|
||||
max_frame_list_size_in_pages);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
#define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
|
||||
set_xen_guest_handle((_d_)->frame_list, (uint64_t *)(nat.get_status + 1))
|
||||
XLAT_gnttab_get_status_frames(nat.get_status, &cmp.get_status);
|
||||
#undef XLAT_gnttab_get_status_frames_HNDL_frame_list
|
||||
|
||||
rc = gnttab_get_status_frames(
|
||||
guest_handle_cast(nat.uop, gnttab_get_status_frames_t),
|
||||
count);
|
||||
if ( rc >= 0 )
|
||||
{
|
||||
#define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
|
||||
do \
|
||||
{ \
|
||||
if ( (_s_)->status == GNTST_okay ) \
|
||||
{ \
|
||||
for ( i = 0; i < (_s_)->nr_frames; ++i ) \
|
||||
{ \
|
||||
uint64_t frame = (_s_)->frame_list.p[i]; \
|
||||
(void)__copy_to_compat_offset((_d_)->frame_list, i, &frame, 1); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
XLAT_gnttab_get_status_frames(&cmp.get_status, nat.get_status);
|
||||
#undef XLAT_gnttab_get_status_frames_HNDL_frame_list
|
||||
if ( unlikely(__copy_to_guest(cmp_uop, &cmp.get_status, 1)) )
|
||||
rc = -EFAULT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
domain_crash(current->domain);
|
||||
break;
|
||||
|
||||
+562
-145
@@ -105,9 +105,24 @@ static unsigned inline int max_nr_maptrack_frames(void)
|
||||
}
|
||||
|
||||
|
||||
#define SHGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_v1_t))
|
||||
#define shared_entry(t, e) \
|
||||
((t)->shared[(e)/SHGNT_PER_PAGE][(e)%SHGNT_PER_PAGE])
|
||||
#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
|
||||
#define shared_entry_v1(t, e) \
|
||||
((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
|
||||
#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
|
||||
#define shared_entry_v2(t, e) \
|
||||
((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
|
||||
#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
|
||||
#define status_entry(t, e) \
|
||||
((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
|
||||
static grant_entry_header_t *
|
||||
shared_entry_header(struct grant_table *t, grant_ref_t ref)
|
||||
{
|
||||
ASSERT(t->gt_version != 0);
|
||||
if (t->gt_version == 1)
|
||||
return (grant_entry_header_t*)&shared_entry_v1(t, ref);
|
||||
else
|
||||
return &shared_entry_v2(t, ref).hdr;
|
||||
}
|
||||
#define ACGNT_PER_PAGE (PAGE_SIZE / sizeof(struct active_grant_entry))
|
||||
#define active_entry(t, e) \
|
||||
((t)->active[(e)/ACGNT_PER_PAGE][(e)%ACGNT_PER_PAGE])
|
||||
@@ -183,6 +198,174 @@ get_maptrack_handle(
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* Number of grant table entries. Caller must hold d's grant table lock. */
|
||||
static unsigned int nr_grant_entries(struct grant_table *gt)
|
||||
{
|
||||
ASSERT(gt->gt_version != 0);
|
||||
if (gt->gt_version == 1)
|
||||
return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v1_t);
|
||||
else
|
||||
return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v2_t);
|
||||
}
|
||||
|
||||
static int _set_status_v1(domid_t domid,
|
||||
int readonly,
|
||||
grant_entry_header_t *shah,
|
||||
struct active_grant_entry *act)
|
||||
{
|
||||
int rc = GNTST_okay;
|
||||
union grant_combo scombo, prev_scombo, new_scombo;
|
||||
uint16_t mask = GTF_type_mask;
|
||||
|
||||
/*
|
||||
* We bound the number of times we retry CMPXCHG on memory locations that
|
||||
* we share with a guest OS. The reason is that the guest can modify that
|
||||
* location at a higher rate than we can read-modify-CMPXCHG, so the guest
|
||||
* could cause us to livelock. There are a few cases where it is valid for
|
||||
* the guest to race our updates (e.g., to change the GTF_readonly flag),
|
||||
* so we allow a few retries before failing.
|
||||
*/
|
||||
int retries = 0;
|
||||
|
||||
scombo.word = *(u32 *)shah;
|
||||
|
||||
/*
|
||||
* This loop attempts to set the access (reading/writing) flags
|
||||
* in the grant table entry. It tries a cmpxchg on the field
|
||||
* up to five times, and then fails under the assumption that
|
||||
* the guest is misbehaving.
|
||||
*/
|
||||
for ( ; ; )
|
||||
{
|
||||
/* If not already pinned, check the grant domid and type. */
|
||||
if ( !act->pin &&
|
||||
(((scombo.shorts.flags & mask) !=
|
||||
GTF_permit_access) ||
|
||||
(scombo.shorts.domid != domid)) )
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Bad flags (%x) or dom (%d). (expected dom %d)\n",
|
||||
scombo.shorts.flags, scombo.shorts.domid,
|
||||
domid);
|
||||
|
||||
new_scombo = scombo;
|
||||
new_scombo.shorts.flags |= GTF_reading;
|
||||
|
||||
if ( !readonly )
|
||||
{
|
||||
new_scombo.shorts.flags |= GTF_writing;
|
||||
if ( unlikely(scombo.shorts.flags & GTF_readonly) )
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Attempt to write-pin a r/o grant entry.\n");
|
||||
}
|
||||
|
||||
prev_scombo.word = cmpxchg((u32 *)shah,
|
||||
scombo.word, new_scombo.word);
|
||||
if ( likely(prev_scombo.word == scombo.word) )
|
||||
break;
|
||||
|
||||
if ( retries++ == 4 )
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Shared grant entry is unstable.\n");
|
||||
|
||||
scombo = prev_scombo;
|
||||
}
|
||||
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _set_status_v2(domid_t domid,
|
||||
int readonly,
|
||||
grant_entry_header_t *shah,
|
||||
struct active_grant_entry *act,
|
||||
grant_status_t *status)
|
||||
{
|
||||
int rc = GNTST_okay;
|
||||
union grant_combo scombo;
|
||||
uint16_t flags = shah->flags;
|
||||
domid_t id = shah->domid;
|
||||
uint16_t mask = GTF_type_mask;
|
||||
|
||||
/* we read flags and domid in a single memory access.
|
||||
this avoids the need for another memory barrier to
|
||||
ensure access to these fields are not reordered */
|
||||
scombo.word = *(u32 *)shah;
|
||||
barrier(); /* but we still need to stop the compiler from turning
|
||||
it back into two reads */
|
||||
flags = scombo.shorts.flags;
|
||||
id = scombo.shorts.domid;
|
||||
|
||||
/* If not already pinned, check the grant domid and type. */
|
||||
if ( !act->pin &&
|
||||
(((flags & mask) != GTF_permit_access) ||
|
||||
(id != domid)) )
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Bad flags (%x) or dom (%d). (expected dom %d)\n",
|
||||
flags, id, domid);
|
||||
|
||||
if ( readonly )
|
||||
{
|
||||
*status |= GTF_reading;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( unlikely(flags & GTF_readonly) )
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Attempt to write-pin a r/o grant entry.\n");
|
||||
*status |= GTF_reading | GTF_writing;
|
||||
}
|
||||
|
||||
/* Make sure guest sees status update before checking if flags are
|
||||
still valid */
|
||||
mb();
|
||||
|
||||
scombo.word = *(u32 *)shah;
|
||||
barrier();
|
||||
flags = scombo.shorts.flags;
|
||||
id = scombo.shorts.domid;
|
||||
|
||||
if ( !act->pin )
|
||||
{
|
||||
if ( ((flags & mask) != GTF_permit_access) ||
|
||||
(id != domid) ||
|
||||
(!readonly && (flags & GTF_readonly)) )
|
||||
{
|
||||
gnttab_clear_flag(_GTF_reading | _GTF_writing, status);
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Unstable flags (%x) or dom (%d). (expected dom %d) "
|
||||
"(r/w: %d)\n",
|
||||
flags, id, domid, !readonly);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( unlikely(flags & GTF_readonly) )
|
||||
{
|
||||
gnttab_clear_flag(_GTF_writing, status);
|
||||
PIN_FAIL(done, GNTST_general_error,
|
||||
"Unstable grant readonly flag\n");
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int _set_status(unsigned gt_version,
|
||||
domid_t domid,
|
||||
int readonly,
|
||||
grant_entry_header_t *shah,
|
||||
struct active_grant_entry *act,
|
||||
grant_status_t *status)
|
||||
{
|
||||
|
||||
if (gt_version == 1)
|
||||
return _set_status_v1(domid, readonly, shah, act);
|
||||
else
|
||||
return _set_status_v2(domid, readonly, shah, act, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 if TLB flush / invalidate required by caller.
|
||||
* va will indicate the address to be invalidated.
|
||||
@@ -205,18 +388,10 @@ __gnttab_map_grant_ref(
|
||||
unsigned int cache_flags;
|
||||
struct active_grant_entry *act;
|
||||
struct grant_mapping *mt;
|
||||
grant_entry_v1_t *sha;
|
||||
union grant_combo scombo, prev_scombo, new_scombo;
|
||||
|
||||
/*
|
||||
* We bound the number of times we retry CMPXCHG on memory locations that
|
||||
* we share with a guest OS. The reason is that the guest can modify that
|
||||
* location at a higher rate than we can read-modify-CMPXCHG, so the guest
|
||||
* could cause us to livelock. There are a few cases where it is valid for
|
||||
* the guest to race our updates (e.g., to change the GTF_readonly flag),
|
||||
* so we allow a few retries before failing.
|
||||
*/
|
||||
int retries = 0;
|
||||
grant_entry_v1_t *sha1;
|
||||
grant_entry_v2_t *sha2;
|
||||
grant_entry_header_t *shah;
|
||||
uint16_t *status;
|
||||
|
||||
led = current;
|
||||
ld = led->domain;
|
||||
@@ -262,12 +437,25 @@ __gnttab_map_grant_ref(
|
||||
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
if ( rd->grant_table->gt_version == 0 )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"remote grant table not yet set up");
|
||||
|
||||
/* Bounds check on the grant ref */
|
||||
if ( unlikely(op->ref >= nr_grant_entries(rd->grant_table)))
|
||||
PIN_FAIL(unlock_out, GNTST_bad_gntref, "Bad ref (%d).\n", op->ref);
|
||||
|
||||
act = &active_entry(rd->grant_table, op->ref);
|
||||
sha = &shared_entry(rd->grant_table, op->ref);
|
||||
shah = shared_entry_header(rd->grant_table, op->ref);
|
||||
if (rd->grant_table->gt_version == 1) {
|
||||
sha1 = &shared_entry_v1(rd->grant_table, op->ref);
|
||||
sha2 = NULL;
|
||||
status = &shah->flags;
|
||||
} else {
|
||||
sha2 = &shared_entry_v2(rd->grant_table, op->ref);
|
||||
sha1 = NULL;
|
||||
status = &status_entry(rd->grant_table, op->ref);
|
||||
}
|
||||
|
||||
/* If already pinned, check the active domid and avoid refcnt overflow. */
|
||||
if ( act->pin &&
|
||||
@@ -281,54 +469,19 @@ __gnttab_map_grant_ref(
|
||||
(!(op->flags & GNTMAP_readonly) &&
|
||||
!(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) )
|
||||
{
|
||||
scombo.word = *(u32 *)&sha->flags;
|
||||
|
||||
/*
|
||||
* This loop attempts to set the access (reading/writing) flags
|
||||
* in the grant table entry. It tries a cmpxchg on the field
|
||||
* up to five times, and then fails under the assumption that
|
||||
* the guest is misbehaving.
|
||||
*/
|
||||
for ( ; ; )
|
||||
{
|
||||
/* If not already pinned, check the grant domid and type. */
|
||||
if ( !act->pin &&
|
||||
(((scombo.shorts.flags & GTF_type_mask) !=
|
||||
GTF_permit_access) ||
|
||||
(scombo.shorts.domid != ld->domain_id)) )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Bad flags (%x) or dom (%d). (expected dom %d)\n",
|
||||
scombo.shorts.flags, scombo.shorts.domid,
|
||||
ld->domain_id);
|
||||
|
||||
new_scombo = scombo;
|
||||
new_scombo.shorts.flags |= GTF_reading;
|
||||
|
||||
if ( !(op->flags & GNTMAP_readonly) )
|
||||
{
|
||||
new_scombo.shorts.flags |= GTF_writing;
|
||||
if ( unlikely(scombo.shorts.flags & GTF_readonly) )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Attempt to write-pin a r/o grant entry.\n");
|
||||
}
|
||||
|
||||
prev_scombo.word = cmpxchg((u32 *)&sha->flags,
|
||||
scombo.word, new_scombo.word);
|
||||
if ( likely(prev_scombo.word == scombo.word) )
|
||||
break;
|
||||
|
||||
if ( retries++ == 4 )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Shared grant entry is unstable.\n");
|
||||
|
||||
scombo = prev_scombo;
|
||||
}
|
||||
if ( (rc = _set_status(rd->grant_table->gt_version,
|
||||
ld->domain_id, op->flags & GNTMAP_readonly,
|
||||
shah, act, status) ) != GNTST_okay )
|
||||
goto unlock_out;
|
||||
|
||||
if ( !act->pin )
|
||||
{
|
||||
act->domid = scombo.shorts.domid;
|
||||
act->gfn = sha->frame;
|
||||
act->frame = gmfn_to_mfn(rd, sha->frame);
|
||||
act->domid = ld->domain_id;
|
||||
if ( sha1 )
|
||||
act->gfn = sha1->frame;
|
||||
else
|
||||
act->gfn = sha2->full_page.frame;
|
||||
act->frame = gmfn_to_mfn(rd, act->gfn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +496,7 @@ __gnttab_map_grant_ref(
|
||||
frame = act->frame;
|
||||
act_pin = act->pin;
|
||||
|
||||
cache_flags = (sha->flags & (GTF_PAT | GTF_PWT | GTF_PCD) );
|
||||
cache_flags = (shah->flags & (GTF_PAT | GTF_PWT | GTF_PCD) );
|
||||
|
||||
spin_unlock(&rd->grant_table->lock);
|
||||
|
||||
@@ -457,7 +610,7 @@ __gnttab_map_grant_ref(
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
act = &active_entry(rd->grant_table, op->ref);
|
||||
sha = &shared_entry(rd->grant_table, op->ref);
|
||||
shah = shared_entry_header(rd->grant_table, op->ref);
|
||||
|
||||
if ( op->flags & GNTMAP_device_map )
|
||||
act->pin -= (op->flags & GNTMAP_readonly) ?
|
||||
@@ -468,10 +621,10 @@ __gnttab_map_grant_ref(
|
||||
|
||||
if ( !(op->flags & GNTMAP_readonly) &&
|
||||
!(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) )
|
||||
gnttab_clear_flag(_GTF_writing, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_writing, status);
|
||||
|
||||
if ( !act->pin )
|
||||
gnttab_clear_flag(_GTF_reading, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_reading, status);
|
||||
|
||||
unlock_out:
|
||||
spin_unlock(&rd->grant_table->lock);
|
||||
@@ -508,7 +661,6 @@ __gnttab_unmap_common(
|
||||
domid_t dom;
|
||||
struct domain *ld, *rd;
|
||||
struct active_grant_entry *act;
|
||||
grant_entry_v1_t *sha;
|
||||
s16 rc = 0;
|
||||
u32 old_pin;
|
||||
|
||||
@@ -556,7 +708,6 @@ __gnttab_unmap_common(
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
act = &active_entry(rd->grant_table, op->map->ref);
|
||||
sha = &shared_entry(rd->grant_table, op->map->ref);
|
||||
old_pin = act->pin;
|
||||
|
||||
if ( op->frame == 0 )
|
||||
@@ -622,8 +773,9 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
|
||||
{
|
||||
struct domain *ld, *rd;
|
||||
struct active_grant_entry *act;
|
||||
grant_entry_v1_t *sha;
|
||||
grant_entry_header_t *sha;
|
||||
struct page_info *pg;
|
||||
uint16_t *status;
|
||||
|
||||
rd = op->rd;
|
||||
|
||||
@@ -642,8 +794,16 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
|
||||
rcu_lock_domain(rd);
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
if ( rd->grant_table->gt_version == 0 )
|
||||
goto unmap_out;
|
||||
|
||||
act = &active_entry(rd->grant_table, op->map->ref);
|
||||
sha = &shared_entry(rd->grant_table, op->map->ref);
|
||||
sha = shared_entry_header(rd->grant_table, op->map->ref);
|
||||
|
||||
if ( rd->grant_table->gt_version == 1 )
|
||||
status = &sha->flags;
|
||||
else
|
||||
status = &status_entry(rd->grant_table, op->map->ref);
|
||||
|
||||
if ( unlikely(op->frame != act->frame) )
|
||||
{
|
||||
@@ -694,10 +854,10 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
|
||||
|
||||
if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) &&
|
||||
!(op->flags & GNTMAP_readonly) )
|
||||
gnttab_clear_flag(_GTF_writing, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_writing, status);
|
||||
|
||||
if ( act->pin == 0 )
|
||||
gnttab_clear_flag(_GTF_reading, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_reading, status);
|
||||
|
||||
unmap_out:
|
||||
spin_unlock(&rd->grant_table->lock);
|
||||
@@ -829,6 +989,50 @@ fault:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static int
|
||||
gnttab_populate_status_frames(struct domain *d, struct grant_table *gt)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned req_status_frames;
|
||||
|
||||
req_status_frames = grant_to_status_frames(gt->nr_grant_frames);
|
||||
for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
|
||||
{
|
||||
if ( (gt->status[i] = alloc_xenheap_page()) == NULL )
|
||||
goto status_alloc_failed;
|
||||
clear_page(gt->status[i]);
|
||||
}
|
||||
/* Share the new status frames with the recipient domain */
|
||||
for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
|
||||
gnttab_create_status_page(d, gt, i);
|
||||
|
||||
gt->nr_status_frames = req_status_frames;
|
||||
|
||||
return 0;
|
||||
|
||||
status_alloc_failed:
|
||||
for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
|
||||
{
|
||||
free_xenheap_page(gt->status[i]);
|
||||
gt->status[i] = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void
|
||||
gnttab_unpopulate_status_frames(struct domain *d, struct grant_table *gt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < nr_status_frames(gt); i++ )
|
||||
{
|
||||
page_set_owner(virt_to_page(gt->status[i]), dom_xen);
|
||||
free_xenheap_page(gt->status[i]);
|
||||
gt->status[i] = NULL;
|
||||
}
|
||||
gt->nr_status_frames = 0;
|
||||
}
|
||||
|
||||
int
|
||||
gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
|
||||
{
|
||||
@@ -855,9 +1059,9 @@ gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
|
||||
/* Shared */
|
||||
for ( i = nr_grant_frames(gt); i < req_nr_frames; i++ )
|
||||
{
|
||||
if ( (gt->shared[i] = alloc_xenheap_page()) == NULL )
|
||||
if ( (gt->shared_raw[i] = alloc_xenheap_page()) == NULL )
|
||||
goto shared_alloc_failed;
|
||||
clear_page(gt->shared[i]);
|
||||
clear_page(gt->shared_raw[i]);
|
||||
}
|
||||
|
||||
/* Share the new shared frames with the recipient domain */
|
||||
@@ -866,13 +1070,20 @@ gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
|
||||
|
||||
gt->nr_grant_frames = req_nr_frames;
|
||||
|
||||
/* Status pages - version 2 */
|
||||
if (gt->gt_version > 1)
|
||||
{
|
||||
if ( gnttab_populate_status_frames(d, gt) )
|
||||
goto shared_alloc_failed;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
shared_alloc_failed:
|
||||
for ( i = nr_grant_frames(gt); i < req_nr_frames; i++ )
|
||||
{
|
||||
free_xenheap_page(gt->shared[i]);
|
||||
gt->shared[i] = NULL;
|
||||
free_xenheap_page(gt->shared_raw[i]);
|
||||
gt->shared_raw[i] = NULL;
|
||||
}
|
||||
active_alloc_failed:
|
||||
for ( i = nr_active_grant_frames(gt);
|
||||
@@ -942,7 +1153,13 @@ gnttab_setup_table(
|
||||
|
||||
spin_lock(&d->grant_table->lock);
|
||||
|
||||
if ( (op.nr_frames > nr_grant_frames(d->grant_table)) &&
|
||||
if ( d->grant_table->gt_version == 0 )
|
||||
d->grant_table->gt_version = 1;
|
||||
|
||||
if ( (op.nr_frames > nr_grant_frames(d->grant_table) ||
|
||||
( (d->grant_table->gt_version > 1 ) &&
|
||||
(grant_to_status_frames(op.nr_frames) >
|
||||
nr_status_frames(d->grant_table)) ) ) &&
|
||||
!gnttab_grow_table(d, op.nr_frames) )
|
||||
{
|
||||
gdprintk(XENLOG_INFO,
|
||||
@@ -1046,7 +1263,7 @@ gnttab_prepare_for_transfer(
|
||||
struct domain *rd, struct domain *ld, grant_ref_t ref)
|
||||
{
|
||||
struct grant_table *rgt;
|
||||
struct grant_entry_v1 *sha;
|
||||
grant_entry_header_t *sha;
|
||||
union grant_combo scombo, prev_scombo, new_scombo;
|
||||
int retries = 0;
|
||||
|
||||
@@ -1058,6 +1275,14 @@ gnttab_prepare_for_transfer(
|
||||
|
||||
spin_lock(&rgt->lock);
|
||||
|
||||
if ( rgt->gt_version == 0 )
|
||||
{
|
||||
gdprintk(XENLOG_INFO,
|
||||
"Grant table not ready for transfer to domain(%d).\n",
|
||||
rd->domain_id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( unlikely(ref >= nr_grant_entries(rd->grant_table)) )
|
||||
{
|
||||
gdprintk(XENLOG_INFO,
|
||||
@@ -1066,7 +1291,7 @@ gnttab_prepare_for_transfer(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sha = &shared_entry(rgt, ref);
|
||||
sha = shared_entry_header(rgt, ref);
|
||||
|
||||
scombo.word = *(u32 *)&sha->flags;
|
||||
|
||||
@@ -1115,7 +1340,6 @@ gnttab_transfer(
|
||||
struct domain *e;
|
||||
struct page_info *page;
|
||||
int i;
|
||||
grant_entry_v1_t *sha;
|
||||
struct gnttab_transfer gop;
|
||||
unsigned long mfn;
|
||||
unsigned int max_bitsize;
|
||||
@@ -1248,11 +1472,21 @@ gnttab_transfer(
|
||||
/* Tell the guest about its new page frame. */
|
||||
spin_lock(&e->grant_table->lock);
|
||||
|
||||
sha = &shared_entry(e->grant_table, gop.ref);
|
||||
if ( e->grant_table->gt_version == 1 )
|
||||
{
|
||||
grant_entry_v1_t *sha = &shared_entry_v1(e->grant_table, gop.ref);
|
||||
guest_physmap_add_page(e, sha->frame, mfn, 0);
|
||||
sha->frame = mfn;
|
||||
}
|
||||
else
|
||||
{
|
||||
grant_entry_v2_t *sha = &shared_entry_v2(e->grant_table, gop.ref);
|
||||
guest_physmap_add_page(e, sha->full_page.frame, mfn, 0);
|
||||
sha->full_page.frame = mfn;
|
||||
}
|
||||
wmb();
|
||||
sha->flags |= GTF_transfer_completed;
|
||||
shared_entry_header(e->grant_table, gop.ref)->flags |=
|
||||
GTF_transfer_completed;
|
||||
|
||||
spin_unlock(&e->grant_table->lock);
|
||||
|
||||
@@ -1278,16 +1512,22 @@ static void
|
||||
__release_grant_for_copy(
|
||||
struct domain *rd, unsigned long gref, int readonly)
|
||||
{
|
||||
grant_entry_v1_t *sha;
|
||||
grant_entry_header_t *sha;
|
||||
struct active_grant_entry *act;
|
||||
unsigned long r_frame;
|
||||
uint16_t *status;
|
||||
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
act = &active_entry(rd->grant_table, gref);
|
||||
sha = &shared_entry(rd->grant_table, gref);
|
||||
sha = shared_entry_header(rd->grant_table, gref);
|
||||
r_frame = act->frame;
|
||||
|
||||
if (rd->grant_table->gt_version == 1)
|
||||
status = &sha->flags;
|
||||
else
|
||||
status = &status_entry(rd->grant_table, gref);
|
||||
|
||||
if ( readonly )
|
||||
{
|
||||
act->pin -= GNTPIN_hstr_inc;
|
||||
@@ -1298,11 +1538,11 @@ __release_grant_for_copy(
|
||||
|
||||
act->pin -= GNTPIN_hstw_inc;
|
||||
if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) )
|
||||
gnttab_clear_flag(_GTF_writing, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_writing, status);
|
||||
}
|
||||
|
||||
if ( !act->pin )
|
||||
gnttab_clear_flag(_GTF_reading, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_reading, status);
|
||||
|
||||
spin_unlock(&rd->grant_table->lock);
|
||||
}
|
||||
@@ -1316,20 +1556,37 @@ __acquire_grant_for_copy(
|
||||
struct domain *rd, unsigned long gref, int readonly,
|
||||
unsigned long *frame)
|
||||
{
|
||||
grant_entry_v1_t *sha;
|
||||
grant_entry_v1_t *sha1;
|
||||
grant_entry_v2_t *sha2;
|
||||
grant_entry_header_t *shah;
|
||||
struct active_grant_entry *act;
|
||||
grant_status_t *status;
|
||||
s16 rc = GNTST_okay;
|
||||
int retries = 0;
|
||||
union grant_combo scombo, prev_scombo, new_scombo;
|
||||
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
if ( rd->grant_table->gt_version == 0 )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"remote grant table not ready\n");
|
||||
|
||||
if ( unlikely(gref >= nr_grant_entries(rd->grant_table)) )
|
||||
PIN_FAIL(unlock_out, GNTST_bad_gntref,
|
||||
"Bad grant reference %ld\n", gref);
|
||||
|
||||
act = &active_entry(rd->grant_table, gref);
|
||||
sha = &shared_entry(rd->grant_table, gref);
|
||||
shah = shared_entry_header(rd->grant_table, gref);
|
||||
if ( rd->grant_table->gt_version == 1 )
|
||||
{
|
||||
sha1 = &shared_entry_v1(rd->grant_table, gref);
|
||||
sha2 = NULL;
|
||||
status = &shah->flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
sha1 = NULL;
|
||||
sha2 = &shared_entry_v2(rd->grant_table, gref);
|
||||
status = &status_entry(rd->grant_table, gref);
|
||||
}
|
||||
|
||||
/* If already pinned, check the active domid and avoid refcnt overflow. */
|
||||
if ( act->pin &&
|
||||
@@ -1342,48 +1599,18 @@ __acquire_grant_for_copy(
|
||||
if ( !act->pin ||
|
||||
(!readonly && !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
|
||||
{
|
||||
scombo.word = *(u32 *)&sha->flags;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
/* If not already pinned, check the grant domid and type. */
|
||||
if ( !act->pin &&
|
||||
(((scombo.shorts.flags & GTF_type_mask) !=
|
||||
GTF_permit_access) ||
|
||||
(scombo.shorts.domid != current->domain->domain_id)) )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Bad flags (%x) or dom (%d). (expected dom %d)\n",
|
||||
scombo.shorts.flags, scombo.shorts.domid,
|
||||
current->domain->domain_id);
|
||||
|
||||
new_scombo = scombo;
|
||||
new_scombo.shorts.flags |= GTF_reading;
|
||||
|
||||
if ( !readonly )
|
||||
{
|
||||
new_scombo.shorts.flags |= GTF_writing;
|
||||
if ( unlikely(scombo.shorts.flags & GTF_readonly) )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Attempt to write-pin a r/o grant entry.\n");
|
||||
}
|
||||
|
||||
prev_scombo.word = cmpxchg((u32 *)&sha->flags,
|
||||
scombo.word, new_scombo.word);
|
||||
if ( likely(prev_scombo.word == scombo.word) )
|
||||
break;
|
||||
|
||||
if ( retries++ == 4 )
|
||||
PIN_FAIL(unlock_out, GNTST_general_error,
|
||||
"Shared grant entry is unstable.\n");
|
||||
|
||||
scombo = prev_scombo;
|
||||
}
|
||||
|
||||
if ( (rc = _set_status(rd->grant_table->gt_version,
|
||||
current->domain->domain_id,
|
||||
readonly, shah, act, status) ) != GNTST_okay )
|
||||
goto unlock_out;
|
||||
if ( !act->pin )
|
||||
{
|
||||
act->domid = scombo.shorts.domid;
|
||||
act->gfn = sha->frame;
|
||||
act->frame = gmfn_to_mfn(rd, sha->frame);
|
||||
act->domid = current->domain->domain_id;
|
||||
if ( sha1 )
|
||||
act->gfn = sha1->frame;
|
||||
else
|
||||
act->gfn = sha2->full_page.frame;
|
||||
act->frame = gmfn_to_mfn(rd, act->gfn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1528,6 +1755,165 @@ gnttab_copy(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
gnttab_set_version(XEN_GUEST_HANDLE(gnttab_set_version_t uop))
|
||||
{
|
||||
gnttab_set_version_t op;
|
||||
struct domain *d = current->domain;
|
||||
struct grant_table *gt = d->grant_table;
|
||||
struct active_grant_entry *act;
|
||||
long res = 0;
|
||||
int i;
|
||||
|
||||
if (copy_from_guest(&op, uop, 1))
|
||||
return -EFAULT;
|
||||
|
||||
if (op.version != 1 && op.version != 2)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(>->lock);
|
||||
/* Make sure that the grant table isn't currently in use when we
|
||||
change the version number. */
|
||||
/* (You need to change the version number for e.g. kexec.) */
|
||||
if ( gt->gt_version != 0 )
|
||||
{
|
||||
for ( i = 0; i < nr_grant_entries(gt); i++ )
|
||||
{
|
||||
act = &active_entry(gt, i);
|
||||
if ( act->pin != 0 )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING,
|
||||
"tried to change grant table version from %d to %d, but some grant entries still in use\n",
|
||||
gt->gt_version,
|
||||
op.version);
|
||||
res = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: If we're going to version 2, we could maybe shrink the
|
||||
active grant table here. */
|
||||
|
||||
if ( op.version == 2 && gt->gt_version < 2 )
|
||||
{
|
||||
res = gnttab_populate_status_frames(d, gt);
|
||||
if ( res < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( op.version < 2 && gt->gt_version == 2 )
|
||||
gnttab_unpopulate_status_frames(d, gt);
|
||||
|
||||
if ( op.version != gt->gt_version )
|
||||
{
|
||||
/* Make sure there's no crud left over in the table from the
|
||||
old version. */
|
||||
for ( i = 0; i < nr_grant_frames(gt); i++ )
|
||||
memset(gt->shared_raw[i], 0, PAGE_SIZE);
|
||||
}
|
||||
|
||||
gt->gt_version = op.version;
|
||||
|
||||
out:
|
||||
spin_unlock(>->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static long
|
||||
gnttab_get_status_frames(XEN_GUEST_HANDLE(gnttab_get_status_frames_t) uop,
|
||||
int count)
|
||||
{
|
||||
gnttab_get_status_frames_t op;
|
||||
struct domain *d;
|
||||
struct grant_table *gt;
|
||||
uint64_t gmfn;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if ( count != 1 )
|
||||
return -EINVAL;
|
||||
|
||||
if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
|
||||
{
|
||||
gdprintk(XENLOG_INFO,
|
||||
"Fault while reading gnttab_get_status_frames_t.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = rcu_lock_target_domain_by_id(op.dom, &d);
|
||||
if ( rc < 0 )
|
||||
{
|
||||
if ( rc == -ESRCH )
|
||||
op.status = GNTST_bad_domain;
|
||||
else if ( rc == -EPERM )
|
||||
op.status = GNTST_permission_denied;
|
||||
else
|
||||
op.status = GNTST_general_error;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
gt = d->grant_table;
|
||||
|
||||
if ( unlikely(op.nr_frames > nr_status_frames(gt)) ) {
|
||||
gdprintk(XENLOG_INFO, "Guest requested addresses for %d grant status "
|
||||
"frames, but only %d are available.\n",
|
||||
op.nr_frames, nr_status_frames(gt));
|
||||
op.status = GNTST_general_error;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
op.status = GNTST_okay;
|
||||
|
||||
spin_lock(>->lock);
|
||||
|
||||
for ( i = 0; i < op.nr_frames; i++ )
|
||||
{
|
||||
gmfn = gnttab_status_gmfn(d, d->grant_table, i);
|
||||
if (copy_to_guest_offset(op.frame_list,
|
||||
i,
|
||||
&gmfn,
|
||||
1))
|
||||
op.status = GNTST_bad_virt_addr;
|
||||
}
|
||||
|
||||
spin_unlock(>->lock);
|
||||
out2:
|
||||
rcu_unlock_domain(d);
|
||||
out1:
|
||||
if ( unlikely(copy_to_guest(uop, &op, 1)) )
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
gnttab_get_version(XEN_GUEST_HANDLE(gnttab_get_version_t uop))
|
||||
{
|
||||
gnttab_get_version_t op;
|
||||
struct domain *d;
|
||||
|
||||
if ( copy_from_guest(&op, uop, 1) )
|
||||
return -EFAULT;
|
||||
d = rcu_lock_domain_by_id(op.dom);
|
||||
if ( d == NULL )
|
||||
return -ESRCH;
|
||||
if ( !IS_PRIV_FOR(current->domain, d) )
|
||||
{
|
||||
rcu_unlock_domain(d);
|
||||
return -EPERM;
|
||||
}
|
||||
spin_lock(&d->grant_table->lock);
|
||||
op.version = d->grant_table->gt_version;
|
||||
spin_unlock(&d->grant_table->lock);
|
||||
|
||||
if ( copy_to_guest(uop, &op, 1) )
|
||||
return -EFAULT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
do_grant_table_op(
|
||||
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
|
||||
@@ -1630,6 +2016,22 @@ do_grant_table_op(
|
||||
ASSERT(rc <= 0);
|
||||
break;
|
||||
}
|
||||
case GNTTABOP_set_version:
|
||||
{
|
||||
rc = gnttab_set_version(guest_handle_cast(uop, gnttab_set_version_t));
|
||||
break;
|
||||
}
|
||||
case GNTTABOP_get_status_frames:
|
||||
{
|
||||
rc = gnttab_get_status_frames(
|
||||
guest_handle_cast(uop, gnttab_get_status_frames_t), count);
|
||||
break;
|
||||
}
|
||||
case GNTTABOP_get_version:
|
||||
{
|
||||
rc = gnttab_get_version(guest_handle_cast(uop, gnttab_get_version_t));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -ENOSYS;
|
||||
break;
|
||||
@@ -1666,9 +2068,6 @@ grant_table_create(
|
||||
struct grant_table *t;
|
||||
int i;
|
||||
|
||||
/* If this sizeof assertion fails, fix the function: shared_index */
|
||||
ASSERT(sizeof(grant_entry_v1_t) == 8);
|
||||
|
||||
if ( (t = xmalloc(struct grant_table)) == NULL )
|
||||
goto no_mem_0;
|
||||
|
||||
@@ -1703,28 +2102,36 @@ grant_table_create(
|
||||
t->maptrack[0][i].ref = i+1;
|
||||
|
||||
/* Shared grant table. */
|
||||
if ( (t->shared = xmalloc_array(struct grant_entry_v1 *,
|
||||
max_nr_grant_frames)) == NULL )
|
||||
if ( (t->shared_raw = xmalloc_array(void *, max_nr_grant_frames)) == NULL )
|
||||
goto no_mem_3;
|
||||
memset(t->shared, 0, max_nr_grant_frames * sizeof(t->shared[0]));
|
||||
memset(t->shared_raw, 0, max_nr_grant_frames * sizeof(t->shared_raw[0]));
|
||||
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
|
||||
{
|
||||
if ( (t->shared[i] = alloc_xenheap_page()) == NULL )
|
||||
if ( (t->shared_raw[i] = alloc_xenheap_page()) == NULL )
|
||||
goto no_mem_4;
|
||||
clear_page(t->shared[i]);
|
||||
clear_page(t->shared_raw[i]);
|
||||
}
|
||||
|
||||
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
|
||||
gnttab_create_shared_page(d, t, i);
|
||||
|
||||
/* Status pages for grant table - for version 2 */
|
||||
t->status = xmalloc_array(grant_status_t *,
|
||||
grant_to_status_frames(max_nr_grant_frames));
|
||||
if ( t->status == NULL )
|
||||
goto no_mem_4;
|
||||
memset(t->status, 0,
|
||||
grant_to_status_frames(max_nr_grant_frames) * sizeof(t->status[0]));
|
||||
t->nr_status_frames = 0;
|
||||
|
||||
/* Okay, install the structure. */
|
||||
d->grant_table = t;
|
||||
return 0;
|
||||
|
||||
no_mem_4:
|
||||
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
|
||||
free_xenheap_page(t->shared[i]);
|
||||
xfree(t->shared);
|
||||
free_xenheap_page(t->shared_raw[i]);
|
||||
xfree(t->shared_raw);
|
||||
no_mem_3:
|
||||
free_xenheap_page(t->maptrack[0]);
|
||||
xfree(t->maptrack);
|
||||
@@ -1749,7 +2156,8 @@ gnttab_release_mappings(
|
||||
grant_handle_t handle;
|
||||
struct domain *rd;
|
||||
struct active_grant_entry *act;
|
||||
struct grant_entry_v1*sha;
|
||||
grant_entry_header_t *sha;
|
||||
uint16_t *status;
|
||||
struct page_info *pg;
|
||||
|
||||
BUG_ON(!d->is_dying);
|
||||
@@ -1777,7 +2185,12 @@ gnttab_release_mappings(
|
||||
spin_lock(&rd->grant_table->lock);
|
||||
|
||||
act = &active_entry(rd->grant_table, ref);
|
||||
sha = &shared_entry(rd->grant_table, ref);
|
||||
sha = shared_entry_header(rd->grant_table, ref);
|
||||
if (rd->grant_table->gt_version == 1)
|
||||
status = &sha->flags;
|
||||
else
|
||||
status = &status_entry(rd->grant_table, ref);
|
||||
|
||||
pg = mfn_to_page(act->frame);
|
||||
|
||||
if ( map->flags & GNTMAP_readonly )
|
||||
@@ -1823,11 +2236,11 @@ gnttab_release_mappings(
|
||||
}
|
||||
|
||||
if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 )
|
||||
gnttab_clear_flag(_GTF_writing, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_writing, status);
|
||||
}
|
||||
|
||||
if ( act->pin == 0 )
|
||||
gnttab_clear_flag(_GTF_reading, &sha->flags);
|
||||
gnttab_clear_flag(_GTF_reading, status);
|
||||
|
||||
spin_unlock(&rd->grant_table->lock);
|
||||
|
||||
@@ -1849,8 +2262,8 @@ grant_table_destroy(
|
||||
return;
|
||||
|
||||
for ( i = 0; i < nr_grant_frames(t); i++ )
|
||||
free_xenheap_page(t->shared[i]);
|
||||
xfree(t->shared);
|
||||
free_xenheap_page(t->shared_raw[i]);
|
||||
xfree(t->shared_raw);
|
||||
|
||||
for ( i = 0; i < nr_maptrack_frames(t); i++ )
|
||||
free_xenheap_page(t->maptrack[i]);
|
||||
@@ -1860,6 +2273,10 @@ grant_table_destroy(
|
||||
free_xenheap_page(t->active[i]);
|
||||
xfree(t->active);
|
||||
|
||||
for ( i = 0; i < nr_status_frames(t); i++ )
|
||||
free_xenheap_page(t->status[i]);
|
||||
xfree(t->status);
|
||||
|
||||
xfree(t);
|
||||
d->grant_table = NULL;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ compat/%.h: compat/%.i Makefile $(BASEDIR)/tools/compat-build-header.py
|
||||
mv -f $@.new $@
|
||||
|
||||
compat/%.i: compat/%.c Makefile
|
||||
$(CPP) $(filter-out -M% .%.d,$(CFLAGS)) $(cppflags-y) -o $@ $<
|
||||
$(CPP) -include public/xen-compat.h $(filter-out -M% .%.d,$(CFLAGS)) $(cppflags-y) -o $@ $<
|
||||
|
||||
compat/%.c: public/%.h xlat.lst Makefile $(BASEDIR)/tools/compat-build-source.py
|
||||
mkdir -p $(@D)
|
||||
|
||||
@@ -21,16 +21,31 @@ int replace_grant_host_mapping(
|
||||
#define gnttab_create_shared_page(d, t, i) \
|
||||
do { \
|
||||
share_xen_page_with_guest( \
|
||||
virt_to_page((char *)(t)->shared[i]), \
|
||||
virt_to_page((char *)(t)->shared_raw[i]), \
|
||||
(d), XENSHARE_writable); \
|
||||
} while ( 0 )
|
||||
|
||||
#define gnttab_create_status_page(d, t, i) \
|
||||
do { \
|
||||
share_xen_page_with_guest( \
|
||||
virt_to_page((char *)(t)->status[i]), \
|
||||
(d), XENSHARE_writable); \
|
||||
} while ( 0 )
|
||||
|
||||
|
||||
#define gnttab_shared_mfn(d, t, i) \
|
||||
((virt_to_maddr((t)->shared[i]) >> PAGE_SHIFT))
|
||||
((virt_to_maddr((t)->shared_raw[i]) >> PAGE_SHIFT))
|
||||
|
||||
#define gnttab_shared_gmfn(d, t, i) \
|
||||
(mfn_to_gmfn(d, gnttab_shared_mfn(d, t, i)))
|
||||
|
||||
|
||||
#define gnttab_status_mfn(d, t, i) \
|
||||
((virt_to_maddr((t)->status[i]) >> PAGE_SHIFT))
|
||||
|
||||
#define gnttab_status_gmfn(d, t, i) \
|
||||
(mfn_to_gmfn(d, gnttab_status_mfn(d, t, i)))
|
||||
|
||||
#define gnttab_mark_dirty(d, f) paging_mark_dirty((d), (f))
|
||||
|
||||
static inline void gnttab_clear_flag(unsigned long nr, uint16_t *addr)
|
||||
|
||||
@@ -90,6 +90,11 @@
|
||||
* [XEN]: This field is written by Xen and read by the sharing guest.
|
||||
* [GST]: This field is written by the guest and read by Xen.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Version 1 of the grant table entry structure is maintained purely
|
||||
* for backwards compatibility. New guests should use version 2.
|
||||
*/
|
||||
#if __XEN_INTERFACE_VERSION__ < 0x0003020a
|
||||
#define grant_entry_v1 grant_entry
|
||||
#define grant_entry_v1_t grant_entry_t
|
||||
@@ -154,6 +159,55 @@ typedef struct grant_entry_v1 grant_entry_v1_t;
|
||||
#define _GTF_transfer_completed (3)
|
||||
#define GTF_transfer_completed (1U<<_GTF_transfer_completed)
|
||||
|
||||
/*
|
||||
* Version 2 grant table entries. These fulfil the same role as
|
||||
* version 1 entries, but can represent more complicated operations.
|
||||
* Any given domain will have either a version 1 or a version 2 table,
|
||||
* and every entry in the table will be the same version.
|
||||
*
|
||||
* The interface by which domains use grant references does not depend
|
||||
* on the grant table version in use by the other domain.
|
||||
*/
|
||||
#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
|
||||
/*
|
||||
* Version 1 and version 2 grant entries share a common prefix. The
|
||||
* fields of the prefix are documented as part of struct
|
||||
* grant_entry_v1.
|
||||
*/
|
||||
struct grant_entry_header {
|
||||
uint16_t flags;
|
||||
domid_t domid;
|
||||
};
|
||||
typedef struct grant_entry_header grant_entry_header_t;
|
||||
|
||||
/*
|
||||
* Version 2 of the grant entry structure.
|
||||
*/
|
||||
union grant_entry_v2 {
|
||||
grant_entry_header_t hdr;
|
||||
|
||||
/*
|
||||
* This member is used for V1-style full page grants, where either:
|
||||
*
|
||||
* -- hdr.type is GTF_accept_transfer, or
|
||||
* -- hdr.type is GTF_permit_access and GTF_sub_page is not set.
|
||||
*
|
||||
* In that case, the frame field has the same semantics as the
|
||||
* field of the same name in the V1 entry structure.
|
||||
*/
|
||||
struct {
|
||||
grant_entry_header_t hdr;
|
||||
uint32_t pad0;
|
||||
uint64_t frame;
|
||||
} full_page;
|
||||
|
||||
uint32_t __spacer[4]; /* Pad to a power of two */
|
||||
};
|
||||
typedef union grant_entry_v2 grant_entry_v2_t;
|
||||
|
||||
typedef uint16_t grant_status_t;
|
||||
|
||||
#endif /* __XEN_INTERFACE_VERSION__ */
|
||||
|
||||
/***********************************
|
||||
* GRANT TABLE QUERIES AND USES
|
||||
@@ -363,6 +417,63 @@ struct gnttab_unmap_and_replace {
|
||||
typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t);
|
||||
|
||||
#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
|
||||
/*
|
||||
* GNTTABOP_set_version: Request a particular version of the grant
|
||||
* table shared table structure. This operation can only be performed
|
||||
* once in any given domain. It must be performed before any grants
|
||||
* are activated; otherwise, the domain will be stuck with version 1.
|
||||
* The only defined versions are 1 and 2.
|
||||
*/
|
||||
#define GNTTABOP_set_version 8
|
||||
struct gnttab_set_version {
|
||||
/* IN parameters */
|
||||
uint32_t version;
|
||||
};
|
||||
typedef struct gnttab_set_version gnttab_set_version_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t);
|
||||
|
||||
|
||||
/*
|
||||
* GNTTABOP_get_status_frames: Get the list of frames used to store grant
|
||||
* status for <dom>. In grant format version 2, the status is separated
|
||||
* from the other shared grant fields to allow more efficient synchronization
|
||||
* using barriers instead of atomic cmpexch operations.
|
||||
* <nr_frames> specify the size of vector <frame_list>.
|
||||
* The frame addresses are returned in the <frame_list>.
|
||||
* Only <nr_frames> addresses are returned, even if the table is larger.
|
||||
* NOTES:
|
||||
* 1. <dom> may be specified as DOMID_SELF.
|
||||
* 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
|
||||
*/
|
||||
#define GNTTABOP_get_status_frames 9
|
||||
struct gnttab_get_status_frames {
|
||||
/* IN parameters. */
|
||||
uint32_t nr_frames;
|
||||
domid_t dom;
|
||||
/* OUT parameters. */
|
||||
int16_t status; /* GNTST_* */
|
||||
XEN_GUEST_HANDLE(uint64_t) frame_list;
|
||||
};
|
||||
typedef struct gnttab_get_status_frames gnttab_get_status_frames_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t);
|
||||
|
||||
/*
|
||||
* GNTTABOP_get_version: Get the grant table version which is in
|
||||
* effect for domain <dom>.
|
||||
*/
|
||||
#define GNTTABOP_get_version 10
|
||||
struct gnttab_get_version {
|
||||
/* IN parameters */
|
||||
domid_t dom;
|
||||
uint16_t pad;
|
||||
/* OUT parameters */
|
||||
uint32_t version;
|
||||
};
|
||||
typedef struct gnttab_get_version gnttab_get_version_t;
|
||||
DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t);
|
||||
|
||||
#endif /* __XEN_INTERFACE_VERSION__ */
|
||||
|
||||
/*
|
||||
* Bitfield values for gnttab_map_grant_ref.flags.
|
||||
|
||||
@@ -211,6 +211,8 @@ struct xen_add_to_physmap {
|
||||
#define XENMAPSPACE_gmfn 2 /* GMFN */
|
||||
unsigned int space;
|
||||
|
||||
#define XENMAPIDX_grant_table_status 0x80000000
|
||||
|
||||
/* Index into source mapping space. */
|
||||
xen_ulong_t idx;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ DEFINE_XEN_GUEST_HANDLE(long);
|
||||
__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long);
|
||||
DEFINE_XEN_GUEST_HANDLE(void);
|
||||
|
||||
DEFINE_XEN_GUEST_HANDLE(uint64_t);
|
||||
DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -80,7 +80,15 @@ struct grant_table {
|
||||
/* Table size. Number of frames shared with guest */
|
||||
unsigned int nr_grant_frames;
|
||||
/* Shared grant table (see include/public/grant_table.h). */
|
||||
struct grant_entry_v1 **shared;
|
||||
union {
|
||||
void **shared_raw;
|
||||
struct grant_entry_v1 **shared_v1;
|
||||
union grant_entry_v2 **shared_v2;
|
||||
};
|
||||
/* Number of grant status frames shared with guest (for version 2) */
|
||||
unsigned int nr_status_frames;
|
||||
/* State grant table (see include/public/grant_table.h). */
|
||||
grant_status_t **status;
|
||||
/* Active grant table. */
|
||||
struct active_grant_entry **active;
|
||||
/* Mapping tracking table. */
|
||||
@@ -89,6 +97,9 @@ struct grant_table {
|
||||
unsigned int maptrack_limit;
|
||||
/* Lock protecting updates to active and shared grant tables. */
|
||||
spinlock_t lock;
|
||||
/* The defined versions are 1 and 2. Set to 0 if we don't know
|
||||
what version to use yet. */
|
||||
unsigned gt_version;
|
||||
};
|
||||
|
||||
/* Create/destroy per-domain grant table context. */
|
||||
@@ -114,10 +125,19 @@ static inline unsigned int nr_grant_frames(struct grant_table *gt)
|
||||
return gt->nr_grant_frames;
|
||||
}
|
||||
|
||||
/* Number of grant table entries. Caller must hold d's grant table lock. */
|
||||
static inline unsigned int nr_grant_entries(struct grant_table *gt)
|
||||
/* Number of status grant table frames. Caller must hold d's gr. table lock.*/
|
||||
static inline unsigned int nr_status_frames(struct grant_table *gt)
|
||||
{
|
||||
return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v1_t);
|
||||
return gt->nr_status_frames;
|
||||
}
|
||||
|
||||
#define GRANT_STATUS_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
|
||||
#define GRANT_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_v2_t))
|
||||
/* Number of grant table status entries. Caller must hold d's gr. table lock.*/
|
||||
static inline unsigned int grant_to_status_frames(int grant_frames)
|
||||
{
|
||||
return (grant_frames * GRANT_PER_PAGE + GRANT_STATUS_PER_PAGE - 1) /
|
||||
GRANT_STATUS_PER_PAGE;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
|
||||
@@ -44,7 +44,12 @@
|
||||
! gnttab_transfer grant_table.h
|
||||
? gnttab_unmap_grant_ref grant_table.h
|
||||
? gnttab_unmap_and_replace grant_table.h
|
||||
? gnttab_set_version grant_table.h
|
||||
? gnttab_get_version grant_table.h
|
||||
! gnttab_get_status_frames grant_table.h
|
||||
? grant_entry_v1 grant_table.h
|
||||
? grant_entry_header grant_table.h
|
||||
? grant_entry_v2 grant_table.h
|
||||
? kexec_exec kexec.h
|
||||
! kexec_image kexec.h
|
||||
! kexec_range kexec.h
|
||||
|
||||