@@ -1278,8 +1278,8 @@ does not provide `VM_ENTRY_LOAD_GUEST_PAT`.
Control various aspects of the grant table behaviour available to guests.
-* `max-ver` Select the maximum grant table version to offer to guests. Valid
-version are 1 and 2.
+* `max-ver` Select the maximum grant table version to offer to guests. Only
+available when CONFIG_GRANT_TABLE_V2 is set. Valid version are 1 and 2.
* `transitive` Permit or disallow the use of transitive grants. Note that the
use of grant table v2 without transitive grants is an ABI breakage from the
guests point of view.
@@ -23,6 +23,24 @@ config GRANT_TABLE
If unsure, say Y.
+config GRANT_TABLE_V2
+ bool "Grant table version 2 support" if EXPERT
+ depends on GRANT_TABLE && X86
+ help
+ Grant table interface version 2 is not the default. It has never
+ been implemented for ARM.
+
+ The version 2 interface enables support for systems with large amounts
+ of memory and some exotic grant primitives that are not in use by the
+ supported PV drivers.
+
+ Disabling this option reduces the amount of complex security-critical
+ hypervisor code in a subsystem of Xen responsible for approximately
+ 5% of Xen Security Advisories.
+
+ If you do not require large memory support, say N.
+ If you are paranoid, say N. If unsure, say Y.
+
config PDX_COMPRESSION
bool "PDX (Page inDeX) compression" if EXPERT && !X86 && !RISCV
default ARM || PPC
@@ -296,6 +296,9 @@ int compat_grant_table_op(
break;
case GNTTABOP_get_status_frames:
+#ifndef CONFIG_GRANT_TABLE_V2
+ rc = -ENOSYS;
+#else
if ( count != 1)
{
rc = -EINVAL;
@@ -328,6 +331,7 @@ int compat_grant_table_op(
else
i = 1;
}
+#endif
break;
default:
@@ -59,11 +59,13 @@ struct grant_table {
/* Lock protecting the maptrack limit */
spinlock_t maptrack_lock;
unsigned int max_version;
+#ifdef CONFIG_GRANT_TABLE_V2
/*
* Defaults to v1. May be changed with GNTTABOP_set_version. All other
* values are invalid.
*/
unsigned int gt_version;
+#endif
/* Resource limits of the domain. */
unsigned int max_grant_frames;
unsigned int max_maptrack_frames;
@@ -83,7 +85,9 @@ struct grant_table {
union {
void **shared_raw;
struct grant_entry_v1 **shared_v1;
+#ifdef CONFIG_GRANT_TABLE_V2
union grant_entry_v2 **shared_v2;
+#endif
};
/* State grant table (see include/public/grant_table.h). */
grant_status_t **status;
@@ -178,11 +182,20 @@ static int cf_check parse_gnttab_max_maptrack_frames(const char *arg)
opt_max_maptrack_frames_val);
}
+#ifdef CONFIG_GRANT_TABLE_V2
+
#ifndef GNTTAB_MAX_VERSION
#define GNTTAB_MAX_VERSION 2
#endif
#define get_gt_version(gt) ((gt)->gt_version)
+#else
+
+#define GNTTAB_MAX_VERSION 1
+#define get_gt_version(gt) 1
+
+#endif
+
unsigned int __read_mostly opt_gnttab_max_version = GNTTAB_MAX_VERSION;
static bool __read_mostly opt_transitive_grants = true;
#ifdef CONFIG_PV
@@ -193,7 +206,7 @@ static bool __ro_after_init opt_grant_transfer = true;
static int __init cf_check parse_gnttab(const char *s)
{
- const char *ss, *e;
+ const char *ss;
int val, rc = 0;
do {
@@ -204,12 +217,17 @@ static int __init cf_check parse_gnttab(const char *s)
if ( !strncmp(s, "max-ver:", 8) ||
!strncmp(s, "max_ver:", 8) ) /* Alias for original XSA-226 patch */
{
+#ifdef CONFIG_GRANT_TABLE_V2
+ const char *e;
long ver = simple_strtol(s + 8, &e, 10);
if ( e == ss && ver >= 1 && ver <= 2 )
opt_gnttab_max_version = ver;
else
rc = -EINVAL;
+#else
+ no_config_param("GRANT_TABLE_V2", "max_ver", s, ss);
+#endif
}
else if ( (val = parse_boolean("transitive", s, ss)) >= 0 )
opt_transitive_grants = val;
@@ -313,6 +331,8 @@ nr_maptrack_frames(struct grant_table *t)
((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
/* Operations providing a single interface agnostic to grant table version */
+#ifdef CONFIG_GRANT_TABLE_V2
+
#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])
@@ -330,6 +350,14 @@ nr_maptrack_frames(struct grant_table *t)
#define status_entry(t, e) \
((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
+#else /* CONFIG_GRANT_TABLE_V2 */
+
+#define shared_entry_full_frame(gt, ref) ( shared_entry_v1((gt), (ref)).frame )
+#define set_shared_entry(gt, ref, val) \
+ ( shared_entry_v1((gt), (ref)).frame = (val) )
+#define status_addr(gt, ref, flags_addr) (flags_addr)
+
+#endif /* CONFIG_GRANT_TABLE_V2 */
static grant_entry_header_t *
shared_entry_header(struct grant_table *t, grant_ref_t ref)
@@ -341,10 +369,12 @@ shared_entry_header(struct grant_table *t, grant_ref_t ref)
block_speculation();
return (grant_entry_header_t*)&shared_entry_v1(t, ref);
+#ifdef CONFIG_GRANT_TABLE_V2
case 2:
/* Returned values should be independent of speculative execution */
block_speculation();
return &shared_entry_v2(t, ref).hdr;
+#endif
}
ASSERT_UNREACHABLE();
@@ -734,7 +764,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt)
/* Make sure we return a value independently of speculative execution */
block_speculation();
return f2e(nr_grant_frames(gt), 1);
-
+#ifdef CONFIG_GRANT_TABLE_V2
case 2:
BUILD_BUG_ON(f2e(INITIAL_NR_GRANT_FRAMES, 2) <
GNTTAB_NR_RESERVED_ENTRIES);
@@ -742,6 +772,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt)
/* Make sure we return a value independently of speculative execution */
block_speculation();
return f2e(nr_grant_frames(gt), 2);
+#endif
#undef f2e
}
@@ -834,6 +865,7 @@ done:
return rc;
}
+#ifdef CONFIG_GRANT_TABLE_V2
static int _set_status_v2(const grant_entry_header_t *shah,
grant_status_t *status,
struct domain *rd,
@@ -918,7 +950,7 @@ static int _set_status_v2(const grant_entry_header_t *shah,
done:
return rc;
}
-
+#endif
static int _set_status(const grant_entry_header_t *shah,
grant_status_t *status,
@@ -929,11 +961,11 @@ static int _set_status(const grant_entry_header_t *shah,
int mapflag,
domid_t ldomid)
{
-
- if ( evaluate_nospec(rgt_version == 1) )
- return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid);
- else
+#ifdef CONFIG_GRANT_TABLE_V2
+ if ( evaluate_nospec(rgt_version != 1) )
return _set_status_v2(shah, status, rd, act, readonly, mapflag, ldomid);
+#endif
+ return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid);
}
/*
@@ -1796,6 +1828,12 @@ static int
gnttab_populate_status_frames(struct domain *d, struct grant_table *gt,
unsigned int req_nr_frames)
{
+#ifndef CONFIG_GRANT_TABLE_V2
+ ASSERT_UNREACHABLE();
+
+ return 0;
+}
+#else
unsigned int i;
unsigned int req_status_frames;
@@ -1898,6 +1936,7 @@ gnttab_unpopulate_status_frames(struct domain *d, struct grant_table *gt)
return 0;
}
+#endif
/*
* Grow the grant table. The caller must hold the grant table's
@@ -2010,7 +2049,9 @@ int grant_table_init(struct domain *d, int max_grant_frames,
percpu_rwlock_resource_init(>->lock, grant_rwlock);
spin_lock_init(>->maptrack_lock);
+#ifdef CONFIG_GRANT_TABLE_V2
gt->gt_version = 1;
+#endif
gt->max_grant_frames = max_grant_frames;
gt->max_maptrack_frames = max_maptrack_frames;
gt->max_version = max_grant_version;
@@ -2518,12 +2559,14 @@ release_grant_for_copy(
td = rd;
trans_gref = gref;
}
+#ifdef CONFIG_GRANT_TABLE_V2
else
{
td = (act->src_domid == rd->domain_id)
? rd : knownalive_domain_from_domid(act->src_domid);
trans_gref = act->trans_gref;
}
+#endif
if ( readonly )
{
@@ -2613,6 +2656,7 @@ acquire_grant_for_copy(
sha2 = NULL;
status = &shah->flags;
}
+#ifdef CONFIG_GRANT_TABLE_V2
else
{
sha2 = &shared_entry_v2(rgt, gref);
@@ -2748,7 +2792,9 @@ acquire_grant_for_copy(
act->is_sub_page = true;
}
}
- else if ( !old_pin ||
+ else
+#endif
+ if ( !old_pin ||
(!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
{
if ( (rc = _set_status(shah, status, rd, get_gt_version(rgt), act,
@@ -3165,6 +3211,17 @@ static long
gnttab_set_version(XEN_GUEST_HANDLE_PARAM(gnttab_set_version_t) uop)
{
gnttab_set_version_t op;
+#ifndef CONFIG_GRANT_TABLE_V2
+
+ if ( copy_from_guest(&op, uop, 1) )
+ return -EFAULT;
+
+ if ( op.version == 1 )
+ return 0;
+
+ /* Behave as before set_version was introduced. */
+ return -ENOSYS;
+#else
struct domain *currd = current->domain;
struct grant_table *gt = currd->grant_table;
grant_entry_v1_t reserved_entries[GNTTAB_NR_RESERVED_ENTRIES];
@@ -3309,8 +3366,10 @@ gnttab_set_version(XEN_GUEST_HANDLE_PARAM(gnttab_set_version_t) uop)
res = -EFAULT;
return res;
+#endif
}
+#ifdef CONFIG_GRANT_TABLE_V2
static long
gnttab_get_status_frames(XEN_GUEST_HANDLE_PARAM(gnttab_get_status_frames_t) uop,
unsigned int count)
@@ -3383,6 +3442,7 @@ gnttab_get_status_frames(XEN_GUEST_HANDLE_PARAM(gnttab_get_status_frames_t) uop,
return 0;
}
+#endif
static long
gnttab_get_version(XEN_GUEST_HANDLE_PARAM(gnttab_get_version_t) uop)
@@ -3471,6 +3531,7 @@ swap_grant_ref(grant_ref_t ref_a, grant_ref_t ref_b)
shared_entry_v1(gt, ref_a) = shared_entry_v1(gt, ref_b);
shared_entry_v1(gt, ref_b) = shared;
}
+#ifdef CONFIG_GRANT_TABLE_V2
else
{
grant_entry_v2_t shared;
@@ -3485,6 +3546,7 @@ swap_grant_ref(grant_ref_t ref_a, grant_ref_t ref_b)
shared_entry_v2(gt, ref_b) = shared;
status_entry(gt, ref_b) = status;
}
+#endif
out:
if ( act_b != NULL )
@@ -3747,10 +3809,12 @@ long do_grant_table_op(
rc = gnttab_set_version(guest_handle_cast(uop, gnttab_set_version_t));
break;
+#ifdef CONFIG_GRANT_TABLE_V2
case GNTTABOP_get_status_frames:
rc = gnttab_get_status_frames(
guest_handle_cast(uop, gnttab_get_status_frames_t), count);
break;
+#endif
case GNTTABOP_get_version:
rc = gnttab_get_version(guest_handle_cast(uop, gnttab_get_version_t));
@@ -4054,6 +4118,7 @@ int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
flags = sha1->flags;
*gfn = _gfn(sha1->frame);
}
+#ifdef CONFIG_GRANT_TABLE_V2
else
{
const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
@@ -4064,6 +4129,7 @@ int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
else
*gfn = _gfn(sha2->full_page.frame);
}
+#endif
if ( !rc && (flags & GTF_type_mask) != GTF_permit_access )
rc = -ENXIO;
@@ -4080,6 +4146,9 @@ int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
static int gnttab_get_status_frame_mfn(struct domain *d,
unsigned int idx, mfn_t *mfn)
{
+#ifndef CONFIG_GRANT_TABLE_V2
+ ASSERT_UNREACHABLE();
+#else
const struct grant_table *gt = d->grant_table;
ASSERT(gt->gt_version == 2);
@@ -4113,6 +4182,7 @@ static int gnttab_get_status_frame_mfn(struct domain *d,
/* Make sure idx is bounded wrt nr_status_frames */
*mfn = _mfn(virt_to_mfn(
gt->status[array_index_nospec(idx, nr_status_frames(gt))]));
+#endif
return 0;
}