diff options
Diffstat (limited to 'src/interface/xen')
| -rw-r--r-- | src/interface/xen/xengrant.c | 137 |
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 ); } } |
