Skip to content

Commit

Permalink
[xen] Use version 1 grant tables by default
Browse files Browse the repository at this point in the history
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).

Note that the use of version 1 tables on a 64-bit system introduces a
possible failure path in which a frame number cannot fit into the
32-bit field within the v1 structure.  This in turn introduces
additional failure paths into netfront_transmit() and
netfront_refill_rx().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Aug 13, 2014
1 parent 3f39f9f commit be79ca5
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 109 deletions.
75 changes: 23 additions & 52 deletions src/arch/x86/drivers/xen/hvm.c
Expand Up @@ -145,22 +145,24 @@ static void hvm_unmap_hypercall ( struct hvm_device *hvm ) {
*
* @v hvm HVM device
* @v space Source mapping space
* @v pages Number of pages
* @v len Length (must be a multiple of PAGE_SIZE)
* @ret mmio MMIO space address, or NULL on error
*/
static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
unsigned int pages ) {
size_t len ) {
struct xen_add_to_physmap add;
struct xen_remove_from_physmap remove;
unsigned int pages = ( len / PAGE_SIZE );
physaddr_t mmio_phys;
unsigned int i;
size_t len;
void *mmio;
int xenrc;
int rc;

/* Sanity check */
assert ( ( len % PAGE_SIZE ) == 0 );

/* Check for available space */
len = ( pages * PAGE_SIZE );
if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) {
DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space "
"(%zd of %zd remaining)\n", len,
Expand Down Expand Up @@ -218,12 +220,12 @@ static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
*
* @v hvm HVM device
* @v mmio MMIO space address
* @v pages Number of pages
* @v len Length (must be a multiple of PAGE_SIZE)
*/
static void hvm_iounmap ( struct hvm_device *hvm, void *mmio,
unsigned int pages ) {
static void hvm_iounmap ( struct hvm_device *hvm, void *mmio, size_t len ) {
struct xen_remove_from_physmap remove;
physaddr_t mmio_phys = virt_to_phys ( mmio );
unsigned int pages = ( len / PAGE_SIZE );
unsigned int i;
int xenrc;
int rc;
Expand Down Expand Up @@ -258,7 +260,8 @@ static int hvm_map_shared_info ( struct hvm_device *hvm ) {
int rc;

/* Map shared info page */
hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info, 1 );
hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info,
PAGE_SIZE );
if ( ! hvm->xen.shared ) {
rc = -ENOMEM;
goto err_alloc;
Expand All @@ -273,7 +276,7 @@ static int hvm_map_shared_info ( struct hvm_device *hvm ) {

return 0;

hvm_iounmap ( hvm, hvm->xen.shared, 1 );
hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
err_alloc:
return rc;
}
Expand All @@ -286,7 +289,7 @@ static int hvm_map_shared_info ( struct hvm_device *hvm ) {
static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {

/* Unmap shared info page */
hvm_iounmap ( hvm, hvm->xen.shared, 1 );
hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
}

/**
Expand All @@ -296,56 +299,26 @@ static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {
* @ret rc Return status code
*/
static int hvm_map_grant ( struct hvm_device *hvm ) {
struct gnttab_query_size size;
struct gnttab_set_version version;
physaddr_t grant_phys;
size_t len;
int xenrc;
int rc;

/* Get grant table size */
size.dom = DOMID_SELF;
if ( ( xenrc = xengrant_query_size ( &hvm->xen, &size ) ) != 0 ) {
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not get grant table size: %s\n",
/* Initialise grant table */
if ( ( rc = xengrant_init ( &hvm->xen ) ) != 0 ) {
DBGC ( hvm, "HVM could not initialise grant table: %s\n",
strerror ( rc ) );
goto err_query_size;
}
len = ( size.nr_frames * PAGE_SIZE );

/* Configure to use version 2 tables */
version.version = 2;
if ( ( xenrc = xengrant_set_version ( &hvm->xen, &version ) ) != 0 ) {
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not set version 2 grant table: %s\n",
strerror ( rc ) );
goto err_set_version;
}
if ( version.version != 2 ) {
DBGC ( hvm, "HVM could not set version 2 grant table\n" );
rc = -ENOTTY;
goto err_set_version;
return rc;
}

/* Map grant table */
hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table,
size.nr_frames );
if ( ! hvm->xen.grant.table ) {
rc = -ENODEV;
goto err_ioremap;
}
hvm->xen.grant.len );
if ( ! hvm->xen.grant.table )
return -ENODEV;

grant_phys = virt_to_phys ( hvm->xen.grant.table );
DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n",
grant_phys, ( grant_phys + len ) );
hvm->xen.grant.count = ( len / sizeof ( hvm->xen.grant.table[0] ) );

grant_phys, ( grant_phys + hvm->xen.grant.len ) );
return 0;

hvm_iounmap ( hvm, hvm->xen.grant.table, size.nr_frames );
err_ioremap:
err_set_version:
err_query_size:
return rc;
}

