summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/xen/xengrant.c137
1 files changed, 120 insertions, 17 deletions
diff --git a/src/interface/xen/xengrant.c b/src/interface/xen/xengrant.c
index 55731bfa6..be12b23dc 100644
--- a/src/interface/xen/xengrant.c
+++ b/src/interface/xen/xengrant.c
@@ -20,6 +20,7 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
+#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/io.h>
@@ -32,6 +33,106 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
+/** Grant table version to try setting
+ *
+ * Using version 1 grant tables limits guests to using 16TB of
+ * grantable RAM, and prevents the use of subpage grants. Some
+ * versions of the Xen hypervisor refuse to allow the grant table
+ * version to be set after the first grant references have been
+ * created, so the loaded operating system may be stuck with whatever
+ * choice we make here. We therefore currently use version 2 grant
+ * tables, since they give the most flexibility to the loaded OS.
+ *
+ * Current versions (7.2.0) of the Windows PV drivers have no support
+ * for version 2 grant tables, and will merrily create version 1
+ * entries in what the hypervisor believes to be a version 2 table.
+ * This causes some confusion.
+ *
+ * Avoid this problem by attempting to use version 1 tables, since
+ * otherwise we may render Windows unable to boot.
+ *
+ * Play nicely with other potential bootloaders by accepting either
+ * version 1 or version 2 grant tables (if we are unable to set our
+ * requested version).
+ */
+#define XENGRANT_TRY_VERSION 1
+
+/**
+ * Initialise grant table
+ *
+ * @v xen Xen hypervisor
+ * @ret rc Return status code
+ */
+int xengrant_init ( struct xen_hypervisor *xen ) {
+ struct gnttab_query_size size;
+ struct gnttab_set_version set_version;
+ struct gnttab_get_version get_version;
+ struct grant_entry_v1 *v1;
+ union grant_entry_v2 *v2;
+ unsigned int version;
+ int xenrc;
+ int rc;
+
+ /* Get grant table size */
+ size.dom = DOMID_SELF;
+ if ( ( xenrc = xengrant_query_size ( xen, &size ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( xen, "XENGRANT could not get table size: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ xen->grant.len = ( size.nr_frames * PAGE_SIZE );
+
+ /* Set grant table version, if applicable */
+ set_version.version = XENGRANT_TRY_VERSION;
+ if ( ( xenrc = xengrant_set_version ( xen, &set_version ) ) != 0 ) {
+ rc = -EXEN ( xenrc );
+ DBGC ( xen, "XENGRANT could not set version %d: %s\n",
+ XENGRANT_TRY_VERSION, strerror ( rc ) );
+ /* Continue; use whatever version is current */
+ }
+
+ /* Get grant table version */
+ get_version.dom = DOMID_SELF;
+ get_version.pad = 0;
+ if ( ( xenrc = xengrant_get_version ( xen, &get_version ) ) == 0 ) {
+ version = get_version.version;
+ switch ( version ) {
+
+ case 0:
+ /* Version not yet specified: will be version 1 */
+ version = 1;
+ break;
+
+ case 1 :
+ /* Version 1 table: nothing special to do */
+ break;
+
+ case 2:
+ /* Version 2 table: configure shift appropriately */
+ xen->grant.shift = ( fls ( sizeof ( *v2 ) /
+ sizeof ( *v1 ) ) - 1 );
+ break;
+
+ default:
+ /* Unsupported version */
+ DBGC ( xen, "XENGRANT detected unsupported version "
+ "%d\n", version );
+ return -ENOTSUP;
+
+ }
+ } else {
+ rc = -EXEN ( xenrc );
+ DBGC ( xen, "XENGRANT could not get version (assuming v1): "
+ "%s\n", strerror ( rc ) );
+ version = 1;
+ }
+
+ DBGC ( xen, "XENGRANT using v%d table with %d entries\n",
+ version, xengrant_entries ( xen ) );
+ return 0;
+}
+
/**
* Allocate grant references
*
@@ -42,22 +143,22 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
unsigned int count ) {
- union grant_entry_v2 *entry;
- unsigned int mask = ( xen->grant.count - 1 );
+ struct grant_entry_header *hdr;
+ unsigned int entries = xengrant_entries ( xen );
+ unsigned int mask = ( entries - 1 );
unsigned int check = 0;
unsigned int avail;
unsigned int ref;
/* Fail unless we have enough references available */
- avail = ( xen->grant.count - xen->grant.used -
- GNTTAB_NR_RESERVED_ENTRIES );
+ avail = ( entries - xen->grant.used - GNTTAB_NR_RESERVED_ENTRIES );
if ( avail < count ) {
DBGC ( xen, "XENGRANT cannot allocate %d references (only %d "
- "of %d available)\n", count, avail, xen->grant.count );
+ "of %d available)\n", count, avail, entries );
return -ENOBUFS;
}
DBGC ( xen, "XENGRANT allocating %d references (from %d of %d "
- "available)\n", count, avail, xen->grant.count );
+ "available)\n", count, avail, entries );
/* Update number of references used */
xen->grant.used += count;
@@ -66,24 +167,27 @@ int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) {
/* Sanity check */
- assert ( check++ < xen->grant.count );
+ assert ( check++ < entries );
/* Skip reserved references */
if ( ref < GNTTAB_NR_RESERVED_ENTRIES )
continue;
/* Skip in-use references */
- entry = &xen->grant.table[ref];
- if ( readw ( &entry->hdr.flags ) & GTF_type_mask )
+ hdr = xengrant_header ( xen, ref );
+ if ( readw ( &hdr->flags ) & GTF_type_mask )
continue;
- if ( readw ( &entry->hdr.domid ) == DOMID_SELF )
+ if ( readw ( &hdr->domid ) == DOMID_SELF )
continue;
+ /* Zero reference */
+ xengrant_zero ( xen, hdr );
+
/* Mark reference as in-use. We leave the flags as
* empty (to avoid creating a valid grant table entry)
* and set the domid to DOMID_SELF.
*/
- writew ( DOMID_SELF, &entry->hdr.domid );
+ writew ( DOMID_SELF, &hdr->domid );
DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref );
/* Record reference */
@@ -105,7 +209,7 @@ int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
*/
void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
unsigned int count ) {
- union grant_entry_v2 *entry;
+ struct grant_entry_header *hdr;
unsigned int ref;
unsigned int i;
@@ -114,12 +218,11 @@ void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
/* Sanity check */
ref = refs[i];
- assert ( ref < xen->grant.count );
+ assert ( ref < xengrant_entries ( xen ) );
- /* Mark reference as unused */
- entry = &xen->grant.table[ref];
- writew ( 0, &entry->hdr.flags );
- writew ( 0, &entry->hdr.domid );
+ /* Zero reference */
+ hdr = xengrant_header ( xen, ref );
+ xengrant_zero ( xen, hdr );
DBGC2 ( xen, "XENGRANT freed ref %d\n", ref );
}
}