[gve] Parse option list returned in device descriptor

Provide space for the device to return its list of supported options.
Parse the option list and record the existence of each option in a
support bitmask.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-09-25 14:42:19 +01:00
parent 6464f2edb8
commit ee9aea7893
2 changed files with 79 additions and 1 deletions

View File

@@ -420,6 +420,12 @@ static int gve_describe ( struct gve_nic *gve ) {
struct net_device *netdev = gve->netdev; struct net_device *netdev = gve->netdev;
struct gve_device_descriptor *desc = &gve->scratch.buf->desc; struct gve_device_descriptor *desc = &gve->scratch.buf->desc;
union gve_admin_command *cmd; union gve_admin_command *cmd;
struct gve_option *opt;
unsigned int count;
unsigned int id;
size_t offset;
size_t max;
size_t len;
int rc; int rc;
/* Construct request */ /* Construct request */
@@ -451,6 +457,38 @@ static int gve_describe ( struct gve_nic *gve ) {
gve, eth_ntoa ( netdev->hw_addr ), gve, eth_ntoa ( netdev->hw_addr ),
inet_ntoa ( desc->mac.in ), netdev->mtu ); inet_ntoa ( desc->mac.in ), netdev->mtu );
/* Parse options */
count = be16_to_cpu ( desc->opt_count );
max = be16_to_cpu ( desc->len );
gve->options = 0;
for ( offset = offsetof ( typeof ( *desc ), opts ) ; count ;
count--, offset += len ) {
/* Check space for option header */
if ( ( offset + sizeof ( *opt ) ) > max ) {
DBGC ( gve, "GVE %p underlength option at +%#02zx:\n",
gve, offset );
DBGC_HDA ( gve, 0, desc, sizeof ( *desc ) );
return -EINVAL;
}
opt = ( ( ( void * ) desc ) + offset );
/* Check space for option body */
len = ( sizeof ( *opt ) + be16_to_cpu ( opt->len ) );
if ( ( offset + len ) > max ) {
DBGC ( gve, "GVE %p malformed option at +%#02zx:\n",
gve, offset );
DBGC_HDA ( gve, 0, desc, sizeof ( *desc ) );
return -EINVAL;
}
/* Record option as supported */
id = be16_to_cpu ( opt->id );
if ( id < ( 8 * sizeof ( gve->options ) ) )
gve->options |= ( 1 << id );
}
DBGC ( gve, "GVE %p supports options %#08x\n", gve, gve->options );
return 0; return 0;
} }

View File

@@ -155,10 +155,48 @@ struct gve_device_descriptor {
uint8_t reserved_c[4]; uint8_t reserved_c[4];
/** MAC address */ /** MAC address */
struct google_mac mac; struct google_mac mac;
/** Number of device options */
uint16_t opt_count;
/** Total length (including this header) */
uint16_t len;
/** Reserved */ /** Reserved */
uint8_t reserved_d[10]; uint8_t reserved_d[6];
/** Space for options
*
* There is no specified upper limit, and no negotiation
* mechanism for the amount of space required. We allow space
* for seems like a reasonable number of options.
*/
uint8_t opts[216];
} __attribute__ (( packed )); } __attribute__ (( packed ));
/** Device option header */
struct gve_option {
/** Option ID */
uint16_t id;
/** Length (excluding this header) */
uint16_t len;
/** Required feature mask
*
* The purpose of this field is remarkably unclear. The Linux
* kernel driver does define enum gve_dev_opt_req_feat_mask,
* but every member of this enum has a zero value.
*/
uint32_t required;
} __attribute__ (( packed ));
/** In-order descriptor queues with raw DMA addressing */
#define GVE_OPT_GQI_RDA 0x02
/** In-order descriptor queues with queue page list addressing */
#define GVE_OPT_GQI_QPL 0x03
/** Out-of-order descriptor queues with raw DMA addressing */
#define GVE_OPT_DQO_RDA 0x04
/** Out-of-order descriptor queues with queue page list addressing */
#define GVE_OPT_DQO_QPL 0x07
/** Configure device resources command */ /** Configure device resources command */
#define GVE_ADMIN_CONFIGURE 0x0002 #define GVE_ADMIN_CONFIGURE 0x0002
@@ -663,6 +701,8 @@ struct gve_nic {
struct gve_events events; struct gve_events events;
/** Scratch buffer */ /** Scratch buffer */
struct gve_scratch scratch; struct gve_scratch scratch;
/** Supported options */
uint32_t options;
/** Transmit queue */ /** Transmit queue */
struct gve_queue tx; struct gve_queue tx;