/**
Expand All @@ -354,11 +327,9 @@ static int hvm_map_grant ( struct hvm_device *hvm ) {
* @v hvm HVM device
*/
static void hvm_unmap_grant ( struct hvm_device *hvm ) {
size_t len;

/* Unmap grant table */
len = ( hvm->xen.grant.count * sizeof ( hvm->xen.grant.table[0] ) );
hvm_iounmap ( hvm, hvm->xen.grant.table, ( len / PAGE_SIZE ) );
hvm_iounmap ( hvm, hvm->xen.grant.table, hvm->xen.grant.len );
}

/**
Expand Down
90 changes: 62 additions & 28 deletions src/drivers/net/netfront.c
Expand Up @@ -292,8 +292,13 @@ static int netfront_create_ring ( struct netfront_nic *netfront,
}

/* Grant access to shared ring */
xengrant_permit_access ( xen, ring->ref, xendev->backend_id, 0,
ring->sring.raw );
if ( ( rc = xengrant_permit_access ( xen, ring->ref, xendev->backend_id,
0, ring->sring.raw ) ) != 0 ) {
DBGC ( netfront, "NETFRONT %s could not permit access to "
"%#08lx: %s\n", xendev->key,
virt_to_phys ( ring->sring.raw ), strerror ( rc ) );
goto err_permit_access;
}

