/* * Copyright (C) 2014 Michael Brown . * * 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. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include #include /** @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 ); } }