iPXE
xengrant.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <strings.h>
00028 #include <errno.h>
00029 #include <assert.h>
00030 #include <ipxe/io.h>
00031 #include <ipxe/xen.h>
00032 #include <ipxe/xengrant.h>
00033 
00034 /** @file
00035  *
00036  * Xen grant tables
00037  *
00038  */
00039 
00040 /** Grant table version to try setting
00041  *
00042  * Using version 1 grant tables limits guests to using 16TB of
00043  * grantable RAM, and prevents the use of subpage grants.  Some
00044  * versions of the Xen hypervisor refuse to allow the grant table
00045  * version to be set after the first grant references have been
00046  * created, so the loaded operating system may be stuck with whatever
00047  * choice we make here.  We therefore currently use version 2 grant
00048  * tables, since they give the most flexibility to the loaded OS.
00049  *
00050  * Current versions (7.2.0) of the Windows PV drivers have no support
00051  * for version 2 grant tables, and will merrily create version 1
00052  * entries in what the hypervisor believes to be a version 2 table.
00053  * This causes some confusion.
00054  *
00055  * Avoid this problem by attempting to use version 1 tables, since
00056  * otherwise we may render Windows unable to boot.
00057  *
00058  * Play nicely with other potential bootloaders by accepting either
00059  * version 1 or version 2 grant tables (if we are unable to set our
00060  * requested version).
00061  */
00062 #define XENGRANT_TRY_VERSION 1
00063 
00064 /**
00065  * Initialise grant table
00066  *
00067  * @v xen               Xen hypervisor
00068  * @ret rc              Return status code
00069  */
00070 int xengrant_init ( struct xen_hypervisor *xen ) {
00071         struct gnttab_query_size size;
00072         struct gnttab_set_version set_version;
00073         struct gnttab_get_version get_version;
00074         struct grant_entry_v1 *v1;
00075         union grant_entry_v2 *v2;
00076         unsigned int version;
00077         int xenrc;
00078         int rc;
00079 
00080         /* Get grant table size */
00081         size.dom = DOMID_SELF;
00082         if ( ( xenrc = xengrant_query_size ( xen, &size ) ) != 0 ) {
00083                 rc = -EXEN ( xenrc );
00084                 DBGC ( xen, "XENGRANT could not get table size: %s\n",
00085                        strerror ( rc ) );
00086                 return rc;
00087         }
00088         xen->grant.len = ( size.nr_frames * PAGE_SIZE );
00089 
00090         /* Set grant table version, if applicable */
00091         set_version.version = XENGRANT_TRY_VERSION;
00092         if ( ( xenrc = xengrant_set_version ( xen, &set_version ) ) != 0 ) {
00093                 rc = -EXEN ( xenrc );
00094                 DBGC ( xen, "XENGRANT could not set version %d: %s\n",
00095                        XENGRANT_TRY_VERSION, strerror ( rc ) );
00096                 /* Continue; use whatever version is current */
00097         }
00098 
00099         /* Get grant table version */
00100         get_version.dom = DOMID_SELF;
00101         get_version.pad = 0;
00102         if ( ( xenrc = xengrant_get_version ( xen, &get_version ) ) == 0 ) {
00103                 version = get_version.version;
00104                 switch ( version ) {
00105 
00106                 case 0:
00107                         /* Version not yet specified: will be version 1 */
00108                         version = 1;
00109                         break;
00110 
00111                 case 1 :
00112                         /* Version 1 table: nothing special to do */
00113                         break;
00114 
00115                 case 2:
00116                         /* Version 2 table: configure shift appropriately */
00117                         xen->grant.shift = ( fls ( sizeof ( *v2 ) /
00118                                                    sizeof ( *v1 ) ) - 1 );
00119                         break;
00120 
00121                 default:
00122                         /* Unsupported version */
00123                         DBGC ( xen, "XENGRANT detected unsupported version "
00124                                "%d\n", version );
00125                         return -ENOTSUP;
00126 
00127                 }
00128         } else {
00129                 rc = -EXEN ( xenrc );
00130                 DBGC ( xen, "XENGRANT could not get version (assuming v1): "
00131                        "%s\n", strerror ( rc ) );
00132                 version = 1;
00133         }
00134 
00135         DBGC ( xen, "XENGRANT using v%d table with %d entries\n",
00136                version, xengrant_entries ( xen ) );
00137         return 0;
00138 }
00139 
00140 /**
00141  * Allocate grant references
00142  *
00143  * @v xen               Xen hypervisor
00144  * @v refs              Grant references to fill in
00145  * @v count             Number of references
00146  * @ret rc              Return status code
00147  */
00148 int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
00149                      unsigned int count ) {
00150         struct grant_entry_header *hdr;
00151         unsigned int entries = xengrant_entries ( xen );
00152         unsigned int mask = ( entries - 1 );
00153         unsigned int check = 0;
00154         unsigned int avail;
00155         unsigned int ref;
00156 
00157         /* Fail unless we have enough references available */
00158         avail = ( entries - xen->grant.used - GNTTAB_NR_RESERVED_ENTRIES );
00159         if ( avail < count ) {
00160                 DBGC ( xen, "XENGRANT cannot allocate %d references (only %d "
00161                        "of %d available)\n", count, avail, entries );
00162                 return -ENOBUFS;
00163         }
00164         DBGC ( xen, "XENGRANT allocating %d references (from %d of %d "
00165                "available)\n", count, avail, entries );
00166 
00167         /* Update number of references used */
00168         xen->grant.used += count;
00169 
00170         /* Find unused references */
00171         for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) {
00172 
00173                 /* Sanity check */
00174                 assert ( check++ < entries );
00175 
00176                 /* Skip reserved references */
00177                 if ( ref < GNTTAB_NR_RESERVED_ENTRIES )
00178                         continue;
00179 
00180                 /* Skip in-use references */
00181                 hdr = xengrant_header ( xen, ref );
00182                 if ( readw ( &hdr->flags ) & GTF_type_mask )
00183                         continue;
00184                 if ( readw ( &hdr->domid ) == DOMID_SELF )
00185                         continue;
00186 
00187                 /* Zero reference */
00188                 xengrant_zero ( xen, hdr );
00189 
00190                 /* Mark reference as in-use.  We leave the flags as
00191                  * empty (to avoid creating a valid grant table entry)
00192                  * and set the domid to DOMID_SELF.
00193                  */
00194                 writew ( DOMID_SELF, &hdr->domid );
00195                 DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref );
00196 
00197                 /* Record reference */
00198                 refs[--count] = ref;
00199         }
00200 
00201         /* Update cursor */
00202         xen->grant.ref = ref;
00203 
00204         return 0;
00205 }
00206 
00207 /**
00208  * Free grant references
00209  *
00210  * @v xen               Xen hypervisor
00211  * @v refs              Grant references
00212  * @v count             Number of references
00213  */
00214 void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
00215                      unsigned int count ) {
00216         struct grant_entry_header *hdr;
00217         unsigned int ref;
00218         unsigned int i;
00219 
00220         /* Free references */
00221         for ( i = 0 ; i < count ; i++ ) {
00222 
00223                 /* Sanity check */
00224                 ref = refs[i];
00225                 assert ( ref < xengrant_entries ( xen ) );
00226 
00227                 /* Zero reference */
00228                 hdr = xengrant_header ( xen, ref );
00229                 xengrant_zero ( xen, hdr );
00230                 DBGC2 ( xen, "XENGRANT freed ref %d\n", ref );
00231         }
00232 }