/* Publish shared ring reference */
if ( ( rc = netfront_write_num ( netfront, ring->ref_key,
Expand All @@ -309,6 +314,7 @@ static int netfront_create_ring ( struct netfront_nic *netfront,
netfront_rm ( netfront, ring->ref_key );
err_write_num:
xengrant_invalidate ( xen, ring->ref );
err_permit_access:
free_dma ( ring->sring.raw, PAGE_SIZE );
err_alloc:
return rc;
Expand All @@ -320,39 +326,53 @@ static int netfront_create_ring ( struct netfront_nic *netfront,
* @v netfront Netfront device
* @v ring Descriptor ring
* @v iobuf I/O buffer
* @v id Buffer ID to fill in
* @v ref Grant reference to fill in
* @ret id Buffer ID
* @ret rc Return status code
*
* The caller is responsible for ensuring that there is space in the
* ring.
*/
static unsigned int netfront_push ( struct netfront_nic *netfront,
struct netfront_ring *ring,
struct io_buffer *iobuf,
grant_ref_t *ref ) {
static int netfront_push ( struct netfront_nic *netfront,
struct netfront_ring *ring, struct io_buffer *iobuf,
uint16_t *id, grant_ref_t *ref ) {
struct xen_device *xendev = netfront->xendev;
struct xen_hypervisor *xen = xendev->xen;
unsigned int id;
unsigned int next_id;
unsigned int next_ref;
int rc;

/* Sanity check */
assert ( ! netfront_ring_is_full ( ring ) );

/* Allocate buffer ID */
id = ring->ids[ ( ring->id_prod++ ) & ( ring->count - 1 ) ];

/* Store I/O buffer */
assert ( ring->iobufs[id] == NULL );
ring->iobufs[id] = iobuf;
next_id = ring->ids[ ring->id_prod & ( ring->count - 1 ) ];
next_ref = ring->refs[next_id];

/* Grant access to I/O buffer page. I/O buffers are naturally
* aligned, so we never need to worry about crossing a page
* boundary.
*/
*ref = ring->refs[id];
xengrant_permit_access ( xen, ring->refs[id], xendev->backend_id, 0,
iobuf->data );
if ( ( rc = xengrant_permit_access ( xen, next_ref, xendev->backend_id,
0, iobuf->data ) ) != 0 ) {
DBGC ( netfront, "NETFRONT %s could not permit access to "
"%#08lx: %s\n", xendev->key,
virt_to_phys ( iobuf->data ), strerror ( rc ) );
return rc;
}

/* Store I/O buffer */
assert ( ring->iobufs[next_id] == NULL );
ring->iobufs[next_id] = iobuf;

return id;
/* Consume buffer ID */
ring->id_prod++;

/* Return buffer ID and grant reference */
*id = next_id;
*ref = next_ref;

return 0;
}

/**
Expand Down Expand Up @@ -431,13 +451,15 @@ static void netfront_destroy_ring ( struct netfront_nic *netfront,
/**
* Refill receive descriptor ring
*
* @v netfront Netfront device
* @v netdev Network device
*/
static void netfront_refill_rx ( struct netfront_nic *netfront ) {
static void netfront_refill_rx ( struct net_device *netdev ) {
struct netfront_nic *netfront = netdev->priv;
struct xen_device *xendev = netfront->xendev;
struct io_buffer *iobuf;
struct netif_rx_request *request;
int notify;
int rc;

/* Do nothing if ring is already full */
if ( netfront_ring_is_full ( &netfront->rx ) )
Expand All @@ -455,13 +477,20 @@ static void netfront_refill_rx ( struct netfront_nic *netfront ) {

/* Add to descriptor ring */
request = RING_GET_REQUEST ( &netfront->rx_fring,
netfront->rx_fring.req_prod_pvt++);
request->id = netfront_push ( netfront, &netfront->rx, iobuf,
&request->gref );
netfront->rx_fring.req_prod_pvt );
if ( ( rc = netfront_push ( netfront, &netfront->rx,
iobuf, &request->id,
&request->gref ) ) != 0 ) {
netdev_rx_err ( netdev, iobuf, rc );
break;
}
DBGC2 ( netfront, "NETFRONT %s RX id %d ref %d is %#08lx+%zx\n",
xendev->key, request->id, request->gref,
virt_to_phys ( iobuf->data ), iob_tailroom ( iobuf ) );

/* Move to next descriptor */
netfront->rx_fring.req_prod_pvt++;

} while ( ! netfront_ring_is_full ( &netfront->rx ) );

/* Push new descriptors and notify backend if applicable */
Expand Down Expand Up @@ -526,7 +555,7 @@ static int netfront_open ( struct net_device *netdev ) {
}

/* Refill receive descriptor ring */
netfront_refill_rx ( netfront );
netfront_refill_rx ( netdev );

/* Set link up */
netdev_link_up ( netdev );
Expand Down Expand Up @@ -614,6 +643,7 @@ static int netfront_transmit ( struct net_device *netdev,
struct xen_device *xendev = netfront->xendev;
struct netif_tx_request *request;
int notify;
int rc;

/* Check that we have space in the ring */
if ( netfront_ring_is_full ( &netfront->tx ) ) {
Expand All @@ -624,16 +654,21 @@ static int netfront_transmit ( struct net_device *netdev,

/* Add to descriptor ring */
request = RING_GET_REQUEST ( &netfront->tx_fring,
netfront->tx_fring.req_prod_pvt++ );
request->id = netfront_push ( netfront, &netfront->tx, iobuf,
&request->gref );
netfront->tx_fring.req_prod_pvt );
if ( ( rc = netfront_push ( netfront, &netfront->tx, iobuf,
&request->id, &request->gref ) ) != 0 ) {
return rc;
}
request->offset = ( virt_to_phys ( iobuf->data ) & ( PAGE_SIZE - 1 ) );
request->flags = NETTXF_data_validated;
request->size = iob_len ( iobuf );
DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is %#08lx+%zx\n",
xendev->key, request->id, request->gref,
virt_to_phys ( iobuf->data ), iob_len ( iobuf ) );

/* Consume descriptor */
netfront->tx_fring.req_prod_pvt++;

/* Push new descriptor and notify backend if applicable */
RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify );
if ( notify )
Expand Down Expand Up @@ -727,7 +762,6 @@ static void netfront_poll_rx ( struct net_device *netdev ) {
* @v netdev Network device
*/
static void netfront_poll ( struct net_device *netdev ) {
struct netfront_nic *netfront = netdev->priv;

/* Poll for TX completions */
netfront_poll_tx ( netdev );
Expand All @@ -736,7 +770,7 @@ static void netfront_poll ( struct net_device *netdev ) {
netfront_poll_rx ( netdev );

/* Refill RX descriptor ring */
netfront_refill_rx ( netfront );
netfront_refill_rx ( netdev );
}

/** Network device operations */
Expand Down
8 changes: 5 additions & 3 deletions src/include/ipxe/xen.h
Expand Up @@ -27,9 +27,11 @@ struct xen_hypercall;
/** A Xen grant table */
struct xen_grant {
/** Grant table entries */
union grant_entry_v2 *table;
/** Number of grant table entries (must be a power of two) */
unsigned int count;
struct grant_entry_v1 *table;
/** Total grant table length */
size_t len;
/** Entry size shift (for later version tables) */
unsigned int shift;
/** Number of grant table entries in use */
unsigned int used;
/** Most recently used grant reference */
Expand Down

0 comments on commit be79ca5

Please sign in to comment.