mirror of
https://github.com/ipxe/ipxe
synced 2025-12-26 01:22:37 +03:00
[xen] Use version 1 grant tables by default
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>
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -10,10 +10,14 @@
|
||||
FILE_LICENCE ( GPL2_OR_LATER );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <ipxe/io.h>
|
||||
#include <ipxe/xen.h>
|
||||
#include <xen/grant_table.h>
|
||||
|
||||
/** Induced failure rate (for testing) */
|
||||
#define XENGRANT_FAIL_RATE 0
|
||||
|
||||
/**
|
||||
* Query grant table size
|
||||
*
|
||||
@@ -46,6 +50,90 @@ xengrant_set_version ( struct xen_hypervisor *xen,
|
||||
virt_to_phys ( version ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grant table version
|
||||
*
|
||||
* @v xen Xen hypervisor
|
||||
* @v version Version
|
||||
* @ret xenrc Xen status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
xengrant_get_version ( struct xen_hypervisor *xen,
|
||||
struct gnttab_get_version *version ) {
|
||||
|
||||
return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op,
|
||||
GNTTABOP_get_version,
|
||||
virt_to_phys ( version ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of grant table entries
|
||||
*
|
||||
* @v xen Xen hypervisor
|
||||
* @ret entries Number of grant table entries
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
xengrant_entries ( struct xen_hypervisor *xen ) {
|
||||
|
||||
return ( ( xen->grant.len / sizeof ( xen->grant.table[0] ) )
|
||||
>> xen->grant.shift );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grant table entry header
|
||||
*
|
||||
* @v xen Xen hypervisor
|
||||
* @v ref Grant reference
|
||||
* @ret hdr Grant table entry header
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) struct grant_entry_header *
|
||||
xengrant_header ( struct xen_hypervisor *xen, grant_ref_t ref ) {
|
||||
struct grant_entry_v1 *v1;
|
||||
|
||||
v1 = &xen->grant.table[ ref << xen->grant.shift ];
|
||||
return ( container_of ( &v1->flags, struct grant_entry_header, flags ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version 1 grant table entry
|
||||
*
|
||||
* @v hdr Grant table entry header
|
||||
* @ret v1 Version 1 grant table entry
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) struct grant_entry_v1 *
|
||||
xengrant_v1 ( struct grant_entry_header *hdr ) {
|
||||
|
||||
return ( container_of ( &hdr->flags, struct grant_entry_v1, flags ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version 2 grant table entry
|
||||
*
|
||||
* @v hdr Grant table entry header
|
||||
* @ret v2 Version 2 grant table entry
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) union grant_entry_v2 *
|
||||
xengrant_v2 ( struct grant_entry_header *hdr ) {
|
||||
|
||||
return ( container_of ( &hdr->flags, union grant_entry_v2, hdr.flags ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero grant table entry
|
||||
*
|
||||
* @v xen Xen hypervisor
|
||||
* @v hdr Grant table entry header
|
||||
*/
|
||||
static inline void xengrant_zero ( struct xen_hypervisor *xen,
|
||||
struct grant_entry_header *hdr ) {
|
||||
uint32_t *dword = ( ( uint32_t * ) hdr );
|
||||
unsigned int i = ( ( sizeof ( xen->grant.table[0] ) / sizeof ( *dword ))
|
||||
<< xen->grant.shift );
|
||||
|
||||
while ( i-- )
|
||||
writel ( 0, dword++ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate access to a page
|
||||
*
|
||||
@@ -54,10 +142,10 @@ xengrant_set_version ( struct xen_hypervisor *xen,
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) {
|
||||
union grant_entry_v2 *entry = &xen->grant.table[ref];
|
||||
struct grant_entry_header *hdr = xengrant_header ( xen, ref );
|
||||
|
||||
/* Sanity check */
|
||||
assert ( ( readw ( &entry->hdr.flags ) &
|
||||
assert ( ( readw ( &hdr->flags ) &
|
||||
( GTF_reading | GTF_writing ) ) == 0 );
|
||||
|
||||
/* This should apparently be done using a cmpxchg instruction.
|
||||
@@ -65,7 +153,10 @@ xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) {
|
||||
* mainly since our control flow generally does not permit
|
||||
* failure paths to themselves fail.
|
||||
*/
|
||||
writew ( 0, &entry->hdr.flags );
|
||||
writew ( 0, &hdr->flags );
|
||||
|
||||
/* Leave reference marked as in-use (see xengrant_alloc()) */
|
||||
writew ( DOMID_SELF, &hdr->domid );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,24 +167,63 @@ xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) {
|
||||
* @v domid Domain ID
|
||||
* @v subflags Additional flags
|
||||
* @v page Page start
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
xengrant_permit_access ( struct xen_hypervisor *xen, grant_ref_t ref,
|
||||
domid_t domid, unsigned int subflags, void *page ) {
|
||||
union grant_entry_v2 *entry = &xen->grant.table[ref];
|
||||
struct grant_entry_header *hdr = xengrant_header ( xen, ref );
|
||||
struct grant_entry_v1 *v1 = xengrant_v1 ( hdr );
|
||||
union grant_entry_v2 *v2 = xengrant_v2 ( hdr );
|
||||
unsigned long frame = ( virt_to_phys ( page ) / PAGE_SIZE );
|
||||
|
||||
writew ( domid, &entry->full_page.hdr.domid );
|
||||
if ( sizeof ( physaddr_t ) == sizeof ( uint64_t ) ) {
|
||||
writeq ( frame, &entry->full_page.frame );
|
||||
} else {
|
||||
writel ( frame, &entry->full_page.frame );
|
||||
/* Fail (for test purposes) if applicable */
|
||||
if ( ( XENGRANT_FAIL_RATE > 0 ) &&
|
||||
( random() % XENGRANT_FAIL_RATE ) == 0 ) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Record frame number. This may fail on a 64-bit system if
|
||||
* we are using v1 grant tables. On a 32-bit system, there is
|
||||
* no way for this code path to fail (with either v1 or v2
|
||||
* grant tables); we allow the compiler to optimise the
|
||||
* failure paths away to save space.
|
||||
*/
|
||||
if ( sizeof ( physaddr_t ) == sizeof ( uint64_t ) ) {
|
||||
|
||||
/* 64-bit system */
|
||||
if ( xen->grant.shift ) {
|
||||
/* Version 2 table: no possible failure */
|
||||
writeq ( frame, &v2->full_page.frame );
|
||||
} else {
|
||||
/* Version 1 table: may fail if address above 16TB */
|
||||
if ( frame > 0xffffffffUL )
|
||||
return -ERANGE;
|
||||
writel ( frame, &v1->frame );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* 32-bit system */
|
||||
if ( xen->grant.shift ) {
|
||||
/* Version 2 table: no possible failure */
|
||||
writel ( frame, &v2->full_page.frame );
|
||||
} else {
|
||||
/* Version 1 table: no possible failure */
|
||||
writel ( frame, &v1->frame );
|
||||
}
|
||||
}
|
||||
|
||||
/* Record domain ID and flags */
|
||||
writew ( domid, &hdr->domid );
|
||||
wmb();
|
||||
writew ( ( GTF_permit_access | subflags ), &entry->full_page.hdr.flags);
|
||||
writew ( ( GTF_permit_access | subflags ), &hdr->flags );
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int xengrant_init ( struct xen_hypervisor *xen );
|
||||
extern int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
|
||||
unsigned int count );
|
||||
extern void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
|
||||
|
||||
Reference in New Issue
Block a user