diff options
Diffstat (limited to 'roms/ipxe/src/interface/xen/xengrant.c')
| -rw-r--r-- | roms/ipxe/src/interface/xen/xengrant.c | 228 | 
1 files changed, 228 insertions, 0 deletions
diff --git a/roms/ipxe/src/interface/xen/xengrant.c b/roms/ipxe/src/interface/xen/xengrant.c new file mode 100644 index 00000000..be12b23d --- /dev/null +++ b/roms/ipxe/src/interface/xen/xengrant.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/io.h> +#include <ipxe/xen.h> +#include <ipxe/xengrant.h> + +/** @file + * + * Xen grant tables + * + */ + +/** 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 + * + * @v xen		Xen hypervisor + * @v refs		Grant references to fill in + * @v count		Number of references + * @ret rc		Return status code + */ +int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs, +		     unsigned int count ) { +	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 = ( 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, entries ); +		return -ENOBUFS; +	} +	DBGC ( xen, "XENGRANT allocating %d references (from %d of %d " +	       "available)\n", count, avail, entries ); + +	/* Update number of references used */ +	xen->grant.used += count; + +	/* Find unused references */ +	for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) { + +		/* Sanity check */ +		assert ( check++ < entries ); + +		/* Skip reserved references */ +		if ( ref < GNTTAB_NR_RESERVED_ENTRIES ) +			continue; + +		/* Skip in-use references */ +		hdr = xengrant_header ( xen, ref ); +		if ( readw ( &hdr->flags ) & GTF_type_mask ) +			continue; +		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, &hdr->domid ); +		DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref ); + +		/* Record reference */ +		refs[--count] = ref; +	} + +	/* Update cursor */ +	xen->grant.ref = ref; + +	return 0; +} + +/** + * Free grant references + * + * @v xen		Xen hypervisor + * @v refs		Grant references + * @v count		Number of references + */ +void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs, +		     unsigned int count ) { +	struct grant_entry_header *hdr; +	unsigned int ref; +	unsigned int i; + +	/* Free references */ +	for ( i = 0 ; i < count ; i++ ) { + +		/* Sanity check */ +		ref = refs[i]; +		assert ( ref < xengrant_entries ( xen ) ); + +		/* Zero reference */ +		hdr = xengrant_header ( xen, ref ); +		xengrant_zero ( xen, hdr ); +		DBGC2 ( xen, "XENGRANT freed ref %d\n", ref ); +	} +}  | 
