From 6cee8904d1d26cd3503018e9514b8671a9b0a860 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 30 Nov 2010 00:22:49 +0000 Subject: [PATCH 01/23] [dhcp] Remove redundant length fields in struct dhcp_packet The max_len field is never used, and the len field is used only by dhcp_tx(). Remove these two fields, and perform the necessary trivial calculation in dhcp_tx() instead. Signed-off-by: Michael Brown --- src/include/ipxe/dhcppkt.h | 14 ++++++++++---- src/net/dhcppkt.c | 12 +----------- src/net/udp/dhcp.c | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/include/ipxe/dhcppkt.h b/src/include/ipxe/dhcppkt.h index 5709cc7d..b004800d 100644 --- a/src/include/ipxe/dhcppkt.h +++ b/src/include/ipxe/dhcppkt.h @@ -22,10 +22,6 @@ struct dhcp_packet { struct refcnt refcnt; /** The DHCP packet contents */ struct dhcphdr *dhcphdr; - /** Maximum length of the DHCP packet buffer */ - size_t max_len; - /** Used length of the DHCP packet buffer */ - size_t len; /** DHCP options */ struct dhcp_options options; /** Settings interface */ @@ -54,6 +50,16 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) { ref_put ( &dhcppkt->refcnt ); } +/** + * Get used length of DHCP packet + * + * @v dhcppkt DHCP packet + * @ret len Used length + */ +static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) { + return ( offsetof ( struct dhcphdr, options ) + dhcppkt->options.len ); +} + extern int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, const void *data, size_t len ); extern int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, diff --git a/src/net/dhcppkt.c b/src/net/dhcppkt.c index b68f4e08..e043bb5d 100644 --- a/src/net/dhcppkt.c +++ b/src/net/dhcppkt.c @@ -147,7 +147,6 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, const void *data, size_t len ) { struct dhcp_packet_field *field; void *field_data; - int rc; /* If this is a special field, fill it in */ if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { @@ -163,13 +162,7 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, } /* Otherwise, use the generic options block */ - rc = dhcpopt_store ( &dhcppkt->options, tag, data, len ); - - /* Update our used-length field */ - dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + - dhcppkt->options.len ); - - return rc; + return dhcpopt_store ( &dhcppkt->options, tag, data, len ); } /** @@ -273,11 +266,8 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, size_t len ) { ref_init ( &dhcppkt->refcnt, NULL ); dhcppkt->dhcphdr = data; - dhcppkt->max_len = len; dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, ( len - offsetof ( struct dhcphdr, options ) ) ); - dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + - dhcppkt->options.len ); settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations, &dhcppkt->refcnt, 0 ); } diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 64c49cdd..e6d127ab 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -1122,7 +1122,7 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { } /* Transmit the packet */ - iob_put ( iobuf, dhcppkt.len ); + iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) ); if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ), &meta ) ) != 0 ) { DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n", From 310d46c1edcefeb918125de0ee2e123c2940a70b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 30 Nov 2010 00:31:01 +0000 Subject: [PATCH 02/23] [dhcp] Rename length fields for DHCP options Rename "len" to "used_len" and "max_len" to "alloc_len". Signed-off-by: Michael Brown --- src/include/ipxe/dhcpopts.h | 10 +++++----- src/include/ipxe/dhcppkt.h | 3 ++- src/net/cachedhcp.c | 2 +- src/net/dhcpopts.c | 34 +++++++++++++++++----------------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/include/ipxe/dhcpopts.h b/src/include/ipxe/dhcpopts.h index fea99d59..d88036e3 100644 --- a/src/include/ipxe/dhcpopts.h +++ b/src/include/ipxe/dhcpopts.h @@ -15,10 +15,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct dhcp_options { /** Option block raw data */ void *data; - /** Option block length */ - size_t len; - /** Option block maximum length */ - size_t max_len; + /** Option block used length */ + size_t used_len; + /** Option block allocated length */ + size_t alloc_len; }; extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, @@ -29,6 +29,6 @@ extern int dhcpopt_extensible_store ( struct dhcp_options *options, extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, void *data, size_t len ); extern void dhcpopt_init ( struct dhcp_options *options, - void *data, size_t max_len ); + void *data, size_t alloc_len ); #endif /* _IPXE_DHCPOPTS_H */ diff --git a/src/include/ipxe/dhcppkt.h b/src/include/ipxe/dhcppkt.h index b004800d..3179a6bb 100644 --- a/src/include/ipxe/dhcppkt.h +++ b/src/include/ipxe/dhcppkt.h @@ -57,7 +57,8 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) { * @ret len Used length */ static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) { - return ( offsetof ( struct dhcphdr, options ) + dhcppkt->options.len ); + return ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.used_len ); } extern int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, diff --git a/src/net/cachedhcp.c b/src/net/cachedhcp.c index 294624c8..36f480a9 100644 --- a/src/net/cachedhcp.c +++ b/src/net/cachedhcp.c @@ -58,7 +58,7 @@ void store_cached_dhcpack ( userptr_t data, size_t len ) { dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) ); copy_from_user ( dhcphdr, data, 0, len ); dhcppkt_init ( dhcppkt, dhcphdr, len ); - DBG_HD ( dhcppkt->options.data, dhcppkt->options.len ); + DBG_HD ( dhcppkt->options.data, dhcppkt->options.used_len ); /* Register settings on the last opened network device. * This will have the effect of registering cached settings diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c index f351c32e..214a8244 100644 --- a/src/net/dhcpopts.c +++ b/src/net/dhcpopts.c @@ -117,7 +117,7 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, unsigned int original_tag __attribute__ (( unused )) = tag; struct dhcp_option *option; int offset = 0; - ssize_t remaining = options->len; + ssize_t remaining = options->used_len; unsigned int option_len; /* Sanity check */ @@ -199,8 +199,8 @@ static int resize_dhcp_option ( struct dhcp_options *options, DBGC ( options, "DHCPOPT %p overlength option\n", options ); return -ENOSPC; } - new_options_len = ( options->len + delta ); - if ( new_options_len > options->max_len ) { + new_options_len = ( options->used_len + delta ); + if ( new_options_len > options->alloc_len ) { /* Reallocate options block if allowed to do so. */ if ( can_realloc ) { new_data = realloc ( options->data, new_options_len ); @@ -211,7 +211,7 @@ static int resize_dhcp_option ( struct dhcp_options *options, return -ENOMEM; } options->data = new_data; - options->max_len = new_options_len; + options->alloc_len = new_options_len; } else { DBGC ( options, "DHCPOPT %p out of space\n", options ); return -ENOMEM; @@ -227,13 +227,13 @@ static int resize_dhcp_option ( struct dhcp_options *options, } encapsulator->len = new_encapsulator_len; } - options->len = new_options_len; + options->used_len = new_options_len; /* Move remainder of option data */ option = dhcp_option ( options, offset ); source = ( ( ( void * ) option ) + old_len ); dest = ( ( ( void * ) option ) + new_len ); - end = ( options->data + options->max_len ); + end = ( options->data + options->alloc_len ); memmove ( dest, source, ( end - dest ) ); return 0; @@ -277,7 +277,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, creation_offset = find_dhcp_option_with_encap ( options, DHCP_END, NULL ); if ( creation_offset < 0 ) - creation_offset = options->len; + creation_offset = options->used_len; /* Find old instance of this option, if any */ offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); if ( offset >= 0 ) { @@ -402,14 +402,14 @@ int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, * The "used length" field will be updated based on scanning through * the block to find the end of the options. */ -static void dhcpopt_update_len ( struct dhcp_options *options ) { +static void dhcpopt_update_used_len ( struct dhcp_options *options ) { struct dhcp_option *option; int offset = 0; - ssize_t remaining = options->max_len; + ssize_t remaining = options->alloc_len; unsigned int option_len; /* Find last non-pad option */ - options->len = 0; + options->used_len = 0; while ( remaining ) { option = dhcp_option ( options, offset ); option_len = dhcp_option_len ( option ); @@ -418,7 +418,7 @@ static void dhcpopt_update_len ( struct dhcp_options *options ) { break; offset += option_len; if ( option->tag != DHCP_PAD ) - options->len = offset; + options->used_len = offset; } } @@ -427,21 +427,21 @@ static void dhcpopt_update_len ( struct dhcp_options *options ) { * * @v options Uninitialised DHCP option block * @v data Memory for DHCP option data - * @v max_len Length of memory for DHCP option data + * @v alloc_len Length of memory for DHCP option data * * The memory content must already be filled with valid DHCP options. * A zeroed block counts as a block of valid DHCP options. */ void dhcpopt_init ( struct dhcp_options *options, void *data, - size_t max_len ) { + size_t alloc_len ) { /* Fill in fields */ options->data = data; - options->max_len = max_len; + options->alloc_len = alloc_len; /* Update length */ - dhcpopt_update_len ( options ); + dhcpopt_update_used_len ( options ); - DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n", - options, options->data, options->len, options->max_len ); + DBGC ( options, "DHCPOPT %p created (data %p lengths %#zx,%#zx)\n", + options, options->data, options->used_len, options->alloc_len ); } From 17b6a3c506d94b8dc8bdae89828bac20ef56b1ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Jan 2011 23:58:11 +0000 Subject: [PATCH 03/23] [dhcp] Allow use of custom reallocation functions for DHCP option blocks Allow functions other than realloc() to be used to reallocate DHCP option block data, and specify the reallocation function at the time of calling dhcpopt_init(). Signed-off-by: Michael Brown --- src/core/nvo.c | 3 +- src/include/ipxe/dhcpopts.h | 15 +++-- src/net/dhcpopts.c | 115 ++++++++++++++++++------------------ src/net/dhcppkt.c | 3 +- 4 files changed, 74 insertions(+), 62 deletions(-) diff --git a/src/core/nvo.c b/src/core/nvo.c index 1a886c0f..9b325db7 100644 --- a/src/core/nvo.c +++ b/src/core/nvo.c @@ -133,7 +133,8 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { memset ( nvo->data, 0, nvo->total_len ); } - dhcpopt_init ( &nvo->dhcpopts, options_data, options_len ); + dhcpopt_init ( &nvo->dhcpopts, options_data, options_len, + dhcpopt_no_realloc ); } /** diff --git a/src/include/ipxe/dhcpopts.h b/src/include/ipxe/dhcpopts.h index d88036e3..fe07d903 100644 --- a/src/include/ipxe/dhcpopts.h +++ b/src/include/ipxe/dhcpopts.h @@ -19,16 +19,23 @@ struct dhcp_options { size_t used_len; /** Option block allocated length */ size_t alloc_len; + /** Reallocate option block raw data + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ + int ( * realloc ) ( struct dhcp_options *options, size_t len ); }; extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, const void *data, size_t len ); -extern int dhcpopt_extensible_store ( struct dhcp_options *options, - unsigned int tag, - const void *data, size_t len ); extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, void *data, size_t len ); extern void dhcpopt_init ( struct dhcp_options *options, - void *data, size_t alloc_len ); + void *data, size_t alloc_len, + int ( * realloc ) ( struct dhcp_options *options, + size_t len ) ); +extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ); #endif /* _IPXE_DHCPOPTS_H */ diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c index 214a8244..d1330eae 100644 --- a/src/net/dhcpopts.c +++ b/src/net/dhcpopts.c @@ -169,6 +169,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, return -ENOENT; } +/** + * Refuse to reallocate DHCP option block + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ +int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) { + return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC ); +} + /** * Resize a DHCP option * @@ -177,46 +188,44 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, * @v encap_offset Offset of encapsulating offset (or -ve for none) * @v old_len Old length (including header) * @v new_len New length (including header) - * @v can_realloc Can reallocate options data if necessary * @ret rc Return status code */ static int resize_dhcp_option ( struct dhcp_options *options, int offset, int encap_offset, - size_t old_len, size_t new_len, - int can_realloc ) { + size_t old_len, size_t new_len ) { struct dhcp_option *encapsulator; struct dhcp_option *option; ssize_t delta = ( new_len - old_len ); - size_t new_options_len; + size_t old_alloc_len; + size_t new_used_len; size_t new_encapsulator_len; - void *new_data; void *source; void *dest; void *end; + int rc; - /* Check for sufficient space, and update length fields */ + /* Check for sufficient space */ if ( new_len > DHCP_MAX_LEN ) { DBGC ( options, "DHCPOPT %p overlength option\n", options ); return -ENOSPC; } - new_options_len = ( options->used_len + delta ); - if ( new_options_len > options->alloc_len ) { - /* Reallocate options block if allowed to do so. */ - if ( can_realloc ) { - new_data = realloc ( options->data, new_options_len ); - if ( ! new_data ) { - DBGC ( options, "DHCPOPT %p could not " - "reallocate to %zd bytes\n", options, - new_options_len ); - return -ENOMEM; - } - options->data = new_data; - options->alloc_len = new_options_len; - } else { - DBGC ( options, "DHCPOPT %p out of space\n", options ); - return -ENOMEM; + new_used_len = ( options->used_len + delta ); + + /* Expand options block, if necessary */ + if ( new_used_len > options->alloc_len ) { + /* Reallocate options block */ + old_alloc_len = options->alloc_len; + if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ + DBGC ( options, "DHCPOPT %p could not reallocate to " + "%zd bytes\n", options, new_used_len ); + return rc; } + /* Clear newly allocated space */ + memset ( ( options->data + old_alloc_len ), 0, + ( options->alloc_len - old_alloc_len ) ); } + + /* Update encapsulator, if applicable */ if ( encap_offset >= 0 ) { encapsulator = dhcp_option ( options, encap_offset ); new_encapsulator_len = ( encapsulator->len + delta ); @@ -227,7 +236,9 @@ static int resize_dhcp_option ( struct dhcp_options *options, } encapsulator->len = new_encapsulator_len; } - options->used_len = new_options_len; + + /* Update used length */ + options->used_len = new_used_len; /* Move remainder of option data */ option = dhcp_option ( options, offset ); @@ -236,6 +247,15 @@ static int resize_dhcp_option ( struct dhcp_options *options, end = ( options->data + options->alloc_len ); memmove ( dest, source, ( end - dest ) ); + /* Shrink options block, if applicable */ + if ( new_used_len < options->alloc_len ) { + if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ + DBGC ( options, "DHCPOPT %p could not reallocate to " + "%zd bytes\n", options, new_used_len ); + return rc; + } + } + return 0; } @@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options, * @v tag DHCP option tag * @v data New value for DHCP option * @v len Length of value, in bytes - * @v can_realloc Can reallocate options data if necessary * @ret offset Offset of DHCP option, or negative error * * Sets the value of a DHCP option within the options block. The @@ -258,9 +277,8 @@ static int resize_dhcp_option ( struct dhcp_options *options, * be left with its original value. */ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len, - int can_realloc ) { - static const uint8_t empty_encapsulator[] = { DHCP_END }; + const void *data, size_t len ) { + static const uint8_t empty_encap[] = { DHCP_END }; int offset; int encap_offset = -1; int creation_offset; @@ -291,10 +309,12 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, /* Ensure that encapsulator exists, if required */ if ( encap_tag ) { - if ( encap_offset < 0 ) - encap_offset = set_dhcp_option ( options, encap_tag, - empty_encapsulator, 1, - can_realloc ); + if ( encap_offset < 0 ) { + encap_offset = + set_dhcp_option ( options, encap_tag, + empty_encap, + sizeof ( empty_encap ) ); + } if ( encap_offset < 0 ) return encap_offset; creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN ); @@ -306,8 +326,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, /* Resize option to fit new data */ if ( ( rc = resize_dhcp_option ( options, offset, encap_offset, - old_len, new_len, - can_realloc ) ) != 0 ) + old_len, new_len ) ) != 0 ) return rc; /* Copy new data into option, if applicable */ @@ -322,7 +341,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, if ( encap_offset >= 0 ) { option = dhcp_option ( options, encap_offset ); if ( option->len <= 1 ) - set_dhcp_option ( options, encap_tag, NULL, 0, 0 ); + set_dhcp_option ( options, encap_tag, NULL, 0 ); } return offset; @@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, const void *data, size_t len ) { int offset; - offset = set_dhcp_option ( options, tag, data, len, 0 ); - if ( offset < 0 ) - return offset; - return 0; -} - -/** - * Store value of DHCP option setting, extending options block if necessary - * - * @v options DHCP option block - * @v tag Setting tag number - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len ) { - int offset; - - offset = set_dhcp_option ( options, tag, data, len, 1 ); + offset = set_dhcp_option ( options, tag, data, len ); if ( offset < 0 ) return offset; return 0; @@ -428,16 +428,19 @@ static void dhcpopt_update_used_len ( struct dhcp_options *options ) { * @v options Uninitialised DHCP option block * @v data Memory for DHCP option data * @v alloc_len Length of memory for DHCP option data + * @v realloc DHCP option block reallocator * * The memory content must already be filled with valid DHCP options. * A zeroed block counts as a block of valid DHCP options. */ -void dhcpopt_init ( struct dhcp_options *options, void *data, - size_t alloc_len ) { +void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len, + int ( * realloc ) ( struct dhcp_options *options, + size_t len ) ) { /* Fill in fields */ options->data = data; options->alloc_len = alloc_len; + options->realloc = realloc; /* Update length */ dhcpopt_update_used_len ( options ); diff --git a/src/net/dhcppkt.c b/src/net/dhcppkt.c index e043bb5d..237c3e2c 100644 --- a/src/net/dhcppkt.c +++ b/src/net/dhcppkt.c @@ -267,7 +267,8 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, ref_init ( &dhcppkt->refcnt, NULL ); dhcppkt->dhcphdr = data; dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, - ( len - offsetof ( struct dhcphdr, options ) ) ); + ( len - offsetof ( struct dhcphdr, options ) ), + dhcpopt_no_realloc ); settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations, &dhcppkt->refcnt, 0 ); } From 8f8b55f18728e2cf95909e9eb3c361e60d4d3827 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Jan 2011 19:56:59 +0000 Subject: [PATCH 04/23] [nvs] Allow for non-volatile storage devices without block boundaries Signed-off-by: Michael Brown --- src/drivers/nvs/nvs.c | 48 ++++++++++++++++++++++++++-------------- src/drivers/nvs/nvsvpd.c | 1 - 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/drivers/nvs/nvs.c b/src/drivers/nvs/nvs.c index efa49ac5..a4a06ccf 100644 --- a/src/drivers/nvs/nvs.c +++ b/src/drivers/nvs/nvs.c @@ -30,6 +30,34 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** + * Calculate length up to next block boundary + * + * @v nvs NVS device + * @v address Starting address + * @v max_len Maximum length + * @ret len Length to use, stopping at block boundaries + */ +static size_t nvs_frag_len ( struct nvs_device *nvs, unsigned int address, + size_t max_len ) { + size_t frag_len; + + /* If there are no block boundaries, return the maximum length */ + if ( ! nvs->block_size ) + return max_len; + + /* Calculate space remaining up to next block boundary */ + frag_len = ( ( nvs->block_size - + ( address & ( nvs->block_size - 1 ) ) ) + << nvs->word_len_log2 ); + + /* Limit to maximum length */ + if ( max_len < frag_len ) + return max_len; + + return frag_len; +} + /** * Read from non-volatile storage device * @@ -51,14 +79,8 @@ int nvs_read ( struct nvs_device *nvs, unsigned int address, while ( len ) { - /* Calculate space remaining up to next block boundary */ - frag_len = ( ( nvs->block_size - - ( address & ( nvs->block_size - 1 ) ) ) - << nvs->word_len_log2 ); - - /* Limit to space remaining in buffer */ - if ( frag_len > len ) - frag_len = len; + /* Calculate length to read, stopping at block boundaries */ + frag_len = nvs_frag_len ( nvs, address, len ); /* Read this portion of the buffer from the device */ if ( ( rc = nvs->read ( nvs, address, data, frag_len ) ) != 0 ) @@ -122,14 +144,8 @@ int nvs_write ( struct nvs_device *nvs, unsigned int address, while ( len ) { - /* Calculate space remaining up to next block boundary */ - frag_len = ( ( nvs->block_size - - ( address & ( nvs->block_size - 1 ) ) ) - << nvs->word_len_log2 ); - - /* Limit to space remaining in buffer */ - if ( frag_len > len ) - frag_len = len; + /* Calculate length to write, stopping at block boundaries */ + frag_len = nvs_frag_len ( nvs, address, len ); /* Write this portion of the buffer to the device */ if ( ( rc = nvs->write ( nvs, address, data, frag_len ) ) != 0) diff --git a/src/drivers/nvs/nvsvpd.c b/src/drivers/nvs/nvsvpd.c index 1f61a55d..b53829e8 100644 --- a/src/drivers/nvs/nvsvpd.c +++ b/src/drivers/nvs/nvsvpd.c @@ -111,7 +111,6 @@ int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, } /* Initialise NVS device */ - nvsvpd->nvs.block_size = 1; nvsvpd->nvs.size = len; nvsvpd->nvs.read = nvs_vpd_read; nvsvpd->nvs.write = nvs_vpd_write; From 1651d4f6d7f764ff5bfafc12bd058a88e49b0ff5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 30 Nov 2010 01:10:38 +0000 Subject: [PATCH 05/23] [nvo] Remove the non-volatile options fragment list Since its implementation several years ago, no driver has used a fragment list containing more than a single fragment. Simplify the NVO core and the drivers that use it by removing the whole concept of the fragment list, and using a simple (address,length) pair instead. Signed-off-by: Michael Brown --- src/core/nvo.c | 73 ++++++++++++----------------------- src/drivers/net/etherfabric.c | 11 ++---- src/drivers/net/myri10ge.c | 16 ++------ src/drivers/net/natsemi.c | 18 ++++----- src/drivers/net/rtl8139.c | 20 ++++------ src/include/ipxe/nvo.h | 23 +++-------- 6 files changed, 50 insertions(+), 111 deletions(-) diff --git a/src/core/nvo.c b/src/core/nvo.c index 9b325db7..c5968c6b 100644 --- a/src/core/nvo.c +++ b/src/core/nvo.c @@ -43,7 +43,7 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { uint8_t sum = 0; unsigned int i; - for ( i = 0 ; i < nvo->total_len ; i++ ) { + for ( i = 0 ; i < nvo->len ; i++ ) { sum += *(data++); } return sum; @@ -56,19 +56,14 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { * @ret rc Return status code */ static int nvo_load ( struct nvo_block *nvo ) { - void *data = nvo->data; - struct nvo_fragment *frag; int rc; - /* Read data a fragment at a time */ - for ( frag = nvo->fragments ; frag->len ; frag++ ) { - if ( ( rc = nvs_read ( nvo->nvs, frag->address, data, - frag->len ) ) != 0 ) { - DBGC ( nvo, "NVO %p could not read %zd bytes at " - "%#04x\n", nvo, frag->len, frag->address ); - return rc; - } - data += frag->len; + /* Read data */ + if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not read %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; } DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); @@ -82,23 +77,18 @@ static int nvo_load ( struct nvo_block *nvo ) { * @ret rc Return status code */ static int nvo_save ( struct nvo_block *nvo ) { - void *data = nvo->data; - uint8_t *checksum = data; - struct nvo_fragment *frag; + uint8_t *checksum = nvo->data; int rc; /* Recalculate checksum */ *checksum -= nvo_checksum ( nvo ); - /* Write data a fragment at a time */ - for ( frag = nvo->fragments ; frag->len ; frag++ ) { - if ( ( rc = nvs_write ( nvo->nvs, frag->address, data, - frag->len ) ) != 0 ) { - DBGC ( nvo, "NVO %p could not write %zd bytes at " - "%#04x\n", nvo, frag->len, frag->address ); - return rc; - } - data += frag->len; + /* Write data */ + if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not write %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; } DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); @@ -120,7 +110,7 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { /* Steal one byte for the checksum */ options_data = ( nvo->data + 1 ); - options_len = ( nvo->total_len - 1 ); + options_len = ( nvo->len - 1 ); /* If checksum fails, or options data starts with a zero, * assume the whole block is invalid. This should capture the @@ -130,7 +120,7 @@ static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " "assuming empty\n", nvo, nvo_checksum ( nvo ), options_data[0] ); - memset ( nvo->data, 0, nvo->total_len ); + memset ( nvo->data, 0, nvo->len ); } dhcpopt_init ( &nvo->dhcpopts, options_data, options_len, @@ -198,13 +188,15 @@ static struct settings_operations nvo_settings_operations = { * * @v nvo Non-volatile options block * @v nvs Underlying non-volatile storage device - * @v fragments List of option-containing fragments, or NULL + * @v address Address within NVS device + * @v len Length of non-volatile options data * @v refcnt Containing object reference counter, or NULL */ void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - struct nvo_fragment *fragments, struct refcnt *refcnt ) { + size_t address, size_t len, struct refcnt *refcnt ) { nvo->nvs = nvs; - nvo->fragments = fragments; + nvo->address = address; + nvo->len = len; settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 0 ); } @@ -216,34 +208,17 @@ void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, * @ret rc Return status code */ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { - struct nvo_fragment *fragment = nvo->fragments; int rc; - /* Calculate total length of all fragments, if applicable */ - if ( fragment ) { - for ( ; fragment->len ; fragment++ ) - nvo->total_len += fragment->len; - } else { - nvo->total_len = nvo->nvs->size; - } - - /* Allocate memory for options (and fragment list, if applicable) */ - nvo->data = zalloc ( nvo->total_len + - ( fragment ? 0 : ( 2 * sizeof ( *fragment ) ) ) ); + /* Allocate memory for options */ + nvo->data = zalloc ( nvo->len ); if ( ! nvo->data ) { DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", - nvo, nvo->total_len ); + nvo, nvo->len ); rc = -ENOMEM; goto err_malloc; } - /* Create fragment list, if applicable */ - if ( ! fragment ) { - fragment = ( nvo->data + nvo->total_len ); - fragment->len = nvo->total_len; - nvo->fragments = fragment; - } - /* Read data from NVS */ if ( ( rc = nvo_load ( nvo ) ) != 0 ) goto err_load; diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 352cdd99..77d21247 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -1492,12 +1492,6 @@ fail1: return rc; } -/** Portion of EEPROM available for non-volatile options */ -static struct nvo_fragment falcon_nvo_fragments[] = { - { 0x100, 0xf0 }, - { 0, 0 } -}; - /******************************************************************************* * * @@ -3277,9 +3271,10 @@ falcon_probe_spi ( struct efab_nic *efab ) } /* If the device has EEPROM attached, then advertise NVO space */ - if ( has_eeprom ) - nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, falcon_nvo_fragments, + if ( has_eeprom ) { + nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, 0x100, 0xf0, &efab->netdev->refcnt ); + } return 0; } diff --git a/src/drivers/net/myri10ge.c b/src/drivers/net/myri10ge.c index cea2758f..bc730ea9 100644 --- a/src/drivers/net/myri10ge.c +++ b/src/drivers/net/myri10ge.c @@ -183,8 +183,8 @@ struct myri10ge_private */ struct nvs_device nvs; - struct nvo_fragment nvo_fragment[2]; struct nvo_block nvo; + unsigned int nvo_registered; /* Cached PCI capability locations. */ @@ -727,28 +727,20 @@ static int myri10ge_nv_init ( struct myri10ge_private *priv ) priv->nvs.read = myri10ge_nvs_read; priv->nvs.write = myri10ge_nvs_write; - /* Build the NonVolatile storage fragment list. We would like - to use the whole last EEPROM block for this, but we must - reduce the block size lest malloc fail in - src/core/nvo.o. */ - - priv->nvo_fragment[0].address = nvo_fragment_pos; - priv->nvo_fragment[0].len = 0x200; - /* Register the NonVolatile Options storage. */ nvo_init ( &priv->nvo, &priv->nvs, - priv->nvo_fragment, + nvo_fragment_pos, 0x200, & myri10ge_netdev (priv) -> refcnt ); rc = register_nvo ( &priv->nvo, netdev_settings ( myri10ge_netdev ( priv ) ) ); if ( rc ) { DBG ("register_nvo failed"); - priv->nvo_fragment[0].len = 0; return rc; } + priv->nvo_registered = 1; DBG2 ( "NVO supported\n" ); return 0; } @@ -758,7 +750,7 @@ myri10ge_nv_fini ( struct myri10ge_private *priv ) { /* Simply return if nonvolatile access is not supported. */ - if ( 0 == priv->nvo_fragment[0].len ) + if ( 0 == priv->nvo_registered ) return; unregister_nvo ( &priv->nvo ); diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c index 19c20edd..61073b59 100644 --- a/src/drivers/net/natsemi.c +++ b/src/drivers/net/natsemi.c @@ -130,15 +130,6 @@ static struct bit_basher_operations natsemi_basher_ops = { .write = natsemi_spi_write_bit, }; -/* It looks that this portion of EEPROM can be used for - * non-volatile stored options. Data sheet does not talk about this region. - * Currently it is not working. But with some efforts it can. - */ -static struct nvo_fragment natsemi_nvo_fragments[] = { - { 0x0c, 0x68 }, - { 0, 0 } -}; - /* * Set up for EEPROM access * @@ -157,8 +148,13 @@ static void natsemi_init_eeprom ( struct natsemi_private *np ) { */ init_at93c46 ( &np->eeprom, 16 ); np->eeprom.bus = &np->spibit.bus; - np->nvo.nvs = &np->eeprom.nvs; - np->nvo.fragments = natsemi_nvo_fragments; + + /* It looks that this portion of EEPROM can be used for + * non-volatile stored options. Data sheet does not talk about + * this region. Currently it is not working. But with some + * efforts it can. + */ + nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL ); } /** diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c index 1ce1344e..e97829f0 100644 --- a/src/drivers/net/rtl8139.c +++ b/src/drivers/net/rtl8139.c @@ -251,17 +251,6 @@ static struct bit_basher_operations rtl_basher_ops = { .write = rtl_spi_write_bit, }; -/** Portion of EEPROM available for non-volatile stored options - * - * We use offset 0x40 (i.e. address 0x20), length 0x40. This block is - * marked as VPD in the rtl8139 datasheets, so we use it only if we - * detect that the card is not supporting VPD. - */ -static struct nvo_fragment rtl_nvo_fragments[] = { - { 0x20, 0x40 }, - { 0, 0 } -}; - /** * Set up for EEPROM access * @@ -288,13 +277,18 @@ static void rtl_init_eeprom ( struct net_device *netdev ) { } rtl->eeprom.bus = &rtl->spibit.bus; - /* Initialise space for non-volatile options, if available */ + /* Initialise space for non-volatile options, if available + * + * We use offset 0x40 (i.e. address 0x20), length 0x40. This + * block is marked as VPD in the rtl8139 datasheets, so we use + * it only if we detect that the card is not supporting VPD. + */ vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable ); if ( vpd ) { DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use " "for options\n", rtl ); } else { - nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments, + nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, &netdev->refcnt ); } } diff --git a/src/include/ipxe/nvo.h b/src/include/ipxe/nvo.h index 0a0222b3..1fdc12cc 100644 --- a/src/include/ipxe/nvo.h +++ b/src/include/ipxe/nvo.h @@ -16,16 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct nvs_device; struct refcnt; -/** - * A fragment of a non-volatile storage device used for stored options - */ -struct nvo_fragment { - /** Starting address of fragment within NVS device */ - unsigned int address; - /** Length of fragment */ - size_t len; -}; - /** * A block of non-volatile stored options */ @@ -34,13 +24,10 @@ struct nvo_block { struct settings settings; /** Underlying non-volatile storage device */ struct nvs_device *nvs; - /** List of option-containing fragments - * - * The list is terminated by a fragment with a length of zero. - */ - struct nvo_fragment *fragments; - /** Total length of option-containing fragments */ - size_t total_len; + /** Address within NVS device */ + unsigned int address; + /** Length of options data */ + size_t len; /** Option-containing data */ void *data; /** DHCP options block */ @@ -48,7 +35,7 @@ struct nvo_block { }; extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - struct nvo_fragment *fragments, struct refcnt *refcnt ); + size_t address, size_t len, struct refcnt *refcnt ); extern int register_nvo ( struct nvo_block *nvo, struct settings *parent ); extern void unregister_nvo ( struct nvo_block *nvo ); From 17d28f48776b909d031bcb0435c852ade1bd8988 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Jan 2011 00:53:50 +0000 Subject: [PATCH 06/23] [nvo] Allow resizing of non-volatile stored option blocks Signed-off-by: Michael Brown --- src/core/nvo.c | 152 +++++++++++++++++++++++----------- src/drivers/net/etherfabric.c | 2 +- src/drivers/net/myri10ge.c | 1 + src/drivers/net/natsemi.c | 2 +- src/drivers/net/rtl8139.c | 2 +- src/include/ipxe/dhcpopts.h | 1 + src/include/ipxe/nvo.h | 12 ++- src/net/dhcpopts.c | 2 +- 8 files changed, 120 insertions(+), 54 deletions(-) diff --git a/src/core/nvo.c b/src/core/nvo.c index c5968c6b..f4da407a 100644 --- a/src/core/nvo.c +++ b/src/core/nvo.c @@ -49,6 +49,73 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { return sum; } +/** + * Reallocate non-volatile stored options block + * + * @v nvo Non-volatile options block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc ( struct nvo_block *nvo, size_t len ) { + void *new_data; + + /* Reallocate data */ + new_data = realloc ( nvo->data, len ); + if ( ! new_data ) { + DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", + nvo, len ); + return -ENOMEM; + } + nvo->data = new_data; + nvo->len = len; + + /* Update DHCP option block */ + if ( len ) { + nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ ); + nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ ); + } else { + nvo->dhcpopts.data = NULL; + nvo->dhcpopts.used_len = 0; + nvo->dhcpopts.alloc_len = 0; + } + + return 0; +} + +/** + * Reallocate non-volatile stored options DHCP option block + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) { + struct nvo_block *nvo = + container_of ( options, struct nvo_block, dhcpopts ); + int rc; + + /* Refuse to reallocate if we have no way to resize the block */ + if ( ! nvo->resize ) + return dhcpopt_no_realloc ( options, len ); + + /* Allow one byte for the checksum (if any data is present) */ + if ( len ) + len += 1; + + /* Resize underlying non-volatile options block */ + if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n", + nvo, len, strerror ( rc ) ); + return rc; + } + + /* Reallocate in-memory options block */ + if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 ) + return rc; + + return 0; +} + /** * Load non-volatile stored options from non-volatile storage device * @@ -56,8 +123,15 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { * @ret rc Return status code */ static int nvo_load ( struct nvo_block *nvo ) { + uint8_t *options_data = nvo->dhcpopts.data; int rc; + /* Skip reading zero-length NVO fields */ + if ( nvo->len == 0 ) { + DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo ); + return 0; + } + /* Read data */ if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data, nvo->len ) ) != 0 ) { @@ -66,6 +140,20 @@ static int nvo_load ( struct nvo_block *nvo ) { return rc; } + /* If checksum fails, or options data starts with a zero, + * assume the whole block is invalid. This should capture the + * case of random initial contents. + */ + if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { + DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " + "assuming empty\n", nvo, nvo_checksum ( nvo ), + options_data[0] ); + memset ( nvo->data, 0, nvo->len ); + } + + /* Rescan DHCP option block */ + dhcpopt_update_used_len ( &nvo->dhcpopts ); + DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); return 0; } @@ -80,8 +168,9 @@ static int nvo_save ( struct nvo_block *nvo ) { uint8_t *checksum = nvo->data; int rc; - /* Recalculate checksum */ - *checksum -= nvo_checksum ( nvo ); + /* Recalculate checksum, if applicable */ + if ( nvo->len > 0 ) + *checksum -= nvo_checksum ( nvo ); /* Write data */ if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data, @@ -95,38 +184,6 @@ static int nvo_save ( struct nvo_block *nvo ) { return 0; } -/** - * Parse stored options - * - * @v nvo Non-volatile options block - * - * Verifies that the options data is valid, and configures the DHCP - * options block. If the data is not valid, it is replaced with an - * empty options block. - */ -static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { - uint8_t *options_data; - size_t options_len; - - /* Steal one byte for the checksum */ - options_data = ( nvo->data + 1 ); - options_len = ( nvo->len - 1 ); - - /* If checksum fails, or options data starts with a zero, - * assume the whole block is invalid. This should capture the - * case of random initial contents. - */ - if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { - DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " - "assuming empty\n", nvo, nvo_checksum ( nvo ), - options_data[0] ); - memset ( nvo->data, 0, nvo->len ); - } - - dhcpopt_init ( &nvo->dhcpopts, options_data, options_len, - dhcpopt_no_realloc ); -} - /** * Store value of NVO setting * @@ -190,13 +247,18 @@ static struct settings_operations nvo_settings_operations = { * @v nvs Underlying non-volatile storage device * @v address Address within NVS device * @v len Length of non-volatile options data + * @v resize Resize method * @v refcnt Containing object reference counter, or NULL */ void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - size_t address, size_t len, struct refcnt *refcnt ) { + size_t address, size_t len, + int ( * resize ) ( struct nvo_block *nvo, size_t len ), + struct refcnt *refcnt ) { nvo->nvs = nvs; nvo->address = address; nvo->len = len; + nvo->resize = resize; + dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt ); settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 0 ); } @@ -211,20 +273,14 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { int rc; /* Allocate memory for options */ - nvo->data = zalloc ( nvo->len ); - if ( ! nvo->data ) { - DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", - nvo, nvo->len ); - rc = -ENOMEM; - goto err_malloc; - } + if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 ) + goto err_realloc; /* Read data from NVS */ if ( ( rc = nvo_load ( nvo ) ) != 0 ) goto err_load; - /* Verify and register options */ - nvo_init_dhcpopts ( nvo ); + /* Register settings */ if ( ( rc = register_settings ( &nvo->settings, parent, "nvo" ) ) != 0 ) goto err_register; @@ -233,9 +289,8 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { err_register: err_load: - free ( nvo->data ); - nvo->data = NULL; - err_malloc: + nvo_realloc ( nvo, 0 ); + err_realloc: return rc; } @@ -246,7 +301,6 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { */ void unregister_nvo ( struct nvo_block *nvo ) { unregister_settings ( &nvo->settings ); - free ( nvo->data ); - nvo->data = NULL; + nvo_realloc ( nvo, 0 ); DBGC ( nvo, "NVO %p unregistered\n", nvo ); } diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 77d21247..836b85a9 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -3273,7 +3273,7 @@ falcon_probe_spi ( struct efab_nic *efab ) /* If the device has EEPROM attached, then advertise NVO space */ if ( has_eeprom ) { nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, 0x100, 0xf0, - &efab->netdev->refcnt ); + NULL, &efab->netdev->refcnt ); } return 0; diff --git a/src/drivers/net/myri10ge.c b/src/drivers/net/myri10ge.c index bc730ea9..c7b9dfa2 100644 --- a/src/drivers/net/myri10ge.c +++ b/src/drivers/net/myri10ge.c @@ -732,6 +732,7 @@ static int myri10ge_nv_init ( struct myri10ge_private *priv ) nvo_init ( &priv->nvo, &priv->nvs, nvo_fragment_pos, 0x200, + NULL, & myri10ge_netdev (priv) -> refcnt ); rc = register_nvo ( &priv->nvo, netdev_settings ( myri10ge_netdev ( priv ) ) ); diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c index 61073b59..da2f0886 100644 --- a/src/drivers/net/natsemi.c +++ b/src/drivers/net/natsemi.c @@ -154,7 +154,7 @@ static void natsemi_init_eeprom ( struct natsemi_private *np ) { * this region. Currently it is not working. But with some * efforts it can. */ - nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL ); + nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL ); } /** diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c index e97829f0..7cc1de2f 100644 --- a/src/drivers/net/rtl8139.c +++ b/src/drivers/net/rtl8139.c @@ -288,7 +288,7 @@ static void rtl_init_eeprom ( struct net_device *netdev ) { DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use " "for options\n", rtl ); } else { - nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, + nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, NULL, &netdev->refcnt ); } } diff --git a/src/include/ipxe/dhcpopts.h b/src/include/ipxe/dhcpopts.h index fe07d903..8fb3d2d7 100644 --- a/src/include/ipxe/dhcpopts.h +++ b/src/include/ipxe/dhcpopts.h @@ -36,6 +36,7 @@ extern void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len, int ( * realloc ) ( struct dhcp_options *options, size_t len ) ); +extern void dhcpopt_update_used_len ( struct dhcp_options *options ); extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ); #endif /* _IPXE_DHCPOPTS_H */ diff --git a/src/include/ipxe/nvo.h b/src/include/ipxe/nvo.h index 1fdc12cc..995afd74 100644 --- a/src/include/ipxe/nvo.h +++ b/src/include/ipxe/nvo.h @@ -30,12 +30,22 @@ struct nvo_block { size_t len; /** Option-containing data */ void *data; + /** + * Resize non-volatile stored option block + * + * @v nvo Non-volatile options block + * @v len New size + * @ret rc Return status code + */ + int ( * resize ) ( struct nvo_block *nvo, size_t len ); /** DHCP options block */ struct dhcp_options dhcpopts; }; extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - size_t address, size_t len, struct refcnt *refcnt ); + size_t address, size_t len, + int ( * resize ) ( struct nvo_block *nvo, size_t len ), + struct refcnt *refcnt ); extern int register_nvo ( struct nvo_block *nvo, struct settings *parent ); extern void unregister_nvo ( struct nvo_block *nvo ); diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c index d1330eae..f04b8e71 100644 --- a/src/net/dhcpopts.c +++ b/src/net/dhcpopts.c @@ -402,7 +402,7 @@ int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, * The "used length" field will be updated based on scanning through * the block to find the end of the options. */ -static void dhcpopt_update_used_len ( struct dhcp_options *options ) { +void dhcpopt_update_used_len ( struct dhcp_options *options ) { struct dhcp_option *option; int offset = 0; ssize_t remaining = options->alloc_len; From 267ef3179177cc345e5c1a550cd187a6e5cc8e69 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Jan 2011 01:37:27 +0000 Subject: [PATCH 07/23] [pci] Allow pci_vpd_init() return status to be ignored Most xxx_init() functions are void functions with no failure cases. Allow pci_vpd_init() to be used in the same way. (Subsequent calls to pci_vpd_read() etc. will fail if pci_vpd_init() fails.) Signed-off-by: Michael Brown --- src/drivers/bus/pcivpd.c | 8 ++++++++ src/include/ipxe/pcivpd.h | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/drivers/bus/pcivpd.c b/src/drivers/bus/pcivpd.c index 1f7d832b..1ee6c641 100644 --- a/src/drivers/bus/pcivpd.c +++ b/src/drivers/bus/pcivpd.c @@ -73,6 +73,10 @@ static int pci_vpd_read_dword ( struct pci_vpd *vpd, int address, unsigned int retries; uint16_t flag; + /* Fail if no VPD present */ + if ( ! cap ) + return -ENOTTY; + /* Return cached value, if present */ if ( pci_vpd_cache_is_valid ( vpd ) && ( vpd->cache.address == address ) ) { @@ -127,6 +131,10 @@ static int pci_vpd_write_dword ( struct pci_vpd *vpd, int address, unsigned int retries; uint16_t flag; + /* Fail if no VPD present */ + if ( ! cap ) + return -ENOTTY; + /* Invalidate cache */ pci_vpd_invalidate_cache ( vpd ); diff --git a/src/include/ipxe/pcivpd.h b/src/include/ipxe/pcivpd.h index 8b973ea1..469ec29f 100644 --- a/src/include/ipxe/pcivpd.h +++ b/src/include/ipxe/pcivpd.h @@ -133,6 +133,17 @@ struct pci_vpd { struct pci_vpd_cache cache; }; +/** + * Check for presence of PCI VPD + * + * @v vpd PCI VPD + * @ret is_present VPD is present + */ +static inline __attribute__ (( always_inline )) int +pci_vpd_is_present ( struct pci_vpd *vpd ) { + return ( vpd->cap != 0 ); +} + /** * Check if PCI VPD read cache is valid * From e67c79b8560fbe50e1dad23149e8b90dc5358b56 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Jan 2011 03:34:34 +0000 Subject: [PATCH 08/23] [pci] Add ability to resize a VPD field Signed-off-by: Michael Brown --- src/drivers/bus/pcivpd.c | 161 ++++++++++++++++++++++++++++++++++++++ src/include/ipxe/pcivpd.h | 5 ++ 2 files changed, 166 insertions(+) diff --git a/src/drivers/bus/pcivpd.c b/src/drivers/bus/pcivpd.c index 1ee6c641..15cf9059 100644 --- a/src/drivers/bus/pcivpd.c +++ b/src/drivers/bus/pcivpd.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -392,3 +393,163 @@ int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field, PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ) ); return -ENOENT; } + +/** + * Resize VPD field + * + * @v vpd PCI VPD + * @v field VPD field descriptor + * @v len New length of field body + * @ret address Address of field body + * @ret rc Return status code + */ +int pci_vpd_resize ( struct pci_vpd *vpd, unsigned int field, size_t len, + unsigned int *address ) { + struct pci_vpd_field rw_field; + struct pci_vpd_field old_field; + struct pci_vpd_field new_field; + unsigned int rw_address; + unsigned int old_address; + unsigned int copy_address; + unsigned int dst_address; + unsigned int dump_address; + size_t rw_len; + size_t old_len; + size_t available_len; + size_t copy_len; + size_t dump_len; + void *copy; + int rc; + + /* Sanity checks */ + assert ( PCI_VPD_TAG ( field ) == PCI_VPD_TAG_RW ); + assert ( PCI_VPD_KEYWORD ( field ) != 0 ); + assert ( field != PCI_VPD_FIELD_RW ); + + /* Locate 'RW' field */ + if ( ( rc = pci_vpd_find ( vpd, PCI_VPD_FIELD_RW, &rw_address, + &rw_len ) ) != 0 ) + goto err_no_rw; + + /* Locate old field, if any */ + if ( ( rc = pci_vpd_find ( vpd, field, &old_address, + &old_len ) ) == 0 ) { + + /* Field already exists */ + if ( old_address > rw_address ) { + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT + " at [%04x,%04zx) is after field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), + old_address, ( old_address + old_len ), + PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), + rw_address, ( rw_address + rw_len ) ); + rc = -ENXIO; + goto err_after_rw; + } + dst_address = ( old_address - sizeof ( old_field ) ); + copy_address = ( old_address + old_len ); + copy_len = ( rw_address - sizeof ( rw_field ) - copy_address ); + + /* Calculate available length */ + available_len = ( rw_len + old_len ); + + } else { + + /* Field does not yet exist */ + dst_address = ( rw_address - sizeof ( rw_field ) ); + copy_address = dst_address; + copy_len = 0; + + /* Calculate available length */ + available_len = ( ( rw_len > sizeof ( new_field ) ) ? + ( rw_len - sizeof ( new_field ) ) : 0 ); + } + + /* Dump region before changes */ + dump_address = dst_address; + dump_len = ( rw_address + rw_len - dump_address ); + DBGC ( vpd, PCI_FMT " VPD before resizing field " PCI_VPD_FIELD_FMT + " to %zd bytes:\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), len ); + pci_vpd_dump ( vpd, dump_address, dump_len ); + + /* Check available length */ + if ( available_len > PCI_VPD_MAX_LEN ) + available_len = PCI_VPD_MAX_LEN; + if ( len > available_len ) { + DBGC ( vpd, PCI_FMT " VPD no space for field " + PCI_VPD_FIELD_FMT " (need %02zx, have %02zx)\n", + PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ), + len, available_len ); + rc = -ENOSPC; + goto err_no_space; + } + + /* Preserve intermediate fields, if any */ + copy = malloc ( copy_len ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_copy_alloc; + } + if ( ( rc = pci_vpd_read ( vpd, copy_address, copy, copy_len ) ) != 0 ) + goto err_copy_read; + + /* Create new field, if applicable */ + if ( len ) { + new_field.keyword = PCI_VPD_KEYWORD ( field ); + new_field.len = len; + if ( ( rc = pci_vpd_write ( vpd, dst_address, &new_field, + sizeof ( new_field ) ) ) != 0 ) + goto err_new_write; + dst_address += sizeof ( new_field ); + *address = dst_address; + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now " + "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), dst_address, + ( dst_address + new_field.len ) ); + dst_address += len; + } else { + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT + " no longer exists\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ) ); + } + + /* Restore intermediate fields, if any */ + if ( ( rc = pci_vpd_write ( vpd, dst_address, copy, copy_len ) ) != 0 ) + goto err_copy_write; + dst_address += copy_len; + + /* Create 'RW' field */ + rw_field.keyword = PCI_VPD_KEYWORD ( PCI_VPD_FIELD_RW ); + rw_field.len = ( rw_len + + ( rw_address - sizeof ( rw_field ) ) - dst_address ); + if ( ( rc = pci_vpd_write ( vpd, dst_address, &rw_field, + sizeof ( rw_field ) ) ) != 0 ) + goto err_rw_write; + dst_address += sizeof ( rw_field ); + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now " + "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), dst_address, + ( dst_address + rw_field.len ) ); + + /* Dump region after changes */ + DBGC ( vpd, PCI_FMT " VPD after resizing field " PCI_VPD_FIELD_FMT + " to %zd bytes:\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), len ); + pci_vpd_dump ( vpd, dump_address, dump_len ); + + rc = 0; + + err_rw_write: + err_new_write: + err_copy_write: + err_copy_read: + free ( copy ); + err_copy_alloc: + err_no_space: + err_after_rw: + err_no_rw: + return rc; +} diff --git a/src/include/ipxe/pcivpd.h b/src/include/ipxe/pcivpd.h index 469ec29f..0abf8a95 100644 --- a/src/include/ipxe/pcivpd.h +++ b/src/include/ipxe/pcivpd.h @@ -32,6 +32,9 @@ struct pci_vpd_field { uint8_t len; } __attribute__ (( packed )); +/** Maximum PCI VPD field length */ +#define PCI_VPD_MAX_LEN 0xff + /** Construct PCI VPD field descriptor * * @v tag ISAPnP tag @@ -172,5 +175,7 @@ extern int pci_vpd_write ( struct pci_vpd *vpd, unsigned int address, const void *buf, size_t len ); extern int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field, unsigned int *address, size_t *len ); +extern int pci_vpd_resize ( struct pci_vpd *vpd, unsigned int field, + size_t len, unsigned int *address ); #endif /* _IPXE_PCIVPD_H */ From 7bf37147b31c8b8af6178b094f0c02069f13e152 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 10 Jan 2011 03:35:34 +0000 Subject: [PATCH 09/23] [pci] Auto-resize VPD fields used for non-volatile storage Signed-off-by: Michael Brown --- src/drivers/nvs/nvsvpd.c | 174 ++++++++++++++++++++++++++++++------- src/include/ipxe/errfile.h | 1 + src/include/ipxe/nvsvpd.h | 16 ++-- 3 files changed, 151 insertions(+), 40 deletions(-) diff --git a/src/drivers/nvs/nvsvpd.c b/src/drivers/nvs/nvsvpd.c index b53829e8..a22ec825 100644 --- a/src/drivers/nvs/nvsvpd.c +++ b/src/drivers/nvs/nvsvpd.c @@ -19,9 +19,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include +#include #include /** @file @@ -31,24 +33,50 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Read from VPD + * Read from VPD field * * @v nvs NVS device - * @v address Starting address - * @v buf Data buffer + * @v field VPD field descriptor + * @v data Data buffer * @v len Length of data buffer * @ret rc Return status code */ -static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address, +static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int field, void *data, size_t len ) { struct nvs_vpd_device *nvsvpd = container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t max_len; int rc; - if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, ( nvsvpd->address + address ), - data, len ) ) != 0 ) { - DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not read " - "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ), + /* Allow reading non-existent field */ + if ( len == 0 ) + return 0; + + /* Locate VPD field */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &max_len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not locate field " + PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), + PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len > max_len ) { + DBGC ( pci, PCI_FMT " NVS VPD cannot read %#02zx bytes " + "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ), + address, ( address + max_len ) ); + return -ENXIO; + } + + /* Read from VPD field */ + if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, address, data, len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not read field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), address, ( address + len ), strerror ( rc ) ); return rc; } @@ -57,24 +85,47 @@ static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address, } /** - * Write to VPD + * Write to VPD field * * @v nvs NVS device - * @v address Starting address - * @v buf Data buffer + * @v field VPD field descriptor + * @v data Data buffer * @v len Length of data buffer * @ret rc Return status code */ -static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, +static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int field, const void *data, size_t len ) { struct nvs_vpd_device *nvsvpd = container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t max_len; int rc; - if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, ( nvsvpd->address + address ), - data, len ) ) != 0 ) { - DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not write " - "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ), + /* Locate VPD field */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &max_len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not locate field " + PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), + PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len > max_len ) { + DBGC ( pci, PCI_FMT " NVS VPD cannot write %#02zx bytes " + "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ), + address, ( address + max_len ) ); + return -ENXIO; + } + + /* Write field */ + if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, address, data, + len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not write field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), address, ( address + len ), strerror ( rc ) ); return rc; } @@ -82,6 +133,36 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, return 0; } +/** + * Resize VPD field + * + * @v nvs NVS device + * @v field VPD field descriptor + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int nvs_vpd_resize ( struct nvs_device *nvs, unsigned int field, + size_t len ) { + struct nvs_vpd_device *nvsvpd = + container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + int rc; + + /* Resize field */ + if ( ( rc = pci_vpd_resize ( &nvsvpd->vpd, field, len, + &address ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not resize field " + PCI_VPD_FIELD_FMT " to %#02zx bytes: %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), + len, strerror ( rc ) ); + return rc; + } + + return 0; +} + /** * Initialise NVS VPD device * @@ -89,9 +170,7 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, * @v pci PCI device * @ret rc Return status code */ -int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, - unsigned int field ) { - size_t len; +int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci ) { int rc; /* Initialise VPD device */ @@ -101,23 +180,54 @@ int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, return rc; } - /* Locate VPD field */ - if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &nvsvpd->address, - &len ) ) != 0 ) { - DBGC ( pci, PCI_FMT " NVS could not locate VPD field " - PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), - PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); - return rc; - } - /* Initialise NVS device */ - nvsvpd->nvs.size = len; nvsvpd->nvs.read = nvs_vpd_read; nvsvpd->nvs.write = nvs_vpd_write; - DBGC ( pci, PCI_FMT " NVS using VPD field " PCI_VPD_FIELD_FMT " at " - "[%04x,%04x)\n", PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), - nvsvpd->address, ( nvsvpd->address + nvsvpd->nvs.size ) ); + return 0; +} + +/** + * Resize non-volatile option storage within NVS VPD device + * + * @v nvo Non-volatile options block + * @v len New length + * @ret rc Return status code + */ +static int nvs_vpd_nvo_resize ( struct nvo_block *nvo, size_t len ) { + int rc; + + /* Resize VPD field */ + if ( ( rc = nvs_vpd_resize ( nvo->nvs, nvo->address, len ) ) != 0 ) + return rc; return 0; } + +/** + * Initialise non-volatile option storage within NVS VPD device + * + * @v nvsvpd NVS VPD device + * @v field VPD field descriptor + * @v nvo Non-volatile options block + * @v refcnt Containing object reference counter, or NULL + */ +void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, unsigned int field, + struct nvo_block *nvo, struct refcnt *refcnt ) { + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t len; + int rc; + + /* Locate VPD field, if present */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD field " PCI_VPD_FIELD_FMT + " not present; assuming empty\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ) ); + len = 0; + } + + /* Initialise non-volatile options block */ + nvo_init ( nvo, &nvsvpd->nvs, field, len, nvs_vpd_nvo_resize, refcnt ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2d0422e4..f3a8efb0 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -73,6 +73,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 ) #define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 ) #define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 ) +#define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 ) #define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) #define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) diff --git a/src/include/ipxe/nvsvpd.h b/src/include/ipxe/nvsvpd.h index 5f80844e..3450e5c7 100644 --- a/src/include/ipxe/nvsvpd.h +++ b/src/include/ipxe/nvsvpd.h @@ -13,21 +13,21 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +struct nvo_block; +struct refcnt; + /** An NVS VPD device */ struct nvs_vpd_device { /** NVS device */ struct nvs_device nvs; /** PCI VPD device */ struct pci_vpd vpd; - /** Starting address - * - * This address is added to the NVS address to form the VPD - * address. - */ - unsigned int address; }; -extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, - unsigned int field ); +extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, + struct pci_device *pci ); +extern void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, + unsigned int field, struct nvo_block *nvo, + struct refcnt *refcnt ); #endif /* IPXE_NVSVPD_H */ From 66caec3f005f3ae2a9cd3d131af710e60d246182 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Jan 2011 14:16:11 +0000 Subject: [PATCH 10/23] [netdevice] Allow devices to indicate that interrupts are not supported Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 14 ++++++++++++++ src/net/netdevice.c | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index a7852a81..ac7cec52 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -236,6 +236,9 @@ struct net_device_operations { * * @v netdev Network device * @v enable Interrupts should be enabled + * + * This method may be NULL to indicate that interrupts are not + * supported. */ void ( * irq ) ( struct net_device *netdev, int enable ); }; @@ -515,6 +518,17 @@ netdev_is_open ( struct net_device *netdev ) { return ( netdev->state & NETDEV_OPEN ); } +/** + * Check whether or not network device supports interrupts + * + * @v netdev Network device + * @ret irq_supported Network device supports interrupts + */ +static inline __attribute__ (( always_inline )) int +netdev_irq_supported ( struct net_device *netdev ) { + return ( netdev->op->irq != NULL ); +} + /** * Check whether or not network device interrupts are currently enabled * diff --git a/src/net/netdevice.c b/src/net/netdevice.c index c7e907ad..81382376 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -542,6 +542,10 @@ void unregister_netdev ( struct net_device *netdev ) { */ void netdev_irq ( struct net_device *netdev, int enable ) { + /* Do nothing if device does not support interrupts */ + if ( ! netdev_irq_supported ( netdev ) ) + return; + /* Enable or disable device interrupts */ netdev->op->irq ( netdev, enable ); From 025b1cb3f8d37ee84902ab268bc639a795f7c467 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Jan 2011 14:18:15 +0000 Subject: [PATCH 11/23] [pxe] Report SUPPORTED_IRQ only if device supports interrupts Signed-off-by: Michael Brown --- src/arch/i386/interface/pxe/pxe_undi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/arch/i386/interface/pxe/pxe_undi.c b/src/arch/i386/interface/pxe/pxe_undi.c index eb54417c..afbc1799 100644 --- a/src/arch/i386/interface/pxe/pxe_undi.c +++ b/src/arch/i386/interface/pxe/pxe_undi.c @@ -615,7 +615,9 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info->ServiceFlags = ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST | SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET | - SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ ); + SUPPORTED_OPEN_CLOSE ); + if ( netdev_irq_supported ( pxe_netdev ) ) + undi_get_iface_info->ServiceFlags |= SUPPORTED_IRQ; memset ( undi_get_iface_info->Reserved, 0, sizeof(undi_get_iface_info->Reserved) ); From 3a9f0e0f84d91b8b788d98a041d0da96c63b568f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Jan 2011 14:19:54 +0000 Subject: [PATCH 12/23] [infiniband] Indicate that device does not support interrupts Signed-off-by: Michael Brown --- src/drivers/net/ipoib.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 09c89bdc..4917b58e 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -537,17 +537,6 @@ static void ipoib_poll ( struct net_device *netdev ) { ib_poll_eq ( ibdev ); } -/** - * Enable/disable interrupts on IPoIB network device - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void ipoib_irq ( struct net_device *netdev __unused, - int enable __unused ) { - /* No implementation */ -} - /** * Handle IPv4 broadcast multicast group join completion * @@ -730,7 +719,6 @@ static struct net_device_operations ipoib_operations = { .close = ipoib_close, .transmit = ipoib_transmit, .poll = ipoib_poll, - .irq = ipoib_irq, }; /** From 785335996f5d015c5559d76c57a2986457c71bc7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Jan 2011 14:20:26 +0000 Subject: [PATCH 13/23] [hermon] Indicate that device does not support interrupts Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 63cf7d8c..6e3955af 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2319,17 +2319,6 @@ static void hermon_eth_poll ( struct net_device *netdev ) { ib_poll_eq ( ibdev ); } -/** - * Enable/disable interrupts on Hermon Ethernet device - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void hermon_eth_irq ( struct net_device *netdev __unused, - int enable __unused ) { - /* No implementation */ -} - /** * Open Hermon Ethernet device * @@ -2469,7 +2458,6 @@ static struct net_device_operations hermon_eth_operations = { .close = hermon_eth_close, .transmit = hermon_eth_transmit, .poll = hermon_eth_poll, - .irq = hermon_eth_irq, }; /** From 21d6f9cd8ee16e3c10f9d7a781fae62b8977f474 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Jan 2011 23:24:23 +0000 Subject: [PATCH 14/23] [contrib] Specify boot order in bochsrc.txt Signed-off-by: Michael Brown --- contrib/vm/bochsrc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/vm/bochsrc.txt b/contrib/vm/bochsrc.txt index 9bacd741..32842a3b 100644 --- a/contrib/vm/bochsrc.txt +++ b/contrib/vm/bochsrc.txt @@ -300,6 +300,7 @@ ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 #======================================================================= #boot: floppy #boot: disk +boot: network, floppy #======================================================================= # CLOCK: From a84e15234a86cebe40d7454fb7e3ed226443e2e0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Jan 2011 23:04:43 +0000 Subject: [PATCH 15/23] [prefix] Use 16-bit protected mode for access to high memory Flat real mode works perfectly on real hardware, but seems to cause problems for some hypervisors. Revert to using 16-bit protected mode (and returning to real mode with 4GB limits, so as not to break PMM BIOSes). Allow the code specific to the .mrom format to continue to assume that flat real mode works, since this format is specific to real hardware. Signed-off-by: Michael Brown --- src/arch/i386/prefix/libprefix.S | 232 ++++++++++++++++++++++++------ src/arch/i386/prefix/mromprefix.S | 7 +- 2 files changed, 196 insertions(+), 43 deletions(-) diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index 340e74be..c8fbe95b 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER ) /* Image compression enabled */ #define COMPRESS 1 +/* Protected mode flag */ +#define CR0_PE 1 + /***************************************************************************** * Utility function: print character (with LF -> LF,CR translation) * @@ -229,16 +232,186 @@ print_kill_line: * None **************************************************************************** */ -#if ! COMPRESS .section ".prefix.lib", "awx", @progbits .code16 copy_bytes: - pushl %ecx + pushl %ecx rep addr32 movsb - popl %ecx + popl %ecx ret - .size copy_bytes, . - copy_bytes -#endif /* COMPRESS */ + .size copy_bytes, . - copy_bytes + +/**************************************************************************** + * zero_bytes + * + * Zero bytes + * + * Parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * Returns: + * %ds:esi : next source address + * %es:edi : next destination address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +zero_bytes: + pushl %ecx + pushw %ax + xorw %ax, %ax + rep addr32 stosb + popw %ax + popl %ecx + ret + .size zero_bytes, . - zero_bytes + +/**************************************************************************** + * process_bytes + * + * Call memcpy()-like function + * + * Parameters: + * %esi : source physical address + * %edi : destination physical address + * %ecx : length + * %bx : memcpy()-like function to call, passing parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * and returning: + * %ds:esi : next source address + * %es:edi : next destination address + * Returns: + * %esi : next source physical address + * %edi : next destination physical address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +process_bytes: + +#ifndef KEEP_IT_REAL + + /* Preserve registers */ + pushfw + pushl %eax + pushl %ebp + + /* Construct GDT on stack (since .prefix may not be writable) */ + .equ PM_DS, 0x18 /* Flat data segment */ + pushl $0x008f9300 + pushl $0x0000ffff + .equ PM_SS, 0x10 /* Stack segment based at %ss:0000 */ + pushl $0x008f0930 + pushw %ss + pushw $0xffff + .equ PM_CS, 0x08 /* Code segment based at %cs:0000 */ + pushl $0x008f09b0 + pushw %cs + pushw $0xffff + pushl $0 /* Base and length */ + pushw %ss + pushw $0x1f + movzwl %sp, %ebp + shll $4, 0x02(%bp) + addl %ebp, 0x02(%bp) + shll $4, 0x0a(%bp) + shll $4, 0x12(%bp) + subw $8, %sp + sgdt -8(%bp) + + /* Switch to protected mode */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + pushw $2f + cli + data32 lgdt (%bp) + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + ljmp $PM_CS, $1f +1: movw $PM_SS, %ax + movw %ax, %ss + movw $PM_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Call memcpy()-like function */ + call *%bx + + /* Return to (flat) real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + lret +2: /* lret will ljmp to here */ + popw %ss + popw %ds + popw %es + popw %fs + popw %gs + + /* Restore GDT */ + data32 lgdt -8(%bp) + addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp + + /* Restore registers and return */ + popl %ebp + popl %eax + popfw + ret + +#else /* KEEP_IT_REAL */ + + /* Preserve registers */ + pushl %eax + pushw %ds + pushw %es + + /* Convert %esi and %edi to %ds:esi and %es:edi */ + shrl $4, %esi + movw %si, %ds + xorw %si, %si + shll $4, %esi + shrl $4, %edi + movw %di, %es + xorw %di, %di + shll $4, %edi + + /* Call memcpy()-like function */ + call *%bx + + /* Convert %ds:esi and %es:edi back to physical addresses */ + xorl %eax, %eax + movw %ds, %cx + shll $4, %eax + addl %eax, %esi + xorl %eax, %eax + movw %es, %cx + shll $4, %eax + addl %eax, %edi + + /* Restore registers and return */ + popw %es + popw %ds + popl %eax + ret + +#endif /* KEEP_IT_REAL */ + + .size process_bytes, . - process_bytes /**************************************************************************** * install_block @@ -261,35 +434,22 @@ copy_bytes: .code16 install_block: /* Preserve registers */ - pushw %ds - pushw %es pushl %ecx - - /* Convert %esi and %edi to %ds:esi and %es:edi */ - shrl $4, %esi - movw %si, %ds - xorw %si, %si - shll $4, %esi - shrl $4, %edi - movw %di, %es - xorw %di, %di - shll $4, %edi + pushw %bx + /* Decompress (or copy) source to destination */ #if COMPRESS - /* Decompress source to destination */ - call decompress16 + movw $decompress16, %bx #else - /* Copy source to destination */ - call copy_bytes + movw $copy_bytes, %bx #endif + call process_bytes /* Zero .bss portion */ negl %ecx addl %edx, %ecx - pushw %ax - xorw %ax, %ax - rep addr32 stosb - popw %ax + movw $zero_bytes, %bx + call process_bytes /* Round up %esi and %edi to start of next blocks */ addl $0xf, %esi @@ -297,20 +457,9 @@ install_block: addl $0xf, %edi andl $~0xf, %edi - /* Convert %ds:esi and %es:edi back to physical addresses */ - xorl %ecx, %ecx - movw %ds, %cx - shll $4, %ecx - addl %ecx, %esi - xorl %ecx, %ecx - movw %es, %cx - shll $4, %ecx - addl %ecx, %edi - /* Restore registers and return */ + popw %bx popl %ecx - popw %es - popw %ds ret .size install_block, . - install_block @@ -612,11 +761,10 @@ payload_death_message: /* Copy code to new location */ pushl %edi - pushw %ax - xorw %ax, %ax - movw %ax, %es - es rep addr32 movsb - popw %ax + pushw %bx + movw $copy_bytes, %bx + call process_bytes + popw %bx popl %edi /* Initialise librm at new location */ diff --git a/src/arch/i386/prefix/mromprefix.S b/src/arch/i386/prefix/mromprefix.S index 989cea1e..3e177d00 100644 --- a/src/arch/i386/prefix/mromprefix.S +++ b/src/arch/i386/prefix/mromprefix.S @@ -151,7 +151,12 @@ find_mem_bar: /* Copy payload to buffer, or set buffer address to BAR address */ testl %esi, %esi jz 1f - /* We have a buffer; copy payload to it */ + /* We have a buffer; copy payload to it. Since .mrom is + * designed specifically for real hardware, we assume that + * flat real mode is working properly. (In the unlikely event + * that this code is run inside a hypervisor that doesn't + * properly support flat real mode, it will die horribly.) + */ pushl %esi pushw %es movl %esi, %edi From 7f2d0f12addc76543c383dfaa8e9fc46533aa427 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Jan 2011 23:20:48 +0000 Subject: [PATCH 16/23] [libflat] Remove now-obsolete flatten_real_mode call Flat real mode will have been set up as a side-effect of the protected-mode call invoked during install_block() for .text16.early; there is no need to do so explicitly. Signed-off-by: Michael Brown --- src/arch/i386/prefix/libprefix.S | 5 +- src/arch/i386/transitions/libflat.S | 122 +--------------------------- 2 files changed, 5 insertions(+), 122 deletions(-) diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index c8fbe95b..f95a7a1f 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -649,7 +649,10 @@ install_prealloc: popl %esi #ifndef KEEP_IT_REAL - /* Access high memory */ + /* Access high memory by enabling the A20 gate. (We will + * already have 4GB segment limits as a result of calling + * install_block.) + */ pushw %cs pushw $1f pushw %ax diff --git a/src/arch/i386/transitions/libflat.S b/src/arch/i386/transitions/libflat.S index 98f6f4df..594d6201 100644 --- a/src/arch/i386/transitions/libflat.S +++ b/src/arch/i386/transitions/libflat.S @@ -21,124 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER ) .arch i386 -#define CR0_PE 1 - -/**************************************************************************** - * flatten_real_mode - * - * Set up 4GB segment limits - * - * Parameters: - * none - * Returns: - * none - * Corrupts: - * none - **************************************************************************** - */ - /* GDT for protected-mode calls */ - .section ".text16.early.data", "aw", @progbits - .align 16 -flatten_gdt: -flatten_gdt_limit: .word flatten_gdt_length - 1 -flatten_gdt_base: .long 0 - .word 0 /* padding */ -flatten_cs: /* 16-bit protected-mode flat code segment */ - .equ FLAT_CS, flatten_cs - flatten_gdt - .word 0xffff, 0 - .byte 0, 0x9b, 0x8f, 0 -flatten_ss: /* 16-bit protected-mode flat stack segment */ - .equ FLAT_SS, flatten_ss - flatten_gdt - .word 0xffff, 0 - .byte 0, 0x93, 0x8f, 0 -flatten_gdt_end: - .equ flatten_gdt_length, . - flatten_gdt - .size flatten_gdt, . - flatten_gdt - - .section ".text16.early.data", "aw", @progbits - .align 16 -flatten_saved_gdt: - .long 0, 0 - .size flatten_saved_gdt, . - flatten_saved_gdt - - .section ".text16.early", "awx", @progbits - .code16 -flatten_real_mode: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushw %si - pushw %gs - pushw %fs - pushw %es - pushw %ds - pushw %ss - - /* Set %ds for access to .text16.early.data variables */ - pushw %cs - popw %ds - - /* Preserve original GDT */ - sgdt flatten_saved_gdt - - /* Set up GDT bases */ - xorl %eax, %eax - movw %cs, %ax - shll $4, %eax - addl $flatten_gdt, %eax - movl %eax, flatten_gdt_base - movw %cs, %ax - movw $flatten_cs, %si - call set_seg_base - movw %ss, %ax - movw $flatten_ss, %si - call set_seg_base - - /* Switch temporarily to protected mode and set segment registers */ - pushw %cs - pushw $2f - cli - data32 lgdt flatten_gdt - movl %cr0, %eax - orb $CR0_PE, %al - movl %eax, %cr0 - ljmp $FLAT_CS, $1f -1: movw $FLAT_SS, %ax - movw %ax, %ss - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movl %cr0, %eax - andb $0!CR0_PE, %al - movl %eax, %cr0 - lret -2: /* lret will ljmp to here */ - - /* Restore GDT, registers and flags */ - data32 lgdt flatten_saved_gdt - popw %ss - popw %ds - popw %es - popw %fs - popw %gs - popw %si - popl %eax - popfl - ret - .size flatten_real_mode, . - flatten_real_mode - - .section ".text16.early", "awx", @progbits - .code16 -set_seg_base: - rolw $4, %ax - movw %ax, 2(%si) - andw $0xfff0, 2(%si) - movb %al, 4(%si) - andb $0x0f, 4(%si) - ret - .size set_seg_base, . - set_seg_base - /**************************************************************************** * test_a20_short, test_a20_long * @@ -403,7 +285,7 @@ enable_a20_method: /**************************************************************************** * access_highmem (real mode far call) * - * Open up access to high memory in flat real mode with A20 enabled + * Open up access to high memory with A20 enabled * * Parameters: * none @@ -419,7 +301,5 @@ enable_a20_method: access_highmem: /* Enable A20 line */ call enable_a20 - /* Set up 4GB limits */ - call flatten_real_mode lret .size access_highmem, . - access_highmem From 228824194229fd9f3d84c6b1da9aaca58548718b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 26 Jan 2011 23:23:07 +0000 Subject: [PATCH 17/23] [liba20] Rename libflat to liba20 libflat no longer has anything to do with flat real mode; it handles only the A20 gate. Update library name to match. Signed-off-by: Michael Brown --- src/arch/i386/transitions/{libflat.S => liba20.S} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/arch/i386/transitions/{libflat.S => liba20.S} (100%) diff --git a/src/arch/i386/transitions/libflat.S b/src/arch/i386/transitions/liba20.S similarity index 100% rename from src/arch/i386/transitions/libflat.S rename to src/arch/i386/transitions/liba20.S From aa69bf84d22e2fb136a1a2bc467980c646a58fe3 Mon Sep 17 00:00:00 2001 From: Masroor Vettuparambil Date: Wed, 19 Jan 2011 22:34:08 -0800 Subject: [PATCH 18/23] [vxge] Add support for new function mode "multi-function 8 Direct IO" Support a new function mode "multi-function 8 Direct IO" which is used in ESX Direct I/O configuration. Update driver version to 3.5.0.1 Signed-off-by: Masroor Vettuparambil Signed-off-by: Sivakumar Subramani Signed-off-by: Michael Brown --- src/drivers/net/vxge/vxge_config.c | 63 ++++++++++++++++++++++------- src/drivers/net/vxge/vxge_config.h | 10 ++--- src/drivers/net/vxge/vxge_main.c | 3 +- src/drivers/net/vxge/vxge_version.h | 4 +- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/drivers/net/vxge/vxge_config.c b/src/drivers/net/vxge/vxge_config.c index bfcd533b..ba62b508 100644 --- a/src/drivers/net/vxge/vxge_config.c +++ b/src/drivers/net/vxge/vxge_config.c @@ -17,6 +17,7 @@ FILE_LICENCE(GPL2_ONLY); #include #include #include +#include #include #include #include @@ -186,28 +187,47 @@ __vxge_hw_device_vpath_reset_in_prog_check(u64 __iomem *vpath_rst_in_prog) return status; } +/* + * __vxge_hw_device_get_legacy_reg + * This routine gets the legacy register section's memory mapped address + * and sets the swapper. + */ +static struct vxge_hw_legacy_reg __iomem * +__vxge_hw_device_get_legacy_reg(struct pci_device *pdev, void __iomem *bar0) +{ + enum vxge_hw_status status; + struct vxge_hw_legacy_reg __iomem *legacy_reg; + /* + * If the length of Bar0 is 16MB, then assume that we are configured + * in MF8P_VP2 mode and then add 8MB to the legacy_reg offsets + */ + if (pci_bar_size(pdev, PCI_BASE_ADDRESS_0) == 0x1000000) + legacy_reg = (struct vxge_hw_legacy_reg __iomem *) + (bar0 + 0x800000); + else + legacy_reg = (struct vxge_hw_legacy_reg __iomem *)bar0; + + status = __vxge_hw_legacy_swapper_set(legacy_reg); + if (status != VXGE_HW_OK) + return NULL; + + return legacy_reg; +} /* * __vxge_hw_device_toc_get * This routine sets the swapper and reads the toc pointer and returns the * memory mapped address of the toc */ struct vxge_hw_toc_reg __iomem * -__vxge_hw_device_toc_get(void __iomem *bar0) +__vxge_hw_device_toc_get(void __iomem *bar0, + struct vxge_hw_legacy_reg __iomem *legacy_reg) { u64 val64; struct vxge_hw_toc_reg __iomem *toc = NULL; - enum vxge_hw_status status; - - struct vxge_hw_legacy_reg __iomem *legacy_reg = - (struct vxge_hw_legacy_reg __iomem *)bar0; - - status = __vxge_hw_legacy_swapper_set(legacy_reg); - if (status != VXGE_HW_OK) - goto exit; val64 = readq(&legacy_reg->toc_first_pointer); toc = (struct vxge_hw_toc_reg __iomem *)(bar0+val64); -exit: + return toc; } @@ -224,9 +244,15 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev) u32 i; enum vxge_hw_status status = VXGE_HW_OK; - hldev->legacy_reg = (struct vxge_hw_legacy_reg __iomem *)hldev->bar0; + hldev->legacy_reg = __vxge_hw_device_get_legacy_reg(hldev->pdev, + hldev->bar0); + if (hldev->legacy_reg == NULL) { + status = VXGE_HW_FAIL; + goto exit; + } - hldev->toc_reg = __vxge_hw_device_toc_get(hldev->bar0); + hldev->toc_reg = __vxge_hw_device_toc_get(hldev->bar0, + hldev->legacy_reg); if (hldev->toc_reg == NULL) { status = VXGE_HW_FAIL; goto exit; @@ -234,7 +260,7 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev) val64 = readq(&hldev->toc_reg->toc_common_pointer); hldev->common_reg = - (struct vxge_hw_common_reg __iomem *)(hldev->bar0 + val64); + (struct vxge_hw_common_reg __iomem *)(hldev->bar0 + val64); val64 = readq(&hldev->toc_reg->toc_mrpcim_pointer); hldev->mrpcim_reg = @@ -355,7 +381,7 @@ void __vxge_hw_device_host_info_get(struct __vxge_hw_device *hldev) * each vpath */ enum vxge_hw_status -vxge_hw_device_hw_info_get(void __iomem *bar0, +vxge_hw_device_hw_info_get(struct pci_device *pdev, void __iomem *bar0, struct vxge_hw_device_hw_info *hw_info) { u32 i; @@ -365,13 +391,20 @@ vxge_hw_device_hw_info_get(void __iomem *bar0, struct vxge_hw_common_reg __iomem *common_reg; struct vxge_hw_vpath_reg __iomem *vpath_reg; struct vxge_hw_vpmgmt_reg __iomem *vpmgmt_reg; + struct vxge_hw_legacy_reg __iomem *legacy_reg; enum vxge_hw_status status; vxge_trace(); memset(hw_info, 0, sizeof(struct vxge_hw_device_hw_info)); - toc = __vxge_hw_device_toc_get(bar0); + legacy_reg = __vxge_hw_device_get_legacy_reg(pdev, bar0); + if (legacy_reg == NULL) { + status = VXGE_HW_ERR_CRITICAL; + goto exit; + } + + toc = __vxge_hw_device_toc_get(bar0, legacy_reg); if (toc == NULL) { status = VXGE_HW_ERR_CRITICAL; goto exit; diff --git a/src/drivers/net/vxge/vxge_config.h b/src/drivers/net/vxge/vxge_config.h index 3313123a..bf25134a 100644 --- a/src/drivers/net/vxge/vxge_config.h +++ b/src/drivers/net/vxge/vxge_config.h @@ -440,7 +440,7 @@ struct vxge_hw_device_hw_info { #define VXGE_HW_VH_NORMAL_FUNCTION 7 u64 function_mode; #define VXGE_HW_FUNCTION_MODE_MIN 0 -#define VXGE_HW_FUNCTION_MODE_MAX 10 +#define VXGE_HW_FUNCTION_MODE_MAX 11 #define VXGE_HW_FUNCTION_MODE_SINGLE_FUNCTION 0 #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION 1 @@ -453,6 +453,7 @@ struct vxge_hw_device_hw_info { #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_2 8 #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_4 9 #define VXGE_HW_FUNCTION_MODE_MRIOV_4 10 +#define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_DIRECT_IO 11 u32 func_id; u64 vpath_mask; @@ -607,6 +608,7 @@ void vxge_hw_ring_rxd_1b_set(struct vxge_hw_ring_rxd_1 *rxdp, } enum vxge_hw_status vxge_hw_device_hw_info_get( + struct pci_device *pdev, void __iomem *bar0, struct vxge_hw_device_hw_info *hw_info); @@ -727,12 +729,6 @@ __vxge_hw_pio_mem_write64(u64 val64, void __iomem *addr, return status; } -struct vxge_hw_toc_reg __iomem * -__vxge_hw_device_toc_get(void __iomem *bar0); - -enum vxge_hw_status -__vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev); - void __vxge_hw_device_host_info_get(struct __vxge_hw_device *hldev); diff --git a/src/drivers/net/vxge/vxge_main.c b/src/drivers/net/vxge/vxge_main.c index 4e07271a..e2abafbf 100644 --- a/src/drivers/net/vxge/vxge_main.c +++ b/src/drivers/net/vxge/vxge_main.c @@ -45,6 +45,7 @@ static char *vxge_func_mode_names[] = { "Multi Function 2 - 2 func, 8 vpath per func", "Multi Function 4 - 4 func, 4 vpath per func", "WLPEX/SharedIO 4 - 17 func, 1 vpath per func (PCIe ARI)", + "Multi Function 8 - For ESX DirectIO - 8 func, 2 vpath per func", }; static inline int is_vxge_card_up(struct vxgedev *vdev) @@ -533,7 +534,7 @@ vxge_probe(struct pci_device *pdev, const struct pci_device_id *id __unused) goto _exit0; } - status = vxge_hw_device_hw_info_get(bar0, &hw_info); + status = vxge_hw_device_hw_info_get(pdev, bar0, &hw_info); if (status != VXGE_HW_OK) { vxge_debug(VXGE_ERR, "%s: Reading of hardware info failed.\n", diff --git a/src/drivers/net/vxge/vxge_version.h b/src/drivers/net/vxge/vxge_version.h index f1a02099..1475b77e 100644 --- a/src/drivers/net/vxge/vxge_version.h +++ b/src/drivers/net/vxge/vxge_version.h @@ -22,9 +22,9 @@ FILE_LICENCE(GPL2_ONLY); * Note: Each field must be a nibble size */ #define VXGE_VERSION_MAJOR 3 -#define VXGE_VERSION_MINOR 1 +#define VXGE_VERSION_MINOR 5 #define VXGE_VERSION_FIX 0 -#define VXGE_VERSION_BUILD 0 +#define VXGE_VERSION_BUILD 1 #define VXGE_FW_VER(major, minor, build) \ (((major) << 16) + ((minor) << 8) + (build)) From 35a50399a5881360303c0ed2d49918a660dd727d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 27 Jan 2011 18:46:07 +0000 Subject: [PATCH 19/23] [xfer] Expose xfer_uri_opener() Signed-off-by: Michael Brown --- src/core/open.c | 55 ++++++++++++++++++++++++++++++----------- src/include/ipxe/open.h | 1 + 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/core/open.c b/src/core/open.c index f8ee9f3a..b026efcd 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -32,6 +32,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** + * Find opener for URI scheme + * + * @v scheme URI scheme + * @ret opener Opener, or NULL + */ +struct uri_opener * xfer_uri_opener ( const char *scheme ) { + struct uri_opener *opener; + + for_each_table_entry ( opener, URI_OPENERS ) { + if ( strcmp ( scheme, opener->scheme ) == 0 ) + return opener; + } + return NULL; +} + /** * Open URI * @@ -45,29 +61,38 @@ FILE_LICENCE ( GPL2_OR_LATER ); int xfer_open_uri ( struct interface *intf, struct uri *uri ) { struct uri_opener *opener; struct uri *resolved_uri; - int rc = -ENOTSUP; + int rc; /* Resolve URI */ resolved_uri = resolve_uri ( cwuri, uri ); - if ( ! resolved_uri ) - return -ENOMEM; + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto err_resolve_uri; + } /* Find opener which supports this URI scheme */ - for_each_table_entry ( opener, URI_OPENERS ) { - if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) { - DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT - " opening %s URI\n", INTF_DBG ( intf ), - resolved_uri->scheme ); - rc = opener->open ( intf, resolved_uri ); - goto done; - } + opener = xfer_uri_opener ( resolved_uri->scheme ); + if ( ! opener ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " + "unsupported URI scheme \"%s\"\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + rc = -ENOTSUP; + goto err_opener; } - DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " - "unsupported URI scheme \"%s\"\n", - INTF_DBG ( intf ), resolved_uri->scheme ); - done: + /* Call opener */ + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " opening %s URI\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + if ( ( rc = opener->open ( intf, resolved_uri ) ) != 0 ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " could not open: " + "%s\n", INTF_DBG ( intf ), strerror ( rc ) ); + goto err_open; + } + + err_open: + err_opener: uri_put ( resolved_uri ); + err_resolve_uri: return rc; } diff --git a/src/include/ipxe/open.h b/src/include/ipxe/open.h index c000990c..a522f0cd 100644 --- a/src/include/ipxe/open.h +++ b/src/include/ipxe/open.h @@ -89,6 +89,7 @@ struct socket_opener { /** Register a socket opener */ #define __socket_opener __table_entry ( SOCKET_OPENERS, 01 ) +extern struct uri_opener * xfer_uri_opener ( const char *scheme ); extern int xfer_open_uri ( struct interface *intf, struct uri *uri ); extern int xfer_open_uri_string ( struct interface *intf, const char *uri_string ); From 962cada830e9b0ce7049a59bef4c33ab01c6161e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 27 Jan 2011 20:35:48 +0000 Subject: [PATCH 20/23] [init] Remove concept of "shutdown exit flags" Remove the concept of shutdown exit flags, and replace it with a counter used to keep track of exposed interfaces that require devices to remain active. Signed-off-by: Michael Brown --- src/arch/i386/drivers/net/undionly.c | 6 ++-- src/arch/i386/image/bzimage.c | 2 +- src/arch/i386/image/elfboot.c | 2 +- src/arch/i386/image/multiboot.c | 2 +- src/arch/i386/image/nbi.c | 2 +- src/arch/i386/interface/pcbios/int13.c | 8 +++-- src/arch/i386/interface/pxe/pxe_call.c | 2 ++ src/arch/i386/interface/pxe/pxe_preboot.c | 2 +- .../i386/interface/syslinux/comboot_call.c | 4 +-- src/core/device.c | 7 +++-- src/core/main.c | 2 +- src/drivers/net/efi/snponly.c | 6 ++-- src/image/efi_image.c | 2 +- src/include/ipxe/device.h | 18 +++++++++++ src/include/ipxe/init.h | 30 +++++++++++-------- src/include/usr/autoboot.h | 2 -- src/usr/autoboot.c | 4 --- 17 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/arch/i386/drivers/net/undionly.c b/src/arch/i386/drivers/net/undionly.c index ab9c61fb..c38b574d 100644 --- a/src/arch/i386/drivers/net/undionly.c +++ b/src/arch/i386/drivers/net/undionly.c @@ -114,13 +114,13 @@ struct root_device undi_root_device __root_device = { /** * Prepare for exit * - * @v flags Shutdown flags + * @v booting System is shutting down for OS boot */ -static void undionly_shutdown ( int flags ) { +static void undionly_shutdown ( int booting ) { /* If we are shutting down to boot an OS, clear the "keep PXE * stack" flag. */ - if ( flags & SHUTDOWN_BOOT ) + if ( booting ) preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL; } diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 900e34e1..45a1e862 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -477,7 +477,7 @@ static int bzimage_exec ( struct image *image ) { bzimage_update_header ( image, &bzimg, bzimg.rm_kernel ); /* Prepare for exiting */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ), diff --git a/src/arch/i386/image/elfboot.c b/src/arch/i386/image/elfboot.c index 7cb51bf9..331d3764 100644 --- a/src/arch/i386/image/elfboot.c +++ b/src/arch/i386/image/elfboot.c @@ -48,7 +48,7 @@ static int elfboot_exec ( struct image *image ) { /* An ELF image has no callback interface, so we need to shut * down before invoking it. */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Jump to OS with flat physical addressing */ DBGC ( image, "ELF %p starting execution at %lx\n", image, entry ); diff --git a/src/arch/i386/image/multiboot.c b/src/arch/i386/image/multiboot.c index 041f0f2a..3ed4d840 100644 --- a/src/arch/i386/image/multiboot.c +++ b/src/arch/i386/image/multiboot.c @@ -278,7 +278,7 @@ static int multiboot_exec ( struct image *image ) { /* Multiboot images may not return and have no callback * interface, so shut everything down prior to booting the OS. */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Build memory map after unhiding bootloader memory regions as part of * shutting everything down. diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c index 67f0d511..804b2303 100644 --- a/src/arch/i386/image/nbi.c +++ b/src/arch/i386/image/nbi.c @@ -406,7 +406,7 @@ static int nbi_exec ( struct image *image ) { /* Shut down now if NBI image will not return */ may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); if ( ! may_return ) - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Execute NBI image */ if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 1d973e77..a27dbad7 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -1209,8 +1209,10 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { int13->cylinders, int13->heads, int13->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ - if ( list_empty ( &int13s ) ) + if ( list_empty ( &int13s ) ) { int13_hook_vector(); + devices_get(); + } /* Add to list of emulated drives */ list_add ( &int13->list, &int13s ); @@ -1277,8 +1279,10 @@ static void int13_unhook ( unsigned int drive ) { DBGC ( int13, "INT13 drive %02x unregistered\n", int13->drive ); /* Unhook INT 13 vector if no more drives */ - if ( list_empty ( &int13s ) ) + if ( list_empty ( &int13s ) ) { + devices_put(); int13_unhook_vector(); + } /* Drop list's reference to drive */ ref_put ( &int13->refcnt ); diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index f6324bbf..f3208000 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -448,6 +448,7 @@ void pxe_activate ( struct net_device *netdev ) { if ( ! int_1a_hooked ) { hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, &pxe_int_1a_vector ); + devices_get(); int_1a_hooked = 1; } @@ -475,6 +476,7 @@ int pxe_deactivate ( void ) { strerror ( rc ) ); return rc; } + devices_put(); int_1a_hooked = 0; } diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/i386/interface/pxe/pxe_preboot.c index 7698df52..9e4853b0 100644 --- a/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/src/arch/i386/interface/pxe/pxe_preboot.c @@ -290,7 +290,7 @@ PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) { pxe_deactivate(); /* Prepare for unload */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Check to see if we still have any hooked interrupts */ if ( hooked_bios_interrupts != 0 ) { diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 0c52b28a..95083270 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -531,7 +531,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; /* Perform final cleanup */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Perform sequence of copies */ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); @@ -608,7 +608,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; /* Perform final cleanup */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Perform sequence of copies */ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); diff --git a/src/core/device.c b/src/core/device.c index cb2c23b0..dc182e03 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -35,6 +35,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Registered root devices */ static LIST_HEAD ( devices ); +/** Device removal inhibition counter */ +int device_keep_count = 0; + /** * Probe a root device * @@ -87,11 +90,11 @@ static void probe_devices ( void ) { * Remove all devices * */ -static void remove_devices ( int flags ) { +static void remove_devices ( int booting __unused ) { struct root_device *rootdev; struct root_device *tmp; - if ( flags & SHUTDOWN_KEEP_DEVICES ) { + if ( device_keep_count != 0 ) { DBG ( "Refusing to remove devices on shutdown\n" ); return; } diff --git a/src/core/main.c b/src/core/main.c index a1128dd0..e2b4e3e2 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -85,7 +85,7 @@ __asmcall int main ( void ) { shell(); } - shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags ); + shutdown_exit(); return 0; } diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c index 435ed4fb..6fcc54a0 100644 --- a/src/drivers/net/efi/snponly.c +++ b/src/drivers/net/efi/snponly.c @@ -114,13 +114,13 @@ struct root_device snp_root_device __root_device = { /** * Prepare for exit * - * @v flags Shutdown flags + * @v booting System is shutting down for OS boot */ -static void snponly_shutdown ( int flags ) { +static void snponly_shutdown ( int booting ) { /* If we are shutting down to boot an OS, make sure the SNP does not * stay active. */ - if ( flags & SHUTDOWN_BOOT ) + if ( booting ) snponly_dev.removal_state = EfiSimpleNetworkStopped; } diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 6b6600de..bf2e6f4a 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -39,7 +39,7 @@ static EFI_EVENT efi_shutdown_event; */ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); } /** diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h index 068268ba..635ce59c 100644 --- a/src/include/ipxe/device.h +++ b/src/include/ipxe/device.h @@ -112,6 +112,24 @@ struct root_driver { /** Declare a root device */ #define __root_device __table_entry ( ROOT_DEVICES, 01 ) +extern int device_keep_count; + +/** + * Prevent devices from being removed on shutdown + * + */ +static inline void devices_get ( void ) { + device_keep_count++; +} + +/** + * Allow devices to be removed on shutdown + * + */ +static inline void devices_put ( void ) { + device_keep_count--; +} + extern struct device * identify_device ( struct interface *intf ); #define identify_device_TYPE( object_type ) \ typeof ( struct device * ( object_type ) ) diff --git a/src/include/ipxe/init.h b/src/include/ipxe/init.h index 10ff8695..954cda45 100644 --- a/src/include/ipxe/init.h +++ b/src/include/ipxe/init.h @@ -32,16 +32,6 @@ struct init_fn { /** @} */ -/** Shutdown flags */ -enum shutdown_flags { - /** Shutdown is in order to exit (return to iPXE's caller) */ - SHUTDOWN_EXIT = 0x0001, - /** Shutdown is in order to boot an OS */ - SHUTDOWN_BOOT = 0x0002, - /** Do not remove devices */ - SHUTDOWN_KEEP_DEVICES = 0x0004, -}; - /** * A startup/shutdown function * @@ -50,7 +40,7 @@ enum shutdown_flags { */ struct startup_fn { void ( * startup ) ( void ); - void ( * shutdown ) ( int flags ); + void ( * shutdown ) ( int booting ); }; /** Startup/shutdown function table */ @@ -76,6 +66,22 @@ struct startup_fn { extern void initialise ( void ); extern void startup ( void ); -extern void shutdown ( int flags ); +extern void shutdown ( int booting ); + +/** + * Shut down system for OS boot + * + */ +static inline void shutdown_boot ( void ) { + shutdown ( 1 ); +} + +/** + * Shut down system for exit back to firmware + * + */ +static inline void shutdown_exit ( void ) { + shutdown ( 0 ); +} #endif /* _IPXE_INIT_H */ diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index e9abf833..32763bee 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -12,8 +12,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include struct net_device; -extern int shutdown_exit_flags; - extern int netboot ( struct net_device *netdev ); extern int autoboot ( void ); extern int boot_next_server_and_filename ( struct in_addr next_server, diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 738c3cea..df152e3a 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -40,9 +40,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Shutdown flags for exit */ -int shutdown_exit_flags = 0; - /** * Perform PXE menu boot when PXE stack is not available */ @@ -193,7 +190,6 @@ int boot_root_path ( const char *root_path ) { if ( fetch_intz_setting ( NULL, &keep_san_setting ) != 0 ) { printf ( "Preserving connection to SAN device %#02x\n", drive ); - shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES; goto err_keep_san; } From e088892a81e428aa4982b1c97001a1196d91acb7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 27 Jan 2011 18:48:47 +0000 Subject: [PATCH 21/23] [autoboot] Connect SAN disk during a filename boot, if applicable For performing installations direct to a SAN target, it can be very useful to hook a SAN disk and then proceed to perform a filename boot. For example, the user may wish to hook the (empty) SAN installation disk and then boot into the OS installer via TFTP. This provides an alternative mechanism to using "keep-san" and relying on the BIOS to fall through to boot from the installation media, which is unreliable on many BIOSes. When a root-path is specified in addition to a boot filename, attempt to hook the root-path as a SAN disk before booting from the specified filename. Since the root-path may be used for non-SAN purposes (e.g. an NFS root mount point), ignore the root-path if it contains a URI scheme that we do not support. Originally-implemented-by: Jarrod Johnson Signed-off-by: Michael Brown --- src/hci/commands/sanboot_cmd.c | 28 ++- src/include/ipxe/errfile.h | 1 + src/include/usr/autoboot.h | 8 +- src/include/usr/imgmgmt.h | 2 + src/usr/autoboot.c | 315 +++++++++++++++++++-------------- src/usr/imgmgmt.c | 51 ++++-- src/usr/pxemenu.c | 19 +- 7 files changed, 261 insertions(+), 163 deletions(-) diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 1f11cc2d..61bc5463 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include FILE_LICENCE ( GPL2_OR_LATER ); @@ -52,23 +54,33 @@ static struct command_descriptor sanboot_cmd = static int sanboot_exec ( int argc, char **argv ) { struct sanboot_options opts; const char *root_path; + struct uri *uri; int rc; /* Parse options */ if ( ( rc = parse_options ( argc, argv, &sanboot_cmd, &opts ) ) != 0 ) - return rc; + goto err_parse_options; /* Parse root path */ root_path = argv[optind]; - - /* Boot from root path */ - if ( ( rc = boot_root_path ( root_path ) ) != 0 ) { - printf ( "Could not boot from %s: %s\n", - root_path, strerror ( rc ) ); - return rc; + uri = parse_uri ( root_path ); + if ( ! uri ) { + rc = -ENOMEM; + goto err_parse_uri; } - return 0; + /* Boot from root path */ + if ( ( rc = uriboot ( NULL, uri ) ) != 0 ) { + printf ( "Could not boot from %s: %s\n", + root_path, strerror ( rc ) ); + goto err_uriboot; + } + + err_uriboot: + uri_put ( uri ); + err_parse_uri: + err_parse_options: + return rc; } /** SAN commands */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index f3a8efb0..24d5b310 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -235,6 +235,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ifmgmt_cmd ( ERRFILE_OTHER | 0x001d0000 ) #define ERRFILE_fcmgmt_cmd ( ERRFILE_OTHER | 0x001e0000 ) #define ERRFILE_gdbstub_cmd ( ERRFILE_OTHER | 0x001f0000 ) +#define ERRFILE_sanboot_cmd ( ERRFILE_OTHER | 0x00200000 ) /** @} */ diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index 32763bee..a608b3dc 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -11,12 +11,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include struct net_device; +struct uri; +struct settings; +extern int uriboot ( struct uri *filename, struct uri *root_path ); +extern struct uri * +fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); extern int autoboot ( void ); -extern int boot_next_server_and_filename ( struct in_addr next_server, - const char *filename ); -extern int boot_root_path ( const char *root_path ); extern int pxe_menu_boot ( struct net_device *netdev ); diff --git a/src/include/usr/imgmgmt.h b/src/include/usr/imgmgmt.h index 0c8c8cf7..0beab513 100644 --- a/src/include/usr/imgmgmt.h +++ b/src/include/usr/imgmgmt.h @@ -11,6 +11,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct image; +extern int imgdownload ( struct image *image, struct uri *uri, + int ( * image_register ) ( struct image *image ) ); extern int imgfetch ( struct image *image, const char *uri_string, int ( * image_register ) ( struct image *image ) ); extern int imgload ( struct image *image ); diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index df152e3a..e7d6787a 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -57,30 +58,21 @@ static struct net_device * find_boot_netdev ( void ) { } /** - * Boot using next-server and filename + * Parse next-server and filename into a URI * - * @v filename Boot filename - * @ret rc Return status code + * @v next_server Next-server address + * @v filename Filename + * @ret uri URI, or NULL on failure */ -int boot_next_server_and_filename ( struct in_addr next_server, - const char *filename ) { +static struct uri * parse_next_server_and_filename ( struct in_addr next_server, + const char *filename ) { struct uri *uri; - struct image *image; - char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + - ( 3 * strlen(filename) ) /* completely URI-encoded */ - + 1 /* NUL */ ]; - int filename_is_absolute; - int rc; + struct uri *tmp; - /* Construct URI */ + /* Parse filename */ uri = parse_uri ( filename ); - if ( ! uri ) { - printf ( "Could not parse \"%s\"\n", filename ); - rc = -ENOMEM; - goto err_parse_uri; - } - filename_is_absolute = uri_is_absolute ( uri ); - uri_put ( uri ); + if ( ! uri ) + return NULL; /* Construct a tftp:// URI for the filename, if applicable. * We can't just rely on the current working URI, because the @@ -88,41 +80,17 @@ int boot_next_server_and_filename ( struct in_addr next_server, * filenames with and without initial slashes, which is * significant for TFTP. */ - if ( ! filename_is_absolute ) { - snprintf ( buf, sizeof ( buf ), "tftp://%s/", - inet_ntoa ( next_server ) ); - uri_encode ( filename, buf + strlen ( buf ), - sizeof ( buf ) - strlen ( buf ), URI_PATH ); - filename = buf; + if ( ! uri_is_absolute ( uri ) ) { + tmp = uri; + tmp->scheme = "tftp"; + tmp->host = inet_ntoa ( next_server ); + uri = uri_dup ( tmp ); + uri_put ( tmp ); + if ( ! uri ) + return NULL; } - /* Download and boot image */ - image = alloc_image(); - if ( ! image ) { - printf ( "Could not allocate image\n" ); - rc = -ENOMEM; - goto err_alloc_image; - } - if ( ( rc = imgfetch ( image, filename, - register_and_autoload_image ) ) != 0 ) { - printf ( "Could not fetch image: %s\n", strerror ( rc ) ); - goto err_imgfetch; - } - if ( ( rc = imgexec ( image ) ) != 0 ) { - printf ( "Could not execute image: %s\n", strerror ( rc ) ); - goto err_imgexec; - } - - /* Drop image reference */ - image_put ( image ); - return 0; - - err_imgexec: - err_imgfetch: - image_put ( image ); - err_alloc_image: - err_parse_uri: - return rc; + return uri; } /** The "keep-san" setting */ @@ -142,71 +110,99 @@ struct setting skip_san_boot_setting __setting = { }; /** - * Boot using root path + * Boot from filename and root-path URIs * + * @v filename Filename * @v root_path Root path * @ret rc Return status code */ -int boot_root_path ( const char *root_path ) { - struct uri *uri; +int uriboot ( struct uri *filename, struct uri *root_path ) { + struct image *image; int drive; int rc; - /* Parse URI */ - uri = parse_uri ( root_path ); - if ( ! uri ) { - printf ( "Could not parse \"%s\"\n", root_path ); + /* Allocate image */ + image = alloc_image(); + if ( ! image ) { + printf ( "Could not allocate image\n" ); rc = -ENOMEM; - goto err_parse_uri; + goto err_alloc_image; } - /* Hook SAN device */ - if ( ( drive = san_hook ( uri, 0 ) ) < 0 ) { - rc = drive; - printf ( "Could not open SAN device: %s\n", - strerror ( rc ) ); - goto err_open; - } - printf ( "Registered as SAN device %#02x\n", drive ); + /* Treat empty URIs as absent */ + if ( filename && ( ! filename->path ) ) + filename = NULL; + if ( root_path && ( ! uri_is_absolute ( root_path ) ) ) + root_path = NULL; - /* Describe SAN device */ - if ( ( rc = san_describe ( drive ) ) != 0 ) { + /* If we have both a filename and a root path, ignore an + * unsupported URI scheme in the root path, since it may + * represent an NFS root. + */ + if ( filename && root_path && + ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) { + printf ( "Ignoring unsupported root path\n" ); + root_path = NULL; + } + + /* Hook SAN device, if applicable */ + if ( root_path ) { + drive = san_hook ( root_path, 0 ); + if ( drive < 0 ) { + rc = drive; + printf ( "Could not open SAN device: %s\n", + strerror ( rc ) ); + goto err_san_hook; + } + printf ( "Registered as SAN device %#02x\n", drive ); + } else { + drive = -ENODEV; + } + + /* Describe SAN device, if applicable */ + if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) { printf ( "Could not describe SAN device %#02x: %s\n", drive, strerror ( rc ) ); - goto err_describe; + goto err_san_describe; } - /* Boot from SAN device */ - if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) != 0 ) { - printf ( "Skipping boot from SAN device %#02x\n", drive ); + /* Attempt filename or SAN boot as applicable */ + if ( filename ) { + if ( ( rc = imgdownload ( image, filename, + register_and_autoexec_image ) ) !=0){ + printf ( "Could not chain image: %s\n", + strerror ( rc ) ); + } + } else if ( root_path ) { + if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { + printf ( "Booting from SAN device %#02x\n", drive ); + rc = san_boot ( drive ); + printf ( "Boot from SAN device %#02x failed: %s\n", + drive, strerror ( rc ) ); + } else { + printf ( "Skipping boot from SAN device %#02x\n", + drive ); + rc = 0; + } } else { - printf ( "Booting from SAN device %#02x\n", drive ); - rc = san_boot ( drive ); - printf ( "Boot from SAN device %#02x failed: %s\n", - drive, strerror ( rc ) ); + printf ( "No filename or root path specified\n" ); + rc = -ENOENT; } - /* Leave drive registered, if instructed to do so */ - if ( fetch_intz_setting ( NULL, &keep_san_setting ) != 0 ) { - printf ( "Preserving connection to SAN device %#02x\n", - drive ); - goto err_keep_san; + err_san_describe: + /* Unhook SAN device, if applicable */ + if ( drive >= 0 ) { + if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { + printf ( "Unregistering SAN device %#02x\n", drive ); + san_unhook ( drive ); + } else { + printf ( "Preserving connection to SAN device %#02x\n", + drive ); + } } - - /* Unhook SAN deivce */ - printf ( "Unregistering SAN device %#02x\n", drive ); - san_unhook ( drive ); - - /* Drop URI reference */ - uri_put ( uri ); - - return 0; - - err_keep_san: - err_describe: - err_open: - uri_put ( uri ); - err_parse_uri: + err_san_hook: + image_put ( image ); + err_alloc_image: return rc; } @@ -227,12 +223,53 @@ static void close_all_netdevs ( void ) { } /** - * Boot from a network device + * Fetch next-server and filename settings into a URI * - * @v netdev Network device - * @ret rc Return status code + * @v settings Settings block + * @ret uri URI, or NULL on failure */ -int netboot ( struct net_device *netdev ) { +struct uri * fetch_next_server_and_filename ( struct settings *settings ) { + struct in_addr next_server; + char filename[256]; + + /* Fetch next-server setting */ + fetch_ipv4_setting ( settings, &next_server_setting, &next_server ); + if ( next_server.s_addr ) + printf ( "Next server: %s\n", inet_ntoa ( next_server ) ); + + /* Fetch filename setting */ + fetch_string_setting ( settings, &filename_setting, + filename, sizeof ( filename ) ); + if ( filename[0] ) + printf ( "Filename: %s\n", filename ); + + return parse_next_server_and_filename ( next_server, filename ); +} + +/** + * Fetch root-path setting into a URI + * + * @v settings Settings block + * @ret uri URI, or NULL on failure + */ +static struct uri * fetch_root_path ( struct settings *settings ) { + char root_path[256]; + + /* Fetch root-path setting */ + fetch_string_setting ( settings, &root_path_setting, + root_path, sizeof ( root_path ) ); + if ( root_path[0] ) + printf ( "Root path: %s\n", root_path ); + + return parse_uri ( root_path ); +} + +/** + * Check whether or not we have a usable PXE menu + * + * @ret have_menu A usable PXE menu is present + */ +static int have_pxe_menu ( void ) { struct setting vendor_class_id_setting = { .tag = DHCP_VENDOR_CLASS_ID }; struct setting pxe_discovery_control_setting @@ -240,8 +277,28 @@ int netboot ( struct net_device *netdev ) { struct setting pxe_boot_menu_setting = { .tag = DHCP_PXE_BOOT_MENU }; char buf[256]; - struct in_addr next_server; unsigned int pxe_discovery_control; + + fetch_string_setting ( NULL, &vendor_class_id_setting, + buf, sizeof ( buf ) ); + pxe_discovery_control = + fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + + return ( ( strcmp ( buf, "PXEClient" ) == 0 ) && + setting_exists ( NULL, &pxe_boot_menu_setting ) && + ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && + setting_exists ( NULL, &filename_setting ) ) ) ); +} + +/** + * Boot from a network device + * + * @v netdev Network device + * @ret rc Return status code + */ +int netboot ( struct net_device *netdev ) { + struct uri *filename; + struct uri *root_path; int rc; /* Close all other network devices */ @@ -249,44 +306,42 @@ int netboot ( struct net_device *netdev ) { /* Open device and display device status */ if ( ( rc = ifopen ( netdev ) ) != 0 ) - return rc; + goto err_ifopen; ifstat ( netdev ); /* Configure device via DHCP */ if ( ( rc = dhcp ( netdev ) ) != 0 ) - return rc; + goto err_dhcp; route(); /* Try PXE menu boot, if applicable */ - fetch_string_setting ( NULL, &vendor_class_id_setting, - buf, sizeof ( buf ) ); - pxe_discovery_control = - fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); - if ( ( strcmp ( buf, "PXEClient" ) == 0 ) && - setting_exists ( NULL, &pxe_boot_menu_setting ) && - ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && - setting_exists ( NULL, &filename_setting ) ) ) ) { + if ( have_pxe_menu() ) { printf ( "Booting from PXE menu\n" ); - return pxe_menu_boot ( netdev ); + rc = pxe_menu_boot ( netdev ); + goto err_pxe_menu_boot; } - /* Try to download and boot whatever we are given as a filename */ - fetch_ipv4_setting ( NULL, &next_server_setting, &next_server ); - fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) ); - if ( buf[0] ) { - printf ( "Booting from filename \"%s\"\n", buf ); - return boot_next_server_and_filename ( next_server, buf ); - } - - /* No filename; try the root path */ - fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) ); - if ( buf[0] ) { - printf ( "Booting from root path \"%s\"\n", buf ); - return boot_root_path ( buf ); - } + /* Fetch next server, filename and root path */ + filename = fetch_next_server_and_filename ( NULL ); + if ( ! filename ) + goto err_filename; + root_path = fetch_root_path ( NULL ); + if ( ! root_path ) + goto err_root_path; - printf ( "No filename or root path specified\n" ); - return -ENOENT; + /* Boot using next server, filename and root path */ + if ( ( rc = uriboot ( filename, root_path ) ) != 0 ) + goto err_uriboot; + + err_uriboot: + uri_put ( root_path ); + err_root_path: + uri_put ( filename ); + err_filename: + err_pxe_menu_boot: + err_dhcp: + err_ifopen: + return rc; } /** diff --git a/src/usr/imgmgmt.c b/src/usr/imgmgmt.c index 5e7629a6..e958bc19 100644 --- a/src/usr/imgmgmt.c +++ b/src/usr/imgmgmt.c @@ -36,24 +36,21 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Fetch an image + * Download an image * - * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz") - * @v name Name for image, or NULL - * @v register_image Image registration routine + * @v image Image + * @v uri URI + * @v image_register Action to take upon a successful download * @ret rc Return status code */ -int imgfetch ( struct image *image, const char *uri_string, - int ( * image_register ) ( struct image *image ) ) { - char uri_string_redacted[ strlen ( uri_string ) + 3 /* "***" */ - + 1 /* NUL */ ]; - struct uri *uri; +int imgdownload ( struct image *image, struct uri *uri, + int ( * image_register ) ( struct image *image ) ) { + size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 ); + char uri_string_redacted[len]; const char *password; int rc; - if ( ! ( uri = parse_uri ( uri_string ) ) ) - return -ENOMEM; - + /* Set image URI */ image_set_uri ( image, uri ); /* Redact password portion of URI, if necessary */ @@ -64,9 +61,35 @@ int imgfetch ( struct image *image, const char *uri_string, uri, URI_ALL ); uri->password = password; + /* Create downloader */ if ( ( rc = create_downloader ( &monojob, image, image_register, - LOCATION_URI, uri ) ) == 0 ) - rc = monojob_wait ( uri_string_redacted ); + LOCATION_URI, uri ) ) != 0 ) + return rc; + + /* Wait for download to complete */ + if ( ( rc = monojob_wait ( uri_string_redacted ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fetch an image + * + * @v image Image + * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz") + * @v image_register Action to take upon a successful fetch + * @ret rc Return status code + */ +int imgfetch ( struct image *image, const char *uri_string, + int ( * image_register ) ( struct image *image ) ) { + struct uri *uri; + int rc; + + if ( ! ( uri = parse_uri ( uri_string ) ) ) + return -ENOMEM; + + rc = imgdownload ( image, uri, image_register ); uri_put ( uri ); return rc; diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index bbd7670c..a9ea1f04 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -346,8 +347,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { struct pxe_menu *menu; unsigned int pxe_type; struct settings *pxebs_settings; - struct in_addr next_server; - char filename[256]; + struct uri *uri; int rc; /* Parse and allocate boot menu */ @@ -372,12 +372,15 @@ int pxe_menu_boot ( struct net_device *netdev ) { if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) return rc; - /* Attempt boot */ + /* Fetch next server and filename */ pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); assert ( pxebs_settings ); - fetch_ipv4_setting ( pxebs_settings, &next_server_setting, - &next_server ); - fetch_string_setting ( pxebs_settings, &filename_setting, - filename, sizeof ( filename ) ); - return boot_next_server_and_filename ( next_server, filename ); + uri = fetch_next_server_and_filename ( pxebs_settings ); + if ( ! uri ) + return -ENOMEM; + + /* Attempt boot */ + rc = uriboot ( uri, NULL ); + uri_put ( uri ); + return rc; } From 990cbb8f2c69224d88c32006ad16a28d454352fa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jan 2011 00:16:18 +0000 Subject: [PATCH 22/23] [settings] Generalise expand_command() to expand_settings() Signed-off-by: Michael Brown --- src/core/exec.c | 76 +--------------------------------- src/core/settings.c | 81 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/settings.h | 1 + 3 files changed, 83 insertions(+), 75 deletions(-) diff --git a/src/core/exec.c b/src/core/exec.c index bb3b343d..57b2df58 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -86,80 +86,6 @@ int execv ( const char *command, char * const argv[] ) { return -ENOEXEC; } -/** - * Expand variables within command line - * - * @v command Command line - * @ret expcmd Expanded command line - * - * The expanded command line is allocated with malloc() and the caller - * must eventually free() it. - */ -static char * expand_command ( const char *command ) { - char *expcmd; - char *start; - char *end; - char *head; - char *name; - char *tail; - int setting_len; - int new_len; - char *tmp; - - /* Obtain temporary modifiable copy of command line */ - expcmd = strdup ( command ); - if ( ! expcmd ) - return NULL; - - /* Expand while expansions remain */ - while ( 1 ) { - - head = expcmd; - - /* Locate setting to be expanded */ - start = NULL; - end = NULL; - for ( tmp = expcmd ; *tmp ; tmp++ ) { - if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) - start = tmp; - if ( start && ( tmp[0] == '}' ) ) { - end = tmp; - break; - } - } - if ( ! end ) - break; - *start = '\0'; - name = ( start + 2 ); - *end = '\0'; - tail = ( end + 1 ); - - /* Determine setting length */ - setting_len = fetchf_named_setting ( name, NULL, 0 ); - if ( setting_len < 0 ) - setting_len = 0; /* Treat error as empty setting */ - - /* Read setting into temporary buffer */ - { - char setting_buf[ setting_len + 1 ]; - - setting_buf[0] = '\0'; - fetchf_named_setting ( name, setting_buf, - sizeof ( setting_buf ) ); - - /* Construct expanded string and discard old string */ - tmp = expcmd; - new_len = asprintf ( &expcmd, "%s%s%s", - head, setting_buf, tail ); - free ( tmp ); - if ( new_len < 0 ) - return NULL; - } - } - - return expcmd; -} - /** * Split command line into tokens * @@ -294,7 +220,7 @@ int system ( const char *command ) { int rc = 0; /* Perform variable expansion */ - expcmd = expand_command ( command ); + expcmd = expand_settings ( command ); if ( ! expcmd ) return -ENOMEM; diff --git a/src/core/settings.c b/src/core/settings.c index e2b48263..da662e61 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1467,6 +1467,87 @@ struct setting_type setting_type_uuid __setting_type = { .fetchf = fetchf_uuid, }; +/****************************************************************************** + * + * Setting expansion + * + ****************************************************************************** + */ + +/** + * Expand variables within string + * + * @v string String + * @ret expstr Expanded string + * + * The expanded string is allocated with malloc() and the caller must + * eventually free() it. + */ +char * expand_settings ( const char *string ) { + char *expstr; + char *start; + char *end; + char *head; + char *name; + char *tail; + int setting_len; + int new_len; + char *tmp; + + /* Obtain temporary modifiable copy of string */ + expstr = strdup ( string ); + if ( ! expstr ) + return NULL; + + /* Expand while expansions remain */ + while ( 1 ) { + + head = expstr; + + /* Locate setting to be expanded */ + start = NULL; + end = NULL; + for ( tmp = expstr ; *tmp ; tmp++ ) { + if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) + start = tmp; + if ( start && ( tmp[0] == '}' ) ) { + end = tmp; + break; + } + } + if ( ! end ) + break; + *start = '\0'; + name = ( start + 2 ); + *end = '\0'; + tail = ( end + 1 ); + + /* Determine setting length */ + setting_len = fetchf_named_setting ( name, NULL, 0 ); + if ( setting_len < 0 ) + setting_len = 0; /* Treat error as empty setting */ + + /* Read setting into temporary buffer */ + { + char setting_buf[ setting_len + 1 ]; + + setting_buf[0] = '\0'; + fetchf_named_setting ( name, setting_buf, + sizeof ( setting_buf ) ); + + /* Construct expanded string and discard old string */ + tmp = expstr; + new_len = asprintf ( &expstr, "%s%s%s", + head, setting_buf, tail ); + free ( tmp ); + if ( new_len < 0 ) + return NULL; + } + } + + return expstr; +} + /****************************************************************************** * * Settings diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index a764bf0e..d99e5ec0 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -221,6 +221,7 @@ extern int storef_setting ( struct settings *settings, const char *value ); extern int storef_named_setting ( const char *name, const char *value ); extern int fetchf_named_setting ( const char *name, char *buf, size_t len ); +extern char * expand_settings ( const char *string ); extern struct setting_type setting_type_string __setting_type; extern struct setting_type setting_type_ipv4 __setting_type; From 3ed849bbf2922b8726f468ef01d9b5e511e83f71 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Jan 2011 00:24:05 +0000 Subject: [PATCH 23/23] [autoboot] Allow setting expansions in filename and root-path Allow the DHCP filename and root-path to contain settings expansions, such as http://boot.ipxe.org/demo/boot.php?mac=${mac:hexhyp} Originally-implemented-by: Jarrod Johnson Signed-off-by: Michael Brown --- src/usr/autoboot.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index e7d6787a..91f1c6e8 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -230,7 +230,9 @@ static void close_all_netdevs ( void ) { */ struct uri * fetch_next_server_and_filename ( struct settings *settings ) { struct in_addr next_server; - char filename[256]; + char buf[256]; + char *filename; + struct uri *uri; /* Fetch next-server setting */ fetch_ipv4_setting ( settings, &next_server_setting, &next_server ); @@ -239,11 +241,20 @@ struct uri * fetch_next_server_and_filename ( struct settings *settings ) { /* Fetch filename setting */ fetch_string_setting ( settings, &filename_setting, - filename, sizeof ( filename ) ); - if ( filename[0] ) - printf ( "Filename: %s\n", filename ); + buf, sizeof ( buf ) ); + if ( buf[0] ) + printf ( "Filename: %s\n", buf ); - return parse_next_server_and_filename ( next_server, filename ); + /* Expand filename setting */ + filename = expand_settings ( buf ); + if ( ! filename ) + return NULL; + + /* Parse next server and filename */ + uri = parse_next_server_and_filename ( next_server, filename ); + + free ( filename ); + return uri; } /** @@ -253,15 +264,26 @@ struct uri * fetch_next_server_and_filename ( struct settings *settings ) { * @ret uri URI, or NULL on failure */ static struct uri * fetch_root_path ( struct settings *settings ) { - char root_path[256]; + char buf[256]; + char *root_path; + struct uri *uri; /* Fetch root-path setting */ fetch_string_setting ( settings, &root_path_setting, - root_path, sizeof ( root_path ) ); - if ( root_path[0] ) - printf ( "Root path: %s\n", root_path ); + buf, sizeof ( buf ) ); + if ( buf[0] ) + printf ( "Root path: %s\n", buf ); - return parse_uri ( root_path ); + /* Expand filename setting */ + root_path = expand_settings ( buf ); + if ( ! root_path ) + return NULL; + + /* Parse root path */ + uri = parse_uri ( root_path ); + + free ( root_path ); + return uri; } /**