From 18d0818f94c6750c680cba582932ae16659c0c38 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 03:10:03 +0200 Subject: [PATCH 01/65] [tcp] Do not send RST for unrecognised connections On large networks with substantial numbers of monitoring agents, unwanted TCP connection attempts may end up flooding iPXE's ARP cache. Fix by silently dropping packets received for unrecognised TCP connections. This should not cause problems, since many firewalls will also silently drop any such packets. Reported-by: Jarrod Johnson Signed-off-by: Michael Brown --- src/net/tcp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/net/tcp.c b/src/net/tcp.c index 938edd57..8432d559 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -1218,7 +1218,6 @@ static int tcp_rx ( struct io_buffer *iobuf, /* If no connection was found, send RST */ if ( ! tcp ) { - tcp_xmit_reset ( tcp, st_src, tcphdr ); rc = -ENOTCONN; goto discard; } From d4f8e56bb4b4529d8775c13f54f0eda3c1d3b28f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 11:15:42 +0200 Subject: [PATCH 02/65] [tcp] Fix comment to match code behaviour Reported-by: Thomas Miletich Signed-off-by: Michael Brown --- src/net/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/tcp.c b/src/net/tcp.c index 8432d559..b97107fc 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -1216,7 +1216,7 @@ static int tcp_rx ( struct io_buffer *iobuf, tcp_dump_flags ( tcp, tcphdr->flags ); DBGC2 ( tcp, "\n" ); - /* If no connection was found, send RST */ + /* If no connection was found, silently drop packet */ if ( ! tcp ) { rc = -ENOTCONN; goto discard; From 362a628e52ac2b8f3ace6c8920ab0942277480f4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 15:14:03 +0200 Subject: [PATCH 03/65] [test] Add self-tests for base16 Signed-off-by: Michael Brown --- src/tests/base16_test.c | 121 ++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 2 files changed, 122 insertions(+) create mode 100644 src/tests/base16_test.c diff --git a/src/tests/base16_test.c b/src/tests/base16_test.c new file mode 100644 index 00000000..9b047b74 --- /dev/null +++ b/src/tests/base16_test.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Base16 tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** A Base16 test */ +struct base16_test { + /** Raw data */ + const void *data; + /** Length of raw data */ + size_t len; + /** Base16-encoded data */ + const char *encoded; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a base16 test */ +#define BASE16( name, DATA, ENCODED ) \ + static const uint8_t name ## _data[] = DATA; \ + static struct base16_test name = { \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .encoded = ENCODED, \ + } + +/** Empty data test */ +BASE16 ( empty_test, DATA(), "" ); + +/** "Hello world" test */ +BASE16 ( hw_test, + DATA ( 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' ), + "48656c6c6f20776f726c64" ); + +/** Random data test */ +BASE16 ( random_test, + DATA ( 0x8b, 0x1a, 0xa2, 0x6c, 0xa9, 0x38, 0x43, 0xb8, 0x81, 0xf8, + 0x30, 0x44, 0xb2, 0x32, 0x6e, 0x82, 0xfe, 0x0f, 0x84, 0x91 ), + "8b1aa26ca93843b881f83044b2326e82fe0f8491" ); + +/** + * Report a base16 encoding test result + * + * @v test Base16 test + */ +#define base16_encode_ok( test ) do { \ + size_t len = base16_encoded_len ( (test)->len ); \ + char buf[ len + 1 /* NUL */ ]; \ + ok ( len == strlen ( (test)->encoded ) ); \ + base16_encode ( (test)->data, (test)->len, buf ); \ + ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ + } while ( 0 ) + +/** + * Report a base16 decoding test result + * + * @v test Base16 test + */ +#define base16_decode_ok( test ) do { \ + size_t max_len = base16_decoded_max_len ( (test)->encoded ); \ + uint8_t buf[max_len]; \ + int len; \ + len = base16_decode ( (test)->encoded, buf ); \ + ok ( len >= 0 ); \ + ok ( ( size_t ) len <= max_len ); \ + ok ( ( size_t ) len == (test)->len ); \ + ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ + } while ( 0 ) + +/** + * Perform Base16 self-tests + * + */ +static void base16_test_exec ( void ) { + + base16_encode_ok ( &empty_test ); + base16_decode_ok ( &empty_test ); + + base16_encode_ok ( &hw_test ); + base16_decode_ok ( &hw_test ); + + base16_encode_ok ( &random_test ); + base16_decode_ok ( &random_test ); +} + +/** Base16 self-test */ +struct self_test base16_test __self_test = { + .name = "base16", + .exec = base16_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index af969ec8..7c99a527 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -31,6 +31,7 @@ REQUIRE_OBJECT ( string_test ); REQUIRE_OBJECT ( list_test ); REQUIRE_OBJECT ( byteswap_test ); REQUIRE_OBJECT ( base64_test ); +REQUIRE_OBJECT ( base16_test ); REQUIRE_OBJECT ( settings_test ); REQUIRE_OBJECT ( time_test ); REQUIRE_OBJECT ( tcpip_test ); From 076f58c4bf9fa1ae0faa65a731523cb531705974 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 14:44:20 +0200 Subject: [PATCH 04/65] [base16] Generalise base16_decode() to hex_decode() Provide a generic hex_decode() routine which can be shared between the Base16 code and the "hex" and "hexhyp" settings parsers. Signed-off-by: Michael Brown --- src/core/base16.c | 70 ++++++++++++++++++++++++++------------- src/core/misc.c | 13 ++++++++ src/include/ipxe/base16.h | 2 ++ src/include/stdlib.h | 14 +------- 4 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/core/base16.c b/src/core/base16.c index 1f0e536c..7fa4b200 100644 --- a/src/core/base16.c +++ b/src/core/base16.c @@ -60,6 +60,48 @@ void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { assert ( strlen ( encoded ) == base16_encoded_len ( len ) ); } +/** + * Decode hexadecimal string + * + * @v encoded Encoded string + * @v separator Byte separator character, or 0 for no separator + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error + */ +int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { + uint8_t *out = data; + unsigned int count = 0; + unsigned int sixteens; + unsigned int units; + + while ( *encoded ) { + + /* Check separator, if applicable */ + if ( count && separator && ( ( *(encoded++) != separator ) ) ) + return -EINVAL; + + /* Extract digits. Note that either digit may be NUL, + * which would be interpreted as an invalid value by + * strtoul_charval(); there is therefore no need for an + * explicit end-of-string check. + */ + sixteens = strtoul_charval ( *(encoded++) ); + if ( sixteens >= 16 ) + return -EINVAL; + units = strtoul_charval ( *(encoded++) ); + if ( units >= 16 ) + return -EINVAL; + + /* Store result */ + if ( count < len ) + out[count] = ( ( sixteens << 4 ) | units ); + count++; + + } + return count; +} + /** * Base16-decode data * @@ -75,33 +117,15 @@ void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { * to provide a buffer of the correct size. */ int base16_decode ( const char *encoded, uint8_t *raw ) { - const char *encoded_bytes = encoded; - uint8_t *raw_bytes = raw; - char buf[3]; - char *endp; - size_t len; + int len; - while ( encoded_bytes[0] ) { - if ( ! encoded_bytes[1] ) { - DBG ( "Base16-encoded string \"%s\" has invalid " - "length\n", encoded ); - return -EINVAL; - } - memcpy ( buf, encoded_bytes, 2 ); - buf[2] = '\0'; - *(raw_bytes++) = strtoul ( buf, &endp, 16 ); - if ( *endp != '\0' ) { - DBG ( "Base16-encoded string \"%s\" has invalid " - "byte \"%s\"\n", encoded, buf ); - return -EINVAL; - } - encoded_bytes += 2; - } - len = ( raw_bytes - raw ); + len = hex_decode ( encoded, 0, raw, -1UL ); + if ( len < 0 ) + return len; DBG ( "Base16-decoded \"%s\" to:\n", encoded ); DBG_HDA ( 0, raw, len ); assert ( len <= base16_decoded_max_len ( encoded ) ); - return ( len ); + return len; } diff --git a/src/core/misc.c b/src/core/misc.c index 11342481..eaceddfe 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -33,6 +33,19 @@ int inet_aton ( const char *cp, struct in_addr *inp ) { return 0; } +unsigned int strtoul_charval ( unsigned int charval ) { + + if ( charval >= 'a' ) { + charval = ( charval - 'a' + 10 ); + } else if ( charval >= 'A' ) { + charval = ( charval - 'A' + 10 ); + } else if ( charval <= '9' ) { + charval = ( charval - '0' ); + } + + return charval; +} + unsigned long strtoul ( const char *p, char **endp, int base ) { unsigned long ret = 0; int negative = 0; diff --git a/src/include/ipxe/base16.h b/src/include/ipxe/base16.h index f0c9842f..60e3f231 100644 --- a/src/include/ipxe/base16.h +++ b/src/include/ipxe/base16.h @@ -33,6 +33,8 @@ static inline size_t base16_decoded_max_len ( const char *encoded ) { } extern void base16_encode ( const uint8_t *raw, size_t len, char *encoded ); +extern int hex_decode ( const char *string, char separator, void *data, + size_t len ); extern int base16_decode ( const char *encoded, uint8_t *raw ); #endif /* _IPXE_BASE16_H */ diff --git a/src/include/stdlib.h b/src/include/stdlib.h index 3d30858f..bca85a23 100644 --- a/src/include/stdlib.h +++ b/src/include/stdlib.h @@ -34,19 +34,7 @@ static inline int strtoul_base ( const char **pp, int base ) return base; } -static inline unsigned int strtoul_charval ( unsigned int charval ) -{ - if ( charval >= 'a' ) { - charval = ( charval - 'a' + 10 ); - } else if ( charval >= 'A' ) { - charval = ( charval - 'A' + 10 ); - } else if ( charval <= '9' ) { - charval = ( charval - '0' ); - } - - return charval; -} - +extern unsigned int strtoul_charval ( unsigned int charval ); extern unsigned long strtoul ( const char *p, char **endp, int base ); extern unsigned long long strtoull ( const char *p, char **endp, int base ); From 7774ceed2f57d3711b29ccc464e6c88f20e3c83b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 14:45:55 +0200 Subject: [PATCH 05/65] [settings] Use hex_decode() to parse hex settings Use hex_decode() to parse "hex" and "hexhyp" settings. Note that this parser is stricter than the old parser; it now requires exactly two hex digits for each byte. (The old parser was based upon strtoul() and so would allow leading whitespace and a leading plus or minus sign.) Signed-off-by: Michael Brown --- src/core/settings.c | 61 +++++++++++++++++++-------------------- src/tests/settings_test.c | 16 +++++----- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 54d48d32..42caae0c 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -1685,37 +1686,6 @@ struct setting_type setting_type_uint32 __setting_type = { .format = format_uint_setting, }; -/** - * Parse hex string setting value - * - * @v value Formatted setting value - * @v buf Buffer to contain raw value - * @v len Length of buffer - * @ret len Length of raw value, or negative error - */ -static int parse_hex_setting ( const char *value, void *buf, size_t len ) { - char *ptr = ( char * ) value; - uint8_t *bytes = buf; - unsigned int count = 0; - uint8_t byte; - - while ( 1 ) { - byte = strtoul ( ptr, &ptr, 16 ); - if ( count++ < len ) - *bytes++ = byte; - switch ( *ptr ) { - case '\0' : - return count; - case ':' : - case '-' : - ptr++; - break; - default : - return -EINVAL; - } - } -} - /** * Format hex string setting value * @@ -1742,6 +1712,19 @@ static int format_hex_setting ( const void *raw, size_t raw_len, char *buf, return used; } +/** + * Parse hex string setting value (using colon delimiter) + * + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_setting ( const char *value, void *buf, size_t len ) { + return hex_decode ( value, ':', buf, len ); +} + /** * Format hex string setting value (using colon delimiter) * @@ -1756,6 +1739,20 @@ static int format_hex_colon_setting ( const void *raw, size_t raw_len, return format_hex_setting ( raw, raw_len, buf, len, ":" ); } +/** + * Parse hex string setting value (using hyphen delimiter) + * + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_hyphen_setting ( const char *value, void *buf, + size_t len ) { + return hex_decode ( value, '-', buf, len ); +} + /** * Format hex string setting value (using hyphen delimiter) * @@ -1780,7 +1777,7 @@ struct setting_type setting_type_hex __setting_type = { /** A hex-string setting (hyphen-delimited) */ struct setting_type setting_type_hexhyp __setting_type = { .name = "hexhyp", - .parse = parse_hex_setting, + .parse = parse_hex_hyphen_setting, .format = format_hex_hyphen_setting, }; diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 028f8163..3156f8d2 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -51,9 +51,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); ok ( storef_setting ( settings, setting, formatted ) == 0 ); \ len = fetch_setting ( settings, setting, actual, \ sizeof ( actual ) ); \ - DBGC ( settings, "Stored %s \"%s\", got:\n", \ - (setting)->type->name, formatted ); \ - DBGC_HDA ( settings, 0, actual, len ); \ + if ( len >= 0 ) { \ + DBGC ( settings, "Stored %s \"%s\", got:\n", \ + (setting)->type->name, formatted ); \ + DBGC_HDA ( settings, 0, actual, len ); \ + } else { \ + DBGC ( settings, "Stored %s \"%s\", got error %s\n", \ + (setting)->type->name, formatted, \ + strerror ( len ) ); \ + } \ ok ( len == ( int ) sizeof ( actual ) ); \ ok ( memcmp ( actual, expected, sizeof ( actual ) ) == 0 ); \ } while ( 0 ) @@ -239,10 +245,6 @@ static void settings_test_exec ( void ) { RAW ( 0xf2, 0x37, 0xb2, 0x18 ), "0xf237b218" ); /* "hex" setting type */ - storef_ok ( &test_settings, &test_hex_setting, - ":", RAW ( 0x00, 0x00 ) ); - storef_ok ( &test_settings, &test_hex_setting, - "1:2:", RAW ( 0x01, 0x02, 0x00 ) ); storef_ok ( &test_settings, &test_hex_setting, "08:12:f5:22:90:1b:4b:47:a8:30:cb:4d:67:4c:d6:76", RAW ( 0x08, 0x12, 0xf5, 0x22, 0x90, 0x1b, 0x4b, 0x47, 0xa8, From 9f3bbaca0737dfa90bd9a8074d08e85caed69b6c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 14:58:19 +0200 Subject: [PATCH 06/65] [settings] Add "hexraw" setting type Originally-implemented-by: Jeppe Toustrup Signed-off-by: Michael Brown --- src/core/settings.c | 35 +++++++++++++++++++++++++++++++++++ src/include/ipxe/settings.h | 1 + src/tests/settings_test.c | 14 ++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/core/settings.c b/src/core/settings.c index 42caae0c..b8833c8d 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1767,6 +1767,34 @@ static int format_hex_hyphen_setting ( const void *raw, size_t raw_len, return format_hex_setting ( raw, raw_len, buf, len, "-" ); } +/** + * Parse hex string setting value (using no delimiter) + * + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_raw_setting ( const char *value, void *buf, + size_t len ) { + return hex_decode ( value, 0, buf, len ); +} + +/** + * Format hex string setting value (using no delimiter) + * + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_hex_raw_setting ( const void *raw, size_t raw_len, + char *buf, size_t len ) { + return format_hex_setting ( raw, raw_len, buf, len, "" ); +} + /** A hex-string setting (colon-delimited) */ struct setting_type setting_type_hex __setting_type = { .name = "hex", @@ -1781,6 +1809,13 @@ struct setting_type setting_type_hexhyp __setting_type = { .format = format_hex_hyphen_setting, }; +/** A hex-string setting (non-delimited) */ +struct setting_type setting_type_hexraw __setting_type = { + .name = "hexraw", + .parse = parse_hex_raw_setting, + .format = format_hex_raw_setting, +}; + /** * Parse UUID setting value * diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 9b7404ac..7ceb55ca 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -322,6 +322,7 @@ extern struct setting_type setting_type_uint16 __setting_type; extern struct setting_type setting_type_uint32 __setting_type; extern struct setting_type setting_type_hex __setting_type; extern struct setting_type setting_type_hexhyp __setting_type; +extern struct setting_type setting_type_hexraw __setting_type; extern struct setting_type setting_type_uuid __setting_type; extern struct setting ip_setting __setting ( SETTING_IPv4 ); diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 3156f8d2..d6d12574 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -170,6 +170,12 @@ static struct setting test_hexhyp_setting = { .type = &setting_type_hexhyp, }; +/** Test raw hex string setting type */ +static struct setting test_hexraw_setting = { + .name = "test_hexraw", + .type = &setting_type_hexraw, +}; + /** Test UUID setting type */ static struct setting test_uuid_setting = { .name = "test_uuid", @@ -262,6 +268,14 @@ static void settings_test_exec ( void ) { 0x09, 0x6c, 0x66, 0x13, 0xc1, 0xa8, 0xec, 0x27 ), "9f-e5-6d-fb-24-3a-4c-bb-a9-09-6c-66-13-c1-a8-ec-27" ); + /* "hexraw" setting type */ + storef_ok ( &test_settings, &test_hexraw_setting, + "012345abcdef", RAW ( 0x01, 0x23, 0x45, 0xab, 0xcd, 0xef )); + fetchf_ok ( &test_settings, &test_hexraw_setting, + RAW ( 0x9e, 0x4b, 0x6e, 0xef, 0x36, 0xb6, 0x46, 0xfe, 0x8f, + 0x17, 0x06, 0x39, 0x6b, 0xf4, 0x48, 0x4e ), + "9e4b6eef36b646fe8f1706396bf4484e" ); + /* "uuid" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_uuid_setting, RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, From c0cff9432011b7c14cc2fb63444a31db79f015a8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 22:25:35 +0200 Subject: [PATCH 07/65] [netdevice] Add "bustype" and "busloc" settings Signed-off-by: Michael Brown --- src/net/netdev_settings.c | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 028a62ca..3ea7ace5 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -40,6 +40,16 @@ struct setting mac_setting __setting ( SETTING_NETDEV ) = { .description = "MAC address", .type = &setting_type_hex, }; +struct setting bustype_setting __setting ( SETTING_NETDEV ) = { + .name = "bustype", + .description = "Bus type", + .type = &setting_type_string, +}; +struct setting busloc_setting __setting ( SETTING_NETDEV ) = { + .name = "busloc", + .description = "Bus location", + .type = &setting_type_uint32, +}; struct setting busid_setting __setting ( SETTING_NETDEV ) = { .name = "busid", .description = "Bus ID", @@ -93,6 +103,54 @@ static int netdev_fetch_mac ( struct net_device *netdev, void *data, return netdev->ll_protocol->ll_addr_len; } +/** + * Fetch bus type setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_bustype ( struct net_device *netdev, void *data, + size_t len ) { + static const char *bustypes[] = { + [BUS_TYPE_PCI] = "PCI", + [BUS_TYPE_ISAPNP] = "ISAPNP", + [BUS_TYPE_EISA] = "EISA", + [BUS_TYPE_MCA] = "MCA", + [BUS_TYPE_ISA] = "ISA", + }; + struct device_description *desc = &netdev->dev->desc; + const char *bustype; + + assert ( desc->bus_type < ( sizeof ( bustypes ) / + sizeof ( bustypes[0] ) ) ); + bustype = bustypes[desc->bus_type]; + assert ( bustypes != NULL ); + strncpy ( data, bustype, len ); + return strlen ( bustype ); +} + +/** + * Fetch bus location setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_busloc ( struct net_device *netdev, void *data, + size_t len ) { + struct device_description *desc = &netdev->dev->desc; + uint32_t busloc; + + busloc = cpu_to_be32 ( desc->location ); + if ( len > sizeof ( busloc ) ) + len = sizeof ( busloc ); + memcpy ( data, &busloc, len ); + return sizeof ( busloc ); +} + /** * Fetch bus ID setting * @@ -157,6 +215,8 @@ struct netdev_setting_operation { /** Network device settings */ static struct netdev_setting_operation netdev_setting_operations[] = { { &mac_setting, netdev_store_mac, netdev_fetch_mac }, + { &bustype_setting, NULL, netdev_fetch_bustype }, + { &busloc_setting, NULL, netdev_fetch_busloc }, { &busid_setting, NULL, netdev_fetch_busid }, { &chip_setting, NULL, netdev_fetch_chip }, }; From 3dbcce51eaa3de66c945628676d9303aca5a98fd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Jul 2013 22:28:00 +0200 Subject: [PATCH 08/65] [settings] Add "busdevfn" setting type Allow network device's "busloc" setting to be formatted as a PCI bus:dev.fn address using e.g. ${net0/busloc:busdevfn}. Signed-off-by: Michael Brown --- src/core/settings.c | 47 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/settings.h | 1 + src/tests/settings_test.c | 10 ++++++++ 3 files changed, 58 insertions(+) diff --git a/src/core/settings.c b/src/core/settings.c index b8833c8d..927ad845 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -1857,6 +1858,52 @@ struct setting_type setting_type_uuid __setting_type = { .format = format_uuid_setting, }; +/** + * Parse PCI bus:dev.fn setting value + * + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +static int parse_busdevfn_setting ( const char *value __unused, + void *buf __unused, size_t len __unused ) { + return -ENOTSUP; +} + +/** + * Format PCI bus:dev.fn setting value + * + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_busdevfn_setting ( const void *raw, size_t raw_len, char *buf, + size_t len ) { + signed long dummy; + unsigned long busdevfn; + int check_len; + + /* Extract numeric value */ + check_len = numeric_setting_value ( raw, raw_len, &dummy, &busdevfn ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == ( int ) raw_len ); + + /* Format value */ + return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), + PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); +} + +/** PCI bus:dev.fn setting type */ +struct setting_type setting_type_busdevfn __setting_type = { + .name = "busdevfn", + .parse = parse_busdevfn_setting, + .format = format_busdevfn_setting, +}; + /****************************************************************************** * * Setting expansion diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 7ceb55ca..8ee9516e 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -324,6 +324,7 @@ extern struct setting_type setting_type_hex __setting_type; extern struct setting_type setting_type_hexhyp __setting_type; extern struct setting_type setting_type_hexraw __setting_type; extern struct setting_type setting_type_uuid __setting_type; +extern struct setting_type setting_type_busdevfn __setting_type; extern struct setting ip_setting __setting ( SETTING_IPv4 ); extern struct setting netmask_setting __setting ( SETTING_IPv4 ); diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index d6d12574..42957c7d 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -182,6 +182,12 @@ static struct setting test_uuid_setting = { .type = &setting_type_uuid, }; +/** Test PCI bus:dev.fn setting type */ +static struct setting test_busdevfn_setting = { + .name = "test_busdevfn", + .type = &setting_type_busdevfn, +}; + /** * Perform settings self-tests * @@ -282,6 +288,10 @@ static void settings_test_exec ( void ) { 0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ), "1a6a749d-0eda-461a-a87a-7cfe4fca4a57" ); + /* "busdevfn" setting type (no store capability) */ + fetchf_ok ( &test_settings, &test_busdevfn_setting, + RAW ( 0x03, 0x45 ), "03:08.5" ); + /* Clear and unregister test settings block */ clear_settings ( &test_settings ); unregister_settings ( &test_settings ); From d8392851d2d5cfe0a7f79c2dab29219f59affa60 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 13 Jul 2013 12:42:40 +0200 Subject: [PATCH 09/65] [linux] Add support for accessing PCI configuration space via /proc/bus/pci Signed-off-by: Michael Brown --- src/arch/x86/core/linux/linux_api.c | 4 + src/config/defaults/linux.h | 1 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/linux.h | 8 ++ src/include/ipxe/linux/linux_pci.h | 130 +++++++++++++++++++ src/include/ipxe/pci_io.h | 1 + src/include/linux_api.h | 2 + src/interface/linux/linux_pci.c | 185 ++++++++++++++++++++++++++++ 8 files changed, 332 insertions(+) create mode 100644 src/include/ipxe/linux/linux_pci.h create mode 100644 src/interface/linux/linux_pci.c diff --git a/src/arch/x86/core/linux/linux_api.c b/src/arch/x86/core/linux/linux_api.c index c8a09b7d..0bed9fd5 100644 --- a/src/arch/x86/core/linux/linux_api.c +++ b/src/arch/x86/core/linux/linux_api.c @@ -37,6 +37,10 @@ int linux_close ( int fd ) { return linux_syscall ( __NR_close, fd ); } +off_t linux_lseek ( int fd, off_t offset, int whence ) { + return linux_syscall ( __NR_lseek, fd, offset, whence ); +} + __kernel_ssize_t linux_read ( int fd, void *buf, __kernel_size_t count ) { return linux_syscall ( __NR_read, fd, buf, count ); } diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 666db6b8..2b565943 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -17,6 +17,7 @@ #define ENTROPY_LINUX #define TIME_LINUX #define REBOOT_NULL +#define PCIAPI_LINUX #define DRIVERS_LINUX diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 67edcc93..2b9d16ca 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -275,6 +275,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_efi_init ( ERRFILE_OTHER | 0x00390000 ) #define ERRFILE_efi_timer ( ERRFILE_OTHER | 0x003a0000 ) #define ERRFILE_efi_umalloc ( ERRFILE_OTHER | 0x003b0000 ) +#define ERRFILE_linux_pci ( ERRFILE_OTHER | 0x003c0000 ) /** @} */ diff --git a/src/include/ipxe/linux.h b/src/include/ipxe/linux.h index dac508ea..a01ace3d 100644 --- a/src/include/ipxe/linux.h +++ b/src/include/ipxe/linux.h @@ -30,6 +30,14 @@ FILE_LICENCE(GPL2_OR_LATER); #include #include +/** + * Convert a Linux error number to an iPXE status code + * + * @v errno Linux error number + * @ret rc iPXE status code (before negation) + */ +#define ELINUX( errno ) EPLATFORM ( EINFO_EPLATFORM, errno ) + /** A linux device */ struct linux_device { /** Generic device */ diff --git a/src/include/ipxe/linux/linux_pci.h b/src/include/ipxe/linux/linux_pci.h new file mode 100644 index 00000000..43916673 --- /dev/null +++ b/src/include/ipxe/linux/linux_pci.h @@ -0,0 +1,130 @@ +#ifndef _IPXE_LINUX_PCI_H +#define _IPXE_LINUX_PCI_H + +/** @file + * + * iPXE PCI API for Linux + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifdef PCIAPI_LINUX +#define PCIAPI_PREFIX_linux +#else +#define PCIAPI_PREFIX_linux __linux_ +#endif + +struct pci_device; + +extern int linux_pci_read ( struct pci_device *pci, unsigned long where, + unsigned long *value, size_t len ); +extern int linux_pci_write ( struct pci_device *pci, unsigned long where, + unsigned long value, size_t len ); + +/** + * Read byte from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Read word from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Read dword from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Write byte to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +/** + * Write word to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +/** + * Write dword to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +#endif /* _IPXE_LINUX_PCI_H */ diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 7368cf48..781b77fe 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Include all architecture-independent I/O API headers */ #include +#include /* Include all architecture-dependent I/O API headers */ #include diff --git a/src/include/linux_api.h b/src/include/linux_api.h index 94dc991f..28a3cda3 100644 --- a/src/include/linux_api.h +++ b/src/include/linux_api.h @@ -47,11 +47,13 @@ typedef __kernel_loff_t loff_t; typedef unsigned long nfds_t; typedef uint32_t useconds_t; #define MAP_FAILED ( ( void * ) -1 ) +#define SEEK_SET 0 extern long linux_syscall ( int number, ... ); extern int linux_open ( const char *pathname, int flags ); extern int linux_close ( int fd ); +extern off_t linux_lseek ( int fd, off_t offset, int whence ); extern __kernel_ssize_t linux_read ( int fd, void *buf, __kernel_size_t count ); extern __kernel_ssize_t linux_write ( int fd, const void *buf, __kernel_size_t count ); diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c new file mode 100644 index 00000000..cbd825c1 --- /dev/null +++ b/src/interface/linux/linux_pci.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * iPXE PCI API for Linux + * + */ + +/** + * Open PCI configuration space + * + * @v pci PCI device + * @v flags Access mode flags + * @v where Address within configuration space + * @ret fd File handle, or negative error + */ +static int linux_pci_open ( struct pci_device *pci, int flags, + unsigned long where ) { + char filename[ 22 /* "/proc/bus/pci/xx/xx.x" + NUL */ ]; + int fd; + int rc; + + /* Construct filename */ + snprintf ( filename, sizeof ( filename ), "/proc/bus/pci/%02x/%02x.%x", + PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), + PCI_FUNC ( pci->busdevfn ) ); + + /* Open file */ + fd = linux_open ( filename, flags ); + if ( fd < 0 ) { + DBGC ( pci, "PCI could not open %s: %s\n", filename, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_open; + } + + /* Seek to location */ + if ( linux_lseek ( fd, where, SEEK_SET ) < 0 ) { + DBGC ( pci, "PCI could not seek to %s offset %#02lx: %s\n", + filename, where, linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_seek; + } + + return fd; + + err_seek: + linux_close ( fd ); + err_open: + return rc; +} + +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v where Address within configuration space + * @v value Data buffer + * @v len Length to read + * @ret rc Return status code + */ +int linux_pci_read ( struct pci_device *pci, unsigned long where, + unsigned long *value, size_t len ) { + uint32_t tmp = 0; + int fd; + int check_len; + int rc; + + /* Return "missing device" in case of error */ + *value = -1UL; + + /* Open configuration space */ + fd = linux_pci_open ( pci, O_RDONLY, where ); + if ( fd < 0 ) { + rc = fd; + goto err_open; + } + + /* Read value */ + check_len = linux_read ( fd, &tmp, len ); + if ( check_len < 0 ) { + DBGC ( pci, "PCI could not read from " PCI_FMT " %#02lx+%#zx: " + "%s\n", PCI_ARGS ( pci ), where, len, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_read; + } + if ( ( size_t ) check_len != len ) { + DBGC ( pci, "PCI read only %#x bytes from " PCI_FMT + " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), + where, len ); + rc = -EIO; + goto err_read; + } + + /* Return value */ + *value = le32_to_cpu ( tmp ); + + /* Success */ + rc = 0; + + err_read: + linux_close ( fd ); + err_open: + return rc; +} + +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v where Address within configuration space + * @v value Value to write + * @v len Length of value + * @ret rc Return status code + */ +int linux_pci_write ( struct pci_device *pci, unsigned long where, + unsigned long value, size_t len ) { + uint32_t tmp; + int fd; + int check_len; + int rc; + + /* Open configuration space */ + fd = linux_pci_open ( pci, O_WRONLY, where ); + if ( fd < 0 ) { + rc = fd; + goto err_open; + } + + /* Prepare value for writing */ + tmp = cpu_to_le32 ( value ); + assert ( len <= sizeof ( tmp ) ); + + /* Write value */ + check_len = linux_write ( fd, &tmp, len ); + if ( check_len < 0 ) { + DBGC ( pci, "PCI could not write to " PCI_FMT " %#02lx+%#zx: " + "%s\n", PCI_ARGS ( pci ), where, len, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_write; + } + if ( ( size_t ) check_len != len ) { + DBGC ( pci, "PCI wrote only %#x bytes to " PCI_FMT + " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), + where, len ); + rc = -EIO; + goto err_write; + } + + /* Success */ + rc = 0; + + err_write: + linux_close ( fd ); + err_open: + return rc; +} From 258195242b40526a4d2bc930cc425380f337918d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 9 Jul 2013 16:01:52 +0100 Subject: [PATCH 10/65] [settings] Add config/settings.h Move VMWARE_SETTINGS build configuration option from config/sideband.h to a new config/settings.h. Existing instances of config/local/sideband.h will not be affected, since config.c still #includes config/sideband.h. Signed-off-by: Michael Brown --- src/config/config.c | 7 ++++++- src/config/settings.h | 16 ++++++++++++++++ src/config/sideband.h | 1 - 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/config/settings.h diff --git a/src/config/config.c b/src/config/config.c index bd1d9885..15d57d1e 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -293,9 +294,13 @@ REQUIRE_OBJECT ( tap ); REQUIRE_OBJECT ( efi_bofm ); #endif /* BOFM_EFI */ #endif /* CONFIG_BOFM */ + +/* + * Drag in relevant settings sources + */ #ifdef VMWARE_SETTINGS REQUIRE_OBJECT ( guestinfo ); -#endif /* VMWARE_SETTINGS */ +#endif /* * Drag in selected keyboard map diff --git a/src/config/settings.h b/src/config/settings.h new file mode 100644 index 00000000..331c7e3a --- /dev/null +++ b/src/config/settings.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_SETTINGS_H +#define CONFIG_SETTINGS_H + +/** @file + * + * Configuration settings sources + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +//#define VMWARE_SETTINGS /* VMware GuestInfo settings */ + +#include + +#endif /* CONFIG_SETTINGS_H */ diff --git a/src/config/sideband.h b/src/config/sideband.h index 52339993..2e2a8d41 100644 --- a/src/config/sideband.h +++ b/src/config/sideband.h @@ -10,7 +10,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONFIG_BOFM /* IBM's BladeCenter Open Fabric Manager */ -//#define VMWARE_SETTINGS /* VMware GuestInfo settings */ #include From dbfa13ff2cd7eb313d002f185f0274f2ac883231 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 9 Jul 2013 16:03:35 +0100 Subject: [PATCH 11/65] [settings] Expose PCI configuration space via settings mechanism Allow values to be read from PCI configuration space using the syntax ${pci/..} where is the bus:dev.fn address of the PCI device (expressed as a single integer, as returned by ${net0/busloc}), is the offset within PCI configuration space, and is the length within PCI configuration space. Values are returned in reverse byte order, since PCI configuration space is little-endian by definition. Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/settings.h | 1 + src/drivers/bus/pci_settings.c | 124 +++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 129 insertions(+) create mode 100644 src/drivers/bus/pci_settings.c diff --git a/src/config/config.c b/src/config/config.c index 15d57d1e..1de3db43 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -298,6 +298,9 @@ REQUIRE_OBJECT ( efi_bofm ); /* * Drag in relevant settings sources */ +#ifdef PCI_SETTINGS +REQUIRE_OBJECT ( pci_settings ); +#endif #ifdef VMWARE_SETTINGS REQUIRE_OBJECT ( guestinfo ); #endif diff --git a/src/config/settings.h b/src/config/settings.h index 331c7e3a..9c5c2d20 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -9,6 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#define PCI_SETTINGS /* PCI device settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ #include diff --git a/src/drivers/bus/pci_settings.c b/src/drivers/bus/pci_settings.c new file mode 100644 index 00000000..a4d7b933 --- /dev/null +++ b/src/drivers/bus/pci_settings.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include + +/** @file + * + * PCI device settings + * + */ + +/** PCI device settings scope */ +static struct settings_scope pci_settings_scope; + +/** + * Check applicability of PCI device setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int pci_settings_applies ( struct settings *settings __unused, + struct setting *setting ) { + + return ( setting->scope == &pci_settings_scope ); +} + +/** + * Fetch value of PCI device setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int pci_settings_fetch ( struct settings *settings __unused, + struct setting *setting, + void *data, size_t len ) { + struct pci_device pci; + unsigned int tag_busdevfn; + unsigned int tag_offset; + unsigned int tag_len; + unsigned int i; + + /* Extract busdevfn, offset, and length from tag */ + tag_busdevfn = ( ( setting->tag >> 16 ) & 0xffff ); + tag_offset = ( ( setting->tag >> 8 ) & 0xff ); + tag_len = ( ( setting->tag >> 0 ) & 0xff ); + + /* Locate PCI device */ + memset ( &pci, 0, sizeof ( pci ) ); + pci_init ( &pci, tag_busdevfn ); + DBG ( PCI_FMT " reading %#02x+%#x\n", PCI_ARGS ( &pci ), + tag_offset, tag_len ); + + /* Read data one byte at a time, in reverse order (since PCI + * is little-endian and iPXE settings are essentially + * big-endian). + */ + tag_offset += tag_len; + for ( i = 0 ; ( ( i < tag_len ) && ( i < len ) ); i++ ) { + pci_read_config_byte ( &pci, --tag_offset, data++ ); + } + + /* Set type to ":hexraw" if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return tag_len; +} + +/** PCI device settings operations */ +static struct settings_operations pci_settings_operations = { + .applies = pci_settings_applies, + .fetch = pci_settings_fetch, +}; + +/** PCI device settings */ +static struct settings pci_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( pci_settings.siblings ), + .children = LIST_HEAD_INIT ( pci_settings.children ), + .op = &pci_settings_operations, + .default_scope = &pci_settings_scope, +}; + +/** Initialise PCI device settings */ +static void pci_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &pci_settings, NULL, "pci" ) ) != 0 ) { + DBG ( "PCI could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** PCI device settings initialiser */ +struct init_fn pci_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = pci_settings_init, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2b9d16ca..73e1d89f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -276,6 +276,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_efi_timer ( ERRFILE_OTHER | 0x003a0000 ) #define ERRFILE_efi_umalloc ( ERRFILE_OTHER | 0x003b0000 ) #define ERRFILE_linux_pci ( ERRFILE_OTHER | 0x003c0000 ) +#define ERRFILE_pci_settings ( ERRFILE_OTHER | 0x003d0000 ) /** @} */ From 66ea4581256449fe9dcb26340851c09ffd9d6290 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 13 Jul 2013 15:06:20 +0200 Subject: [PATCH 12/65] [settings] Make "netX" settings block function as a symbolic link Add a facility for settings blocks to act as symbolic links to other settings blocks, and reimplement the "netX" virtual settings block using this facility. The primary advantage of this approach is that unscoped settings such as ${mac} and ${filename} will now reflect the settings obtained from the most recently opened network device: in most cases, this will mean the settings obtained from the most recent DHCP attempt. This should improve conformance to the principle of least astonishment. Signed-off-by: Michael Brown --- src/core/settings.c | 73 +++++++++++++++++++++++++------------ src/hci/tui/settings_ui.c | 2 +- src/include/ipxe/settings.h | 7 ++++ src/net/netdev_settings.c | 49 +++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 24 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 927ad845..80cd6a9f 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -271,6 +271,9 @@ struct settings * find_child_settings ( struct settings *parent, const char *name ) { struct settings *settings; + /* Find target parent settings block */ + parent = settings_target ( parent ); + /* Treat empty name as meaning "this block" */ if ( ! *name ) return parent; @@ -278,7 +281,7 @@ struct settings * find_child_settings ( struct settings *parent, /* Look for child with matching name */ list_for_each_entry ( settings, &parent->children, siblings ) { if ( strcmp ( settings->name, name ) == 0 ) - return settings; + return settings_target ( settings ); } return NULL; @@ -299,6 +302,9 @@ static struct settings * autovivify_child_settings ( struct settings *parent, } *new_child; struct settings *settings; + /* Find target parent settings block */ + parent = settings_target ( parent ); + /* Return existing settings, if existent */ if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) return settings; @@ -330,6 +336,10 @@ const char * settings_name ( struct settings *settings ) { static char buf[16]; char tmp[ sizeof ( buf ) ]; + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Construct name */ for ( buf[2] = buf[0] = 0 ; settings ; settings = settings->parent ) { memcpy ( tmp, buf, sizeof ( tmp ) ); snprintf ( buf, sizeof ( buf ), ".%s%s", settings->name, tmp ); @@ -359,20 +369,11 @@ parse_settings_name ( const char *name, /* Parse each name component in turn */ while ( remainder ) { - struct net_device *netdev; - subname = remainder; remainder = strchr ( subname, '.' ); if ( remainder ) *(remainder++) = '\0'; - - /* Special case "netX" root settings block */ - if ( ( subname == name_copy ) && ! strcmp ( subname, "netX" ) && - ( ( netdev = last_opened_netdev() ) != NULL ) ) - settings = get_child ( settings, netdev->name ); - else - settings = get_child ( settings, subname ); - + settings = get_child ( settings, subname ); if ( ! settings ) break; } @@ -460,10 +461,11 @@ int register_settings ( struct settings *settings, struct settings *parent, const char *name ) { struct settings *old_settings; - /* NULL parent => add to settings root */ + /* Sanity check */ assert ( settings != NULL ); - if ( parent == NULL ) - parent = &settings_root; + + /* Find target parent settings block */ + parent = settings_target ( parent ); /* Apply settings block name */ settings->name = name; @@ -523,6 +525,26 @@ void unregister_settings ( struct settings *settings ) { ****************************************************************************** */ +/** + * Redirect to target settings block + * + * @v settings Settings block, or NULL + * @ret settings Underlying settings block + */ +struct settings * settings_target ( struct settings *settings ) { + + /* NULL settings implies the global settings root */ + if ( ! settings ) + settings = &settings_root; + + /* Redirect to underlying settings block, if applicable */ + if ( settings->op->redirect ) + return settings->op->redirect ( settings ); + + /* Otherwise, return this settings block */ + return settings; +} + /** * Check applicability of setting * @@ -532,6 +554,10 @@ void unregister_settings ( struct settings *settings ) { */ int setting_applies ( struct settings *settings, struct setting *setting ) { + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Check applicability of setting */ return ( settings->op->applies ? settings->op->applies ( settings, setting ) : 1 ); } @@ -549,9 +575,8 @@ int store_setting ( struct settings *settings, struct setting *setting, const void *data, size_t len ) { int rc; - /* NULL settings implies storing into the global settings root */ - if ( ! settings ) - settings = &settings_root; + /* Find target settings block */ + settings = settings_target ( settings ); /* Fail if tag does not apply to this settings block */ if ( ! setting_applies ( settings, setting ) ) @@ -609,9 +634,8 @@ static int fetch_setting_and_origin ( struct settings *settings, if ( origin ) *origin = NULL; - /* NULL settings implies starting at the global settings root */ - if ( ! settings ) - settings = &settings_root; + /* Find target settings block */ + settings = settings_target ( settings ); /* Sanity check */ if ( ! settings->op->fetch ) @@ -971,6 +995,11 @@ int fetch_uuid_setting ( struct settings *settings, struct setting *setting, * @v settings Settings block */ void clear_settings ( struct settings *settings ) { + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Clear settings, if applicable */ if ( settings->op->clear ) settings->op->clear ( settings ); } @@ -1230,9 +1259,7 @@ int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { const char *name; - if ( ! settings ) - settings = &settings_root; - + settings = settings_target ( settings ); name = settings_name ( settings ); return snprintf ( buf, len, "%s%s%s:%s", name, ( name[0] ? "/" : "" ), setting->name, setting->type->name ); diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c index 403d1245..eb82ae54 100644 --- a/src/hci/tui/settings_ui.c +++ b/src/hci/tui/settings_ui.c @@ -441,7 +441,7 @@ static void reveal_setting_row ( struct setting_widget *widget, static void init_widget ( struct setting_widget *widget, struct settings *settings ) { - widget->settings = settings; + widget->settings = settings_target ( settings ); widget->num_rows = select_setting_row ( widget, 0 ); widget->first_visible = SETTINGS_LIST_ROWS; draw_title_row ( widget ); diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 8ee9516e..6e75251c 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -77,6 +77,12 @@ struct setting { /** Settings block operations */ struct settings_operations { + /** Redirect to underlying settings block (if applicable) + * + * @v settings Settings block + * @ret settings Underlying settings block + */ + struct settings * ( * redirect ) ( struct settings *settings ); /** Check applicability of setting * * @v settings Settings block @@ -248,6 +254,7 @@ extern int register_settings ( struct settings *settings, struct settings *parent, const char *name ); extern void unregister_settings ( struct settings *settings ); +extern struct settings * settings_target ( struct settings *settings ); extern int setting_applies ( struct settings *settings, struct setting *setting ); extern int store_setting ( struct settings *settings, struct setting *setting, diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 3ea7ace5..72152762 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -295,3 +296,51 @@ struct settings_operations netdev_settings_operations = { .fetch = netdev_fetch, .clear = netdev_clear, }; + +/** + * Redirect "netX" settings block + * + * @v settings Settings block + * @ret settings Underlying settings block + */ +static struct settings * netdev_redirect ( struct settings *settings ) { + struct net_device *netdev; + + /* Redirect to most recently opened network device */ + netdev = last_opened_netdev(); + if ( netdev ) { + return netdev_settings ( netdev ); + } else { + return settings; + } +} + +/** "netX" settings operations */ +static struct settings_operations netdev_redirect_settings_operations = { + .redirect = netdev_redirect, +}; + +/** "netX" settings */ +static struct settings netdev_redirect_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( netdev_redirect_settings.siblings ), + .children = LIST_HEAD_INIT ( netdev_redirect_settings.children ), + .op = &netdev_redirect_settings_operations, +}; + +/** Initialise "netX" settings */ +static void netdev_redirect_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &netdev_redirect_settings, NULL, + "netX" ) ) != 0 ) { + DBG ( "Could not register netX settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** "netX" settings initialiser */ +struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = { + .initialise = netdev_redirect_settings_init, +}; From eba6bb38f2d6642efcb20d43e8b4c6c20406dd32 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 13 Jul 2013 15:22:34 +0200 Subject: [PATCH 13/65] [cmdline] Accept "netX" in iPXE commands Allow any iPXE command expecting a network device name to accept "netX" as a synonym for "most recently opened network device". Signed-off-by: Michael Brown --- src/net/netdevice.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 1191ebc1..5af9c6dc 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -670,6 +670,11 @@ void netdev_irq ( struct net_device *netdev, int enable ) { struct net_device * find_netdev ( const char *name ) { struct net_device *netdev; + /* Allow "netX" shortcut */ + if ( strcmp ( name, "netX" ) == 0 ) + return last_opened_netdev(); + + /* Identify network device by name */ list_for_each_entry ( netdev, &net_devices, list ) { if ( strcmp ( netdev->name, name ) == 0 ) return netdev; From 7016164056fb1065c1379d6ac58d1e9cc475c4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Smidsr=C3=B8d?= Date: Sat, 13 Jul 2013 20:57:39 +0200 Subject: [PATCH 14/65] [settings] Add "version" builtin setting Signed-off-by: Michael Brown --- src/core/settings.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/settings.c b/src/core/settings.c index 80cd6a9f..8cdabe09 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -2176,11 +2177,32 @@ static int platform_fetch ( void *data, size_t len ) { return ( sizeof ( platform ) - 1 /* NUL */ ); } +/** Version setting */ +struct setting version_setting __setting ( SETTING_MISC ) = { + .name = "version", + .description = "Version", + .type = &setting_type_string, + .scope = &builtin_scope, +}; + +/** + * Fetch version setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int version_fetch ( void *data, size_t len ) { + strncpy ( data, product_version, len ); + return ( strlen ( product_version ) ); +} + /** List of built-in setting operations */ static struct builtin_setting_operation builtin_setting_operations[] = { { &errno_setting, errno_fetch }, { &buildarch_setting, buildarch_fetch }, { &platform_setting, platform_fetch }, + { &version_setting, version_fetch }, }; /** From c0d2aebdcfcd34192c9ecfdb1933be6a590b147d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jul 2013 11:37:17 +0200 Subject: [PATCH 15/65] [lotest] Include sequence number within loopback test packets Include a sequence number as the first four bytes of the loopback test packet payload. When a content mismatch occurs, this gives some information about the source of the mismatched packet. Signed-off-by: Michael Brown --- src/usr/lotest.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/usr/lotest.c b/src/usr/lotest.c index c4b0b441..905290a5 100644 --- a/src/usr/lotest.c +++ b/src/usr/lotest.c @@ -175,7 +175,8 @@ static int loopback_wait ( struct net_device *receiver, void *data, */ int loopback_test ( struct net_device *sender, struct net_device *receiver, size_t mtu ) { - uint8_t buf[mtu]; + uint8_t *buf; + uint32_t *seq; struct io_buffer *iobuf; unsigned int i; unsigned int successes; @@ -193,6 +194,14 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, if ( ( rc = iflinkwait ( receiver, LINK_WAIT_MS ) ) != 0 ) return rc; + /* Allocate data buffer */ + if ( mtu < sizeof ( *seq ) ) + mtu = sizeof ( *seq ); + buf = malloc ( mtu ); + if ( ! buf ) + return -ENOMEM; + seq = ( ( void * ) buf ); + /* Print initial statistics */ printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", sender->name, receiver->name, mtu ); @@ -211,17 +220,17 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, printf ( "\r%d", successes ); /* Generate random packet */ - for ( i = 0 ; i < sizeof ( buf ) ; i++ ) + *seq = htonl ( successes ); + for ( i = sizeof ( *seq ) ; i < mtu ; i++ ) buf[i] = random(); - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( buf ) ); + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + mtu ); if ( ! iobuf ) { printf ( "\nFailed to allocate I/O buffer" ); rc = -ENOMEM; break; } iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - memcpy ( iob_put ( iobuf, sizeof ( buf ) ), - buf, sizeof ( buf ) ); + memcpy ( iob_put ( iobuf, mtu ), buf, mtu ); /* Transmit packet */ if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, @@ -233,10 +242,8 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, } /* Wait for received packet */ - if ( ( rc = loopback_wait ( receiver, buf, - sizeof ( buf ) ) ) != 0 ) { + if ( ( rc = loopback_wait ( receiver, buf, mtu ) ) != 0 ) break; - } } printf ( "\n"); @@ -246,5 +253,8 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, ifstat ( sender ); ifstat ( receiver ); + /* Free buffer */ + free ( buf ); + return 0; } From ad4f58d410de36b2c2ceb560167b16f53a3de5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Jamr=C3=B3z?= Date: Sat, 13 Jul 2013 16:11:50 +0200 Subject: [PATCH 16/65] [rhine] Rewrite VIA Rhine driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the old via-rhine driver with a new version using the iPXE API. Includes fixes by Thomas Miletich for: - MMIO access - Link detection - RX completion in RX overflow case - Reset and EEPROM reloading - CRC stripping - Missing cpu_to_le32() calls - Missing memory barriers Signed-off-by: Adrian Jamróz Modified-by: Thomas Miletich Tested-by: Thomas Miletich Tested-by: Robin Smidsrød Modified-by: Michael Brown Tested-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/rhine.c | 787 +++++++++++++++++++ src/drivers/net/rhine.h | 250 ++++++ src/drivers/net/via-rhine.c | 1447 ----------------------------------- src/include/ipxe/errfile.h | 2 +- 4 files changed, 1038 insertions(+), 1448 deletions(-) create mode 100644 src/drivers/net/rhine.c create mode 100644 src/drivers/net/rhine.h delete mode 100644 src/drivers/net/via-rhine.c diff --git a/src/drivers/net/rhine.c b/src/drivers/net/rhine.c new file mode 100644 index 00000000..42bc124e --- /dev/null +++ b/src/drivers/net/rhine.c @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2012 Adrian Jamroz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rhine.h" + +/** @file + * + * VIA Rhine network driver + * + */ + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr; + + DBGC2 ( rhn, "RHINE %p MII read reg %d\n", rhn, reg ); + + /* Initiate read */ + writeb ( reg, rhn->regs + RHINE_MII_ADDR ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + writeb ( ( cr | RHINE_MII_CR_RDEN ), rhn->regs + RHINE_MII_CR ); + + /* Wait for read to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + if ( ! ( cr & RHINE_MII_CR_RDEN ) ) + return readw ( rhn->regs + RHINE_MII_RDWR ); + } + + DBGC ( rhn, "RHINE %p MII read timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int rhine_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr; + + DBGC2 ( rhn, "RHINE %p MII write reg %d data 0x%04x\n", + rhn, reg, data ); + + /* Initiate write */ + writeb ( reg, rhn->regs + RHINE_MII_ADDR ); + writew ( data, rhn->regs + RHINE_MII_RDWR ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + writeb ( ( cr | RHINE_MII_CR_WREN ), rhn->regs + RHINE_MII_CR ); + + /* Wait for write to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + if ( ! ( cr & RHINE_MII_CR_WREN ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p MII write timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** Rhine MII operations */ +static struct mii_operations rhine_mii_operations = { + .read = rhine_mii_read, + .write = rhine_mii_write, +}; + +/** + * Enable auto-polling + * + * @v rhn Rhine device + * @ret rc Return status code + * + * This is voodoo. There seems to be no documentation on exactly what + * we are waiting for, or why we have to do anything other than simply + * turn the feature on. + */ +static int rhine_mii_autopoll ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t addr; + + /* Initiate auto-polling */ + writeb ( MII_BMSR, rhn->regs + RHINE_MII_ADDR ); + writeb ( RHINE_MII_CR_AUTOPOLL, rhn->regs + RHINE_MII_CR ); + + /* Wait for auto-polling to complete */ + while ( timeout-- ) { + udelay ( 1 ); + addr = readb ( rhn->regs + RHINE_MII_ADDR ); + if ( ! ( addr & RHINE_MII_ADDR_MDONE ) ) { + writeb ( ( MII_BMSR | RHINE_MII_ADDR_MSRCEN ), + rhn->regs + RHINE_MII_ADDR ); + return 0; + } + } + + DBGC ( rhn, "RHINE %p MII auto-poll timeout\n", rhn ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v rhn Rhine device + * @ret rc Return status code + * + * We're using PIO because this might reset the MMIO enable bit. + */ +static int rhine_reset ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr1; + + DBGC ( rhn, "RHINE %p reset\n", rhn ); + + /* Initiate reset */ + outb ( RHINE_CR1_RESET, rhn->ioaddr + RHINE_CR1 ); + + /* Wait for reset to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr1 = inb ( rhn->ioaddr + RHINE_CR1 ); + if ( ! ( cr1 & RHINE_CR1_RESET ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p reset timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** + * Enable MMIO register access + * + * @v rhn Rhine device + * @v revision Card revision + */ +static void rhine_enable_mmio ( struct rhine_nic *rhn, int revision ) { + uint8_t conf; + + if ( revision < RHINE_REVISION_OLD ) { + conf = inb ( rhn->ioaddr + RHINE_CHIPCFG_A ); + outb ( ( conf | RHINE_CHIPCFG_A_MMIO ), + rhn->ioaddr + RHINE_CHIPCFG_A ); + } else { + conf = inb ( rhn->ioaddr + RHINE_CHIPCFG_D ); + outb ( ( conf | RHINE_CHIPCFG_D_MMIO ), + rhn->ioaddr + RHINE_CHIPCFG_D ); + } +} + +/** + * Reload EEPROM contents + * + * @v rhn Rhine device + * @ret rc Return status code + * + * We're using PIO because this might reset the MMIO enable bit. + */ +static int rhine_reload_eeprom ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t eeprom; + + /* Initiate reload */ + eeprom = inb ( rhn->ioaddr + RHINE_EEPROM_CTRL ); + outb ( ( eeprom | RHINE_EEPROM_CTRL_RELOAD ), + rhn->ioaddr + RHINE_EEPROM_CTRL ); + + /* Wait for reload to complete */ + while ( timeout-- ) { + udelay ( 1 ); + eeprom = inb ( rhn->ioaddr + RHINE_EEPROM_CTRL ); + if ( ! ( eeprom & RHINE_EEPROM_CTRL_RELOAD ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p EEPROM reload timeout\n", rhn ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void rhine_check_link ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + uint8_t mii_sr; + + /* Read MII status register */ + mii_sr = readb ( rhn->regs + RHINE_MII_SR ); + DBGC ( rhn, "RHINE %p link status %02x\n", rhn, mii_sr ); + + /* Report link state */ + if ( ! ( mii_sr & RHINE_MII_SR_LINKPOLL ) ) { + netdev_link_up ( netdev ); + } else if ( mii_sr & RHINE_MII_SR_PHYERR ) { + netdev_link_err ( netdev, -EIO ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v rhn Rhine device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int rhine_create_ring ( struct rhine_nic *rhn, + struct rhine_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + struct rhine_descriptor *next; + physaddr_t address; + unsigned int i; + + /* Allocate descriptors */ + ring->desc = malloc_dma ( len, RHINE_RING_ALIGN ); + if ( ! ring->desc ) + return -ENOMEM; + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + next = &ring->desc[ ( i + 1 ) % ring->count ]; + ring->desc[i].next = cpu_to_le32 ( virt_to_bus ( next ) ); + } + + /* Program ring address */ + address = virt_to_bus ( ring->desc ); + writel ( address, rhn->regs + ring->reg ); + + DBGC ( rhn, "RHINE %p ring %02x is at [%08llx,%08llx)\n", + rhn, ring->reg, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + + return 0; +} + +/** + * Destroy descriptor ring + * + * @v rhn Rhine device + * @v ring Descriptor ring + */ +static void rhine_destroy_ring ( struct rhine_nic *rhn, + struct rhine_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writel ( 0, rhn->regs + ring->reg ); + + /* Free descriptor ring */ + free_dma ( ring->desc, len ); + ring->desc = NULL; + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill RX descriptor ring + * + * @v rhn Rhine device + */ +static void rhine_refill_rx ( struct rhine_nic *rhn ) { + struct rhine_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + + while ( ( rhn->rx.prod - rhn->rx.cons ) < RHINE_RXDESC_NUM ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( RHINE_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + return; + } + + /* Populate next receive descriptor */ + rx_idx = ( rhn->rx.prod++ % RHINE_RXDESC_NUM ); + desc = &rhn->rx.desc[rx_idx]; + address = virt_to_bus ( iobuf->data ); + desc->buffer = cpu_to_le32 ( address ); + desc->des1 = + cpu_to_le32 ( RHINE_DES1_SIZE ( RHINE_RX_MAX_LEN - 1) | + RHINE_DES1_CHAIN | RHINE_DES1_IC ); + wmb(); + desc->des0 = cpu_to_le32 ( RHINE_DES0_OWN ); + + /* Record I/O buffer */ + rhn->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( rhn, "RHINE %p RX %d is [%llx,%llx)\n", rhn, rx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + RHINE_RX_MAX_LEN ) ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rhine_open ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + int rc; + + /* Create transmit ring */ + if ( ( rc = rhine_create_ring ( rhn, &rhn->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive ring */ + if ( ( rc = rhine_create_ring ( rhn, &rhn->rx ) ) != 0 ) + goto err_create_rx; + + /* Set receive configuration */ + writeb ( ( RHINE_RCR_PHYS_ACCEPT | RHINE_RCR_BCAST_ACCEPT | + RHINE_RCR_RUNT_ACCEPT ), rhn->regs + RHINE_RCR ); + + /* Enable link status monitoring */ + if ( ( rc = rhine_mii_autopoll ( rhn ) ) != 0 ) + goto err_mii_autopoll; + + /* Some cards need an extra delay(observed with VT6102) */ + mdelay ( 10 ); + + /* Enable RX/TX of packets */ + writeb ( ( RHINE_CR0_STARTNIC | RHINE_CR0_RXEN | RHINE_CR0_TXEN ), + rhn->regs + RHINE_CR0 ); + + /* Enable auto polling and full duplex operation */ + rhn->cr1 = RHINE_CR1_FDX; + writeb ( rhn->cr1, rhn->regs + RHINE_CR1 ); + + /* Refill RX ring */ + rhine_refill_rx ( rhn ); + + /* Update link state */ + rhine_check_link ( netdev ); + + return 0; + + err_mii_autopoll: + rhine_destroy_ring ( rhn, &rhn->rx ); + err_create_rx: + rhine_destroy_ring ( rhn, &rhn->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rhine_close ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + unsigned int i; + + /* Disable interrupts */ + writeb ( 0, RHINE_IMR0 ); + writeb ( 0, RHINE_IMR1 ); + + /* Stop card, clear RXON and TXON bits */ + writeb ( RHINE_CR0_STOPNIC, rhn->regs + RHINE_CR0 ); + + /* Destroy receive ring */ + rhine_destroy_ring ( rhn, &rhn->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < RHINE_RXDESC_NUM ; i++ ) { + if ( rhn->rx_iobuf[i] ) + free_iob ( rhn->rx_iobuf[i] ); + rhn->rx_iobuf[i] = NULL; + } + + /* Destroy transmit ring */ + rhine_destroy_ring ( rhn, &rhn->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rhine_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + physaddr_t address; + unsigned int tx_idx; + + /* Get next transmit descriptor */ + if ( ( rhn->tx.prod - rhn->tx.cons ) >= RHINE_TXDESC_NUM ) + return -ENOBUFS; + tx_idx = ( rhn->tx.prod++ % RHINE_TXDESC_NUM ); + desc = &rhn->tx.desc[tx_idx]; + + /* Pad and align packet */ + iob_pad ( iobuf, ETH_ZLEN ); + address = virt_to_bus ( iobuf->data ); + + /* Populate transmit descriptor */ + desc->buffer = cpu_to_le32 ( address ); + desc->des1 = cpu_to_le32 ( RHINE_DES1_IC | RHINE_TDES1_STP | + RHINE_TDES1_EDP | RHINE_DES1_CHAIN | + RHINE_DES1_SIZE ( iob_len ( iobuf ) ) ); + wmb(); + desc->des0 = cpu_to_le32 ( RHINE_DES0_OWN ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writeb ( ( rhn->cr1 | RHINE_CR1_TXPOLL ), rhn->regs + RHINE_CR1 ); + + DBGC2 ( rhn, "RHINE %p TX %d is [%llx,%llx)\n", rhn, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void rhine_poll_tx ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + unsigned int tx_idx; + uint32_t des0; + + /* Check for completed packets */ + while ( rhn->tx.cons != rhn->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( rhn->tx.cons % RHINE_TXDESC_NUM ); + desc = &rhn->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( desc->des0 & cpu_to_le32 ( RHINE_DES0_OWN ) ) + return; + + /* Complete TX descriptor */ + des0 = le32_to_cpu ( desc->des0 ); + if ( des0 & RHINE_TDES0_TERR ) { + DBGC ( rhn, "RHINE %p TX %d error (DES0 %08x)\n", + rhn, tx_idx, des0 ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } else { + DBGC2 ( rhn, "RHINE %p TX %d complete\n", rhn, tx_idx ); + netdev_tx_complete_next ( netdev ); + } + rhn->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void rhine_poll_rx ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + uint32_t des0; + size_t len; + + /* Check for received packets */ + while ( rhn->rx.cons != rhn->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( rhn->rx.cons % RHINE_RXDESC_NUM ); + desc = &rhn->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( desc->des0 & cpu_to_le32 ( RHINE_DES0_OWN ) ) + return; + + /* Populate I/O buffer */ + iobuf = rhn->rx_iobuf[rx_idx]; + rhn->rx_iobuf[rx_idx] = NULL; + des0 = le32_to_cpu ( desc->des0 ); + len = ( RHINE_DES0_GETSIZE ( des0 ) - 4 /* strip CRC */ ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( des0 & RHINE_RDES0_RXOK ) { + DBGC2 ( rhn, "RHINE %p RX %d complete (length %zd)\n", + rhn, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } else { + DBGC ( rhn, "RHINE %p RX %d error (length %zd, DES0 " + "%08x)\n", rhn, rx_idx, len, des0 ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } + rhn->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rhine_poll ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + uint8_t isr0; + uint8_t isr1; + + /* Read and acknowledge interrupts */ + isr0 = readb ( rhn->regs + RHINE_ISR0 ); + isr1 = readb ( rhn->regs + RHINE_ISR1 ); + if ( isr0 ) + writeb ( isr0, rhn->regs + RHINE_ISR0 ); + if ( isr1 ) + writeb ( isr1, rhn->regs + RHINE_ISR1 ); + + /* Report unexpected errors */ + if ( ( isr0 & ( RHINE_ISR0_MIBOVFL | RHINE_ISR0_PCIERR | + RHINE_ISR0_RXRINGERR | RHINE_ISR0_TXRINGERR ) ) || + ( isr1 & ( RHINE_ISR1_GPI | RHINE_ISR1_TXABORT | + RHINE_ISR1_RXFIFOOVFL | RHINE_ISR1_RXFIFOUNFL | + RHINE_ISR1_TXFIFOUNFL ) ) ) { + DBGC ( rhn, "RHINE %p unexpected ISR0 %02x ISR1 %02x\n", + rhn, isr0, isr1 ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -EIO ); + } + + /* Poll for TX completions, if applicable */ + if ( isr0 & ( RHINE_ISR0_TXDONE | RHINE_ISR0_TXERR ) ) + rhine_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( isr0 & ( RHINE_ISR0_RXDONE | RHINE_ISR0_RXERR ) ) + rhine_poll_rx ( netdev ); + + /* Handle RX buffer exhaustion */ + if ( isr1 & RHINE_ISR1_RXNOBUF ) { + rhine_poll_rx ( netdev ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + } + + /* Check link state, if applicable */ + if ( isr1 & RHINE_ISR1_PORTSTATE ) + rhine_check_link ( netdev ); + + /* Refill RX ring */ + rhine_refill_rx ( rhn ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void rhine_irq ( struct net_device *netdev, int enable ) { + struct rhine_nic *nic = netdev->priv; + + if ( enable ) { + /* Enable interrupts */ + writeb ( 0xff, nic->regs + RHINE_IMR0 ); + writeb ( 0xff, nic->regs + RHINE_IMR1 ); + } else { + /* Disable interrupts */ + writeb ( 0, nic->regs + RHINE_IMR0 ); + writeb ( 0, nic->regs + RHINE_IMR1 ); + } +} + +/** Rhine network device operations */ +static struct net_device_operations rhine_operations = { + .open = rhine_open, + .close = rhine_close, + .transmit = rhine_transmit, + .poll = rhine_poll, + .irq = rhine_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int rhine_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct rhine_nic *rhn; + uint8_t revision; + unsigned int i; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *rhn ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &rhine_operations ); + rhn = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( rhn, 0, sizeof ( *rhn ) ); + rhine_init_ring ( &rhn->tx, RHINE_TXDESC_NUM, RHINE_TXQUEUE_BASE ); + rhine_init_ring ( &rhn->rx, RHINE_RXDESC_NUM, RHINE_RXQUEUE_BASE ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + rhn->regs = ioremap ( pci->membase, RHINE_BAR_SIZE ); + rhn->ioaddr = pci->ioaddr; + DBGC ( rhn, "RHINE %p regs at %08lx, I/O at %04lx\n", rhn, + pci->membase, pci->ioaddr ); + + /* Reset the NIC */ + if ( ( rc = rhine_reset ( rhn ) ) != 0 ) + goto err_reset; + + /* Reload EEPROM */ + if ( ( rc = rhine_reload_eeprom ( rhn ) ) != 0 ) + goto err_reload_eeprom; + + /* Read card revision and enable MMIO */ + pci_read_config_byte ( pci, PCI_REVISION, &revision ); + DBGC ( rhn, "RHINE %p revision %#02x detected\n", rhn, revision ); + rhine_enable_mmio ( rhn, revision ); + + /* Read MAC address */ + for ( i = 0 ; i < ETH_ALEN ; i++ ) + netdev->hw_addr[i] = readb ( rhn->regs + RHINE_MAC + i ); + + /* Initialise and reset MII interface */ + mii_init ( &rhn->mii, &rhine_mii_operations ); + if ( ( rc = mii_reset ( &rhn->mii ) ) != 0 ) { + DBGC ( rhn, "RHINE %p could not reset MII: %s\n", + rhn, strerror ( rc ) ); + goto err_mii_reset; + } + DBGC ( rhn, "RHINE PHY vendor %04x device %04x\n", + rhine_mii_read ( &rhn->mii, 0x02 ), + rhine_mii_read ( &rhn->mii, 0x03 ) ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + rhine_check_link ( netdev ); + + return 0; + + err_register_netdev: + err_mii_reset: + err_reload_eeprom: + rhine_reset ( rhn ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void rhine_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct rhine_nic *nic = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + rhine_reset ( nic ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Rhine PCI device IDs */ +static struct pci_device_id rhine_nics[] = { + PCI_ROM ( 0x1106, 0x3065, "dlink-530tx", "VIA VT6102", 0 ), + PCI_ROM ( 0x1106, 0x3106, "vt6105", "VIA VT6105", 0 ), + PCI_ROM ( 0x1106, 0x3043, "dlink-530tx-old", "VIA VT3043", 0 ), + PCI_ROM ( 0x1106, 0x3053, "vt6105m", "VIA VT6105M", 0 ), + PCI_ROM ( 0x1106, 0x6100, "via-rhine-old", "VIA 86C100A", 0 ) +}; + +/** Rhine PCI driver */ +struct pci_driver rhine_driver __pci_driver = { + .ids = rhine_nics, + .id_count = ( sizeof ( rhine_nics ) / sizeof ( rhine_nics[0] ) ), + .probe = rhine_probe, + .remove = rhine_remove, +}; diff --git a/src/drivers/net/rhine.h b/src/drivers/net/rhine.h new file mode 100644 index 00000000..b26f9ae7 --- /dev/null +++ b/src/drivers/net/rhine.h @@ -0,0 +1,250 @@ +#ifndef _RHINE_H +#define _RHINE_H + +/** @file + * + * VIA Rhine network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Rhine BAR size */ +#define RHINE_BAR_SIZE 256 + +/** Default timeout */ +#define RHINE_TIMEOUT_US 10000 + +/** Rhine descriptor format */ +struct rhine_descriptor { + uint32_t des0; + uint32_t des1; + uint32_t buffer; + uint32_t next; +} __attribute__ (( packed )); + +#define RHINE_DES0_OWN (1 << 31) /*< Owned descriptor */ +#define RHINE_DES1_IC (1 << 23) /*< Generate interrupt */ +#define RHINE_TDES1_EDP (1 << 22) /*< End of packet */ +#define RHINE_TDES1_STP (1 << 21) /*< Start of packet */ +#define RHINE_TDES1_TCPCK (1 << 20) /*< HW TCP checksum */ +#define RHINE_TDES1_UDPCK (1 << 19) /*< HW UDP checksum */ +#define RHINE_TDES1_IPCK (1 << 18) /*< HW IP checksum */ +#define RHINE_TDES1_TAG (1 << 17) /*< Tagged frame */ +#define RHINE_TDES1_CRC (1 << 16) /*< No CRC */ +#define RHINE_DES1_CHAIN (1 << 15) /*< Chained descriptor */ +#define RHINE_DES1_SIZE(_x) ((_x) & 0x7ff) /*< Frame size */ +#define RHINE_DES0_GETSIZE(_x) (((_x) >> 16) & 0x7ff) + +#define RHINE_RDES0_RXOK (1 << 15) +#define RHINE_RDES0_VIDHIT (1 << 14) +#define RHINE_RDES0_MAR (1 << 13) +#define RHINE_RDES0_BAR (1 << 12) +#define RHINE_RDES0_PHY (1 << 11) +#define RHINE_RDES0_CHN (1 << 10) +#define RHINE_RDES0_STP (1 << 9) +#define RHINE_RDES0_EDP (1 << 8) +#define RHINE_RDES0_BUFF (1 << 7) +#define RHINE_RDES0_FRAG (1 << 6) +#define RHINE_RDES0_RUNT (1 << 5) +#define RHINE_RDES0_LONG (1 << 4) +#define RHINE_RDES0_FOV (1 << 3) +#define RHINE_RDES0_FAE (1 << 2) +#define RHINE_RDES0_CRCE (1 << 1) +#define RHINE_RDES0_RERR (1 << 0) + +#define RHINE_TDES0_TERR (1 << 15) +#define RHINE_TDES0_UDF (1 << 11) +#define RHINE_TDES0_CRS (1 << 10) +#define RHINE_TDES0_OWC (1 << 9) +#define RHINE_TDES0_ABT (1 << 8) +#define RHINE_TDES0_CDH (1 << 7) +#define RHINE_TDES0_COLS (1 << 4) +#define RHINE_TDES0_NCR(_x) ((_x) & 0xf) + +#define RHINE_RING_ALIGN 4 + +/** Rhine descriptor rings sizes */ +#define RHINE_RXDESC_NUM 4 +#define RHINE_TXDESC_NUM 8 +#define RHINE_RX_MAX_LEN 1536 + +/** Rhine MAC address registers */ +#define RHINE_MAC 0x00 + +/** Receive control register */ +#define RHINE_RCR 0x06 +#define RHINE_RCR_FIFO_TRSH(_x) (((_x) & 0x7) << 5) /*< RX FIFO threshold */ +#define RHINE_RCR_PHYS_ACCEPT (1 << 4) /*< Accept matching PA */ +#define RHINE_RCR_BCAST_ACCEPT (1 << 3) /*< Accept broadcast */ +#define RHINE_RCR_MCAST_ACCEPT (1 << 2) /*< Accept multicast */ +#define RHINE_RCR_RUNT_ACCEPT (1 << 1) /*< Accept runt frames */ +#define RHINE_RCR_ERR_ACCEPT (1 << 0) /*< Accept erroneous frames */ + +/** Transmit control register */ +#define RHINE_TCR 0x07 +#define RHINE_TCR_LOOPBACK(_x) (((_x) & 0x3) << 1) /*< Transmit loop mode */ +#define RHINE_TCR_TAGGING (1 << 0) /*< 802.1P/Q packet tagging */ + +/** Command 0 register */ +#define RHINE_CR0 0x08 +#define RHINE_CR0_RXSTART (1 << 6) +#define RHINE_CR0_TXSTART (1 << 5) +#define RHINE_CR0_TXEN (1 << 4) /*< Transmit enable */ +#define RHINE_CR0_RXEN (1 << 3) /*< Receive enable */ +#define RHINE_CR0_STOPNIC (1 << 2) /*< Stop NIC */ +#define RHINE_CR0_STARTNIC (1 << 1) /*< Start NIC */ + +/** Command 1 register */ +#define RHINE_CR1 0x09 +#define RHINE_CR1_RESET (1 << 7) /*< Software reset */ +#define RHINE_CR1_RXPOLL (1 << 6) /*< Receive poll demand */ +#define RHINE_CR1_TXPOLL (1 << 5) /*< Xmit poll demand */ +#define RHINE_CR1_AUTOPOLL (1 << 3) /*< Disable autopoll */ +#define RHINE_CR1_FDX (1 << 2) /*< Full duplex */ +#define RIHNE_CR1_ACCUNI (1 << 1) /*< Disable accept unicast */ + +/** Transmit queue wake register */ +#define RHINE_TXQUEUE_WAKE 0x0a + +/** Interrupt service 0 */ +#define RHINE_ISR0 0x0c +#define RHINE_ISR0_MIBOVFL (1 << 7) +#define RHINE_ISR0_PCIERR (1 << 6) +#define RHINE_ISR0_RXRINGERR (1 << 5) +#define RHINE_ISR0_TXRINGERR (1 << 4) +#define RHINE_ISR0_TXERR (1 << 3) +#define RHINE_ISR0_RXERR (1 << 2) +#define RHINE_ISR0_TXDONE (1 << 1) +#define RHINE_ISR0_RXDONE (1 << 0) + +/** Interrupt service 1 */ +#define RHINE_ISR1 0x0d +#define RHINE_ISR1_GPI (1 << 7) +#define RHINE_ISR1_PORTSTATE (1 << 6) +#define RHINE_ISR1_TXABORT (1 << 5) +#define RHINE_ISR1_RXNOBUF (1 << 4) +#define RHINE_ISR1_RXFIFOOVFL (1 << 3) +#define RHINE_ISR1_RXFIFOUNFL (1 << 2) +#define RHINE_ISR1_TXFIFOUNFL (1 << 1) +#define RHINE_ISR1_EARLYRX (1 << 0) + +/** Interrupt enable mask register 0 */ +#define RHINE_IMR0 0x0e + +/** Interrupt enable mask register 1 */ +#define RHINE_IMR1 0x0f + +/** RX queue descriptor base address */ +#define RHINE_RXQUEUE_BASE 0x18 + +/** TX queue 0 descriptor base address */ +#define RHINE_TXQUEUE_BASE 0x1c + +/** MII configuration */ +#define RHINE_MII_CFG 0x6c + +/** MII status register */ +#define RHINE_MII_SR 0x6d +#define RHINE_MII_SR_PHYRST (1 << 7) /*< PHY reset */ +#define RHINE_MII_SR_LINKNWAY (1 << 4) /*< Link status after N-Way */ +#define RHINE_MII_SR_PHYERR (1 << 3) /*< PHY device error */ +#define RHINE_MII_SR_DUPLEX (1 << 2) /*< Duplex mode after N-Way */ +#define RHINE_MII_SR_LINKPOLL (1 << 1) /*< Link status after poll */ +#define RHINE_MII_SR_LINKSPD (1 << 0) /*< Link speed after N-Way */ + +/** MII bus control 0 register */ +#define RHINE_MII_BCR0 0x6e + +/** MII bus control 1 register */ +#define RHINE_MII_BCR1 0x6f + +/** MII control register */ +#define RHINE_MII_CR 0x70 +#define RHINE_MII_CR_AUTOPOLL (1 << 7) /*< MII auto polling */ +#define RHINE_MII_CR_RDEN (1 << 6) /*< PHY read enable */ +#define RHINE_MII_CR_WREN (1 << 5) /*< PHY write enable */ +#define RHINE_MII_CR_DIRECT (1 << 4) /*< Direct programming mode */ +#define RHINE_MII_CR_MDIOOUT (1 << 3) /*< MDIO output enable */ + +/** MII port address */ +#define RHINE_MII_ADDR 0x71 +#define RHINE_MII_ADDR_MSRCEN (1 << 6) +#define RHINE_MII_ADDR_MDONE (1 << 5) + +/** MII read/write data */ +#define RHINE_MII_RDWR 0x72 + +/** EERPOM control/status register */ +#define RHINE_EEPROM_CTRL 0x74 +#define RHINE_EEPROM_CTRL_STATUS (1 << 7) /*< EEPROM status */ +#define RHINE_EEPROM_CTRL_RELOAD (1 << 5) /*< EEPROM reload */ + +/** Chip configuration A */ +#define RHINE_CHIPCFG_A 0x78 +/* MMIO enable. Only valid for Rhine I. Reserved on later boards */ +#define RHINE_CHIPCFG_A_MMIO (1 << 5) + +/** Chip configuration B */ +#define RHINE_CHIPCFG_B 0x79 + +/** Chip configuation C */ +#define RHINE_CHIPCFG_C 0x7a + +/** Chip configuration D */ +#define RHINE_CHIPCFG_D 0x7b +/* MMIO enable. Only valid on Rhine II and later. GPIOEN on Rhine I */ +#define RHINE_CHIPCFG_D_MMIO (1 << 7) + +#define RHINE_REVISION_OLD 0x20 + +/** A VIA Rhine descriptor ring */ +struct rhine_ring { + /** Descriptors */ + struct rhine_descriptor *desc; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Register address */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors (must be a power of 2) + * @v reg Register address + */ +static inline __attribute__ (( always_inline)) void +rhine_init_ring ( struct rhine_ring *ring, unsigned int count, + unsigned int reg ) { + ring->count = count; + ring->reg = reg; +} + +/** A VIA Rhine network card */ +struct rhine_nic { + /** I/O address (some PIO access is always required) */ + unsigned long ioaddr; + /** Registers */ + void *regs; + /** Cached value of CR1 (to avoid read-modify-write on fast path) */ + uint8_t cr1; + + /** MII interface */ + struct mii_interface mii; + + /** Transmit descriptor ring */ + struct rhine_ring tx; + /** Receive descriptor ring */ + struct rhine_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[RHINE_RXDESC_NUM]; +}; + +#endif /* _RHINE_H */ diff --git a/src/drivers/net/via-rhine.c b/src/drivers/net/via-rhine.c deleted file mode 100644 index f3bb4e01..00000000 --- a/src/drivers/net/via-rhine.c +++ /dev/null @@ -1,1447 +0,0 @@ -/* rhine.c:Fast Ethernet driver for Linux. */ -/* - Adapted 09-jan-2000 by Paolo Marini (paolom@prisma-eng.it) - - originally written by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU Public License (GPL), incorporated herein by reference. - Drivers derived from this code also fall under the GPL and must retain - this authorship and copyright notice. - - Under no circumstances are the authors responsible for - the proper functioning of this software, nor do the authors assume any - responsibility for damages incurred with its use. - - This driver is designed for the VIA VT86C100A Rhine-II PCI Fast Ethernet - controller. - -*/ - -static const char *version = "rhine.c v1.0.2 2004-10-29\n"; - -/* A few user-configurable values. */ - -// max time out delay time -#define W_MAX_TIMEOUT 0x0FFFU - -/* Size of the in-memory receive ring. */ -#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ -#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE 1536 -#define RX_BUF_SIZE 1536 - -/* PCI Tuning Parameters - Threshold is bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */ -#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ -#define TX_DMA_BURST 4 - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((2000*HZ)/1000) - -#include "etherboot.h" -#include "nic.h" -#include -#include - -/* define all ioaddr */ - -#define byPAR0 ioaddr -#define byRCR ioaddr + 6 -#define byTCR ioaddr + 7 -#define byCR0 ioaddr + 8 -#define byCR1 ioaddr + 9 -#define byISR0 ioaddr + 0x0c -#define byISR1 ioaddr + 0x0d -#define byIMR0 ioaddr + 0x0e -#define byIMR1 ioaddr + 0x0f -#define byMAR0 ioaddr + 0x10 -#define byMAR1 ioaddr + 0x11 -#define byMAR2 ioaddr + 0x12 -#define byMAR3 ioaddr + 0x13 -#define byMAR4 ioaddr + 0x14 -#define byMAR5 ioaddr + 0x15 -#define byMAR6 ioaddr + 0x16 -#define byMAR7 ioaddr + 0x17 -#define dwCurrentRxDescAddr ioaddr + 0x18 -#define dwCurrentTxDescAddr ioaddr + 0x1c -#define dwCurrentRDSE0 ioaddr + 0x20 -#define dwCurrentRDSE1 ioaddr + 0x24 -#define dwCurrentRDSE2 ioaddr + 0x28 -#define dwCurrentRDSE3 ioaddr + 0x2c -#define dwNextRDSE0 ioaddr + 0x30 -#define dwNextRDSE1 ioaddr + 0x34 -#define dwNextRDSE2 ioaddr + 0x38 -#define dwNextRDSE3 ioaddr + 0x3c -#define dwCurrentTDSE0 ioaddr + 0x40 -#define dwCurrentTDSE1 ioaddr + 0x44 -#define dwCurrentTDSE2 ioaddr + 0x48 -#define dwCurrentTDSE3 ioaddr + 0x4c -#define dwNextTDSE0 ioaddr + 0x50 -#define dwNextTDSE1 ioaddr + 0x54 -#define dwNextTDSE2 ioaddr + 0x58 -#define dwNextTDSE3 ioaddr + 0x5c -#define dwCurrRxDMAPtr ioaddr + 0x60 -#define dwCurrTxDMAPtr ioaddr + 0x64 -#define byMPHY ioaddr + 0x6c -#define byMIISR ioaddr + 0x6d -#define byBCR0 ioaddr + 0x6e -#define byBCR1 ioaddr + 0x6f -#define byMIICR ioaddr + 0x70 -#define byMIIAD ioaddr + 0x71 -#define wMIIDATA ioaddr + 0x72 -#define byEECSR ioaddr + 0x74 -#define byTEST ioaddr + 0x75 -#define byGPIO ioaddr + 0x76 -#define byCFGA ioaddr + 0x78 -#define byCFGB ioaddr + 0x79 -#define byCFGC ioaddr + 0x7a -#define byCFGD ioaddr + 0x7b -#define wTallyCntMPA ioaddr + 0x7c -#define wTallyCntCRC ioaddr + 0x7d -#define bySTICKHW ioaddr + 0x83 -#define byWOLcrClr ioaddr + 0xA4 -#define byWOLcgClr ioaddr + 0xA7 -#define byPwrcsrClr ioaddr + 0xAC - -/*--------------------- Exioaddr Definitions -------------------------*/ - -/* - * Bits in the RCR register - */ - -#define RCR_RRFT2 0x80 -#define RCR_RRFT1 0x40 -#define RCR_RRFT0 0x20 -#define RCR_PROM 0x10 -#define RCR_AB 0x08 -#define RCR_AM 0x04 -#define RCR_AR 0x02 -#define RCR_SEP 0x01 - -/* - * Bits in the TCR register - */ - -#define TCR_RTSF 0x80 -#define TCR_RTFT1 0x40 -#define TCR_RTFT0 0x20 -#define TCR_OFSET 0x08 -#define TCR_LB1 0x04 /* loopback[1] */ -#define TCR_LB0 0x02 /* loopback[0] */ - -/* - * Bits in the CR0 register - */ - -#define CR0_RDMD 0x40 /* rx descriptor polling demand */ -#define CR0_TDMD 0x20 /* tx descriptor polling demand */ -#define CR0_TXON 0x10 -#define CR0_RXON 0x08 -#define CR0_STOP 0x04 /* stop NIC, default = 1 */ -#define CR0_STRT 0x02 /* start NIC */ -#define CR0_INIT 0x01 /* start init process */ - - -/* - * Bits in the CR1 register - */ - -#define CR1_SFRST 0x80 /* software reset */ -#define CR1_RDMD1 0x40 /* RDMD1 */ -#define CR1_TDMD1 0x20 /* TDMD1 */ -#define CR1_KEYPAG 0x10 /* turn on par/key */ -#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ -#define CR1_FDX 0x04 /* full duplex mode */ -#define CR1_ETEN 0x02 /* early tx mode */ -#define CR1_EREN 0x01 /* early rx mode */ - -/* - * Bits in the CR register - */ - -#define CR_RDMD 0x0040 /* rx descriptor polling demand */ -#define CR_TDMD 0x0020 /* tx descriptor polling demand */ -#define CR_TXON 0x0010 -#define CR_RXON 0x0008 -#define CR_STOP 0x0004 /* stop NIC, default = 1 */ -#define CR_STRT 0x0002 /* start NIC */ -#define CR_INIT 0x0001 /* start init process */ -#define CR_SFRST 0x8000 /* software reset */ -#define CR_RDMD1 0x4000 /* RDMD1 */ -#define CR_TDMD1 0x2000 /* TDMD1 */ -#define CR_KEYPAG 0x1000 /* turn on par/key */ -#define CR_DPOLL 0x0800 /* disable rx/tx auto polling */ -#define CR_FDX 0x0400 /* full duplex mode */ -#define CR_ETEN 0x0200 /* early tx mode */ -#define CR_EREN 0x0100 /* early rx mode */ - -/* - * Bits in the IMR0 register - */ - -#define IMR0_CNTM 0x80 -#define IMR0_BEM 0x40 -#define IMR0_RUM 0x20 -#define IMR0_TUM 0x10 -#define IMR0_TXEM 0x08 -#define IMR0_RXEM 0x04 -#define IMR0_PTXM 0x02 -#define IMR0_PRXM 0x01 - -/* define imrshadow */ - -#define IMRShadow 0x5AFF - -/* - * Bits in the IMR1 register - */ - -#define IMR1_INITM 0x80 -#define IMR1_SRCM 0x40 -#define IMR1_NBFM 0x10 -#define IMR1_PRAIM 0x08 -#define IMR1_RES0M 0x04 -#define IMR1_ETM 0x02 -#define IMR1_ERM 0x01 - -/* - * Bits in the ISR register - */ - -#define ISR_INITI 0x8000 -#define ISR_SRCI 0x4000 -#define ISR_ABTI 0x2000 -#define ISR_NORBF 0x1000 -#define ISR_PKTRA 0x0800 -#define ISR_RES0 0x0400 -#define ISR_ETI 0x0200 -#define ISR_ERI 0x0100 -#define ISR_CNT 0x0080 -#define ISR_BE 0x0040 -#define ISR_RU 0x0020 -#define ISR_TU 0x0010 -#define ISR_TXE 0x0008 -#define ISR_RXE 0x0004 -#define ISR_PTX 0x0002 -#define ISR_PRX 0x0001 - -/* - * Bits in the ISR0 register - */ - -#define ISR0_CNT 0x80 -#define ISR0_BE 0x40 -#define ISR0_RU 0x20 -#define ISR0_TU 0x10 -#define ISR0_TXE 0x08 -#define ISR0_RXE 0x04 -#define ISR0_PTX 0x02 -#define ISR0_PRX 0x01 - -/* - * Bits in the ISR1 register - */ - -#define ISR1_INITI 0x80 -#define ISR1_SRCI 0x40 -#define ISR1_NORBF 0x10 -#define ISR1_PKTRA 0x08 -#define ISR1_ETI 0x02 -#define ISR1_ERI 0x01 - -/* ISR ABNORMAL CONDITION */ - -#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA - -/* - * Bits in the MIISR register - */ - -#define MIISR_MIIERR 0x08 -#define MIISR_MRERR 0x04 -#define MIISR_LNKFL 0x02 -#define MIISR_SPEED 0x01 - -/* - * Bits in the MIICR register - */ - -#define MIICR_MAUTO 0x80 -#define MIICR_RCMD 0x40 -#define MIICR_WCMD 0x20 -#define MIICR_MDPM 0x10 -#define MIICR_MOUT 0x08 -#define MIICR_MDO 0x04 -#define MIICR_MDI 0x02 -#define MIICR_MDC 0x01 - -/* - * Bits in the EECSR register - */ - -#define EECSR_EEPR 0x80 /* eeprom programed status, 73h means programed */ -#define EECSR_EMBP 0x40 /* eeprom embedded programming */ -#define EECSR_AUTOLD 0x20 /* eeprom content reload */ -#define EECSR_DPM 0x10 /* eeprom direct programming */ -#define EECSR_CS 0x08 /* eeprom CS pin */ -#define EECSR_SK 0x04 /* eeprom SK pin */ -#define EECSR_DI 0x02 /* eeprom DI pin */ -#define EECSR_DO 0x01 /* eeprom DO pin */ - -/* - * Bits in the BCR0 register - */ - -#define BCR0_CRFT2 0x20 -#define BCR0_CRFT1 0x10 -#define BCR0_CRFT0 0x08 -#define BCR0_DMAL2 0x04 -#define BCR0_DMAL1 0x02 -#define BCR0_DMAL0 0x01 - -/* - * Bits in the BCR1 register - */ - -#define BCR1_CTSF 0x20 -#define BCR1_CTFT1 0x10 -#define BCR1_CTFT0 0x08 -#define BCR1_POT2 0x04 -#define BCR1_POT1 0x02 -#define BCR1_POT0 0x01 - -/* - * Bits in the CFGA register - */ - -#define CFGA_EELOAD 0x80 /* enable eeprom embedded and direct programming */ -#define CFGA_JUMPER 0x40 -#define CFGA_MTGPIO 0x08 -#define CFGA_T10EN 0x02 -#define CFGA_AUTO 0x01 - -/* - * Bits in the CFGB register - */ - -#define CFGB_PD 0x80 -#define CFGB_POLEN 0x02 -#define CFGB_LNKEN 0x01 - -/* - * Bits in the CFGC register - */ - -#define CFGC_M10TIO 0x80 -#define CFGC_M10POL 0x40 -#define CFGC_PHY1 0x20 -#define CFGC_PHY0 0x10 -#define CFGC_BTSEL 0x08 -#define CFGC_BPS2 0x04 /* bootrom select[2] */ -#define CFGC_BPS1 0x02 /* bootrom select[1] */ -#define CFGC_BPS0 0x01 /* bootrom select[0] */ - -/* - * Bits in the CFGD register - */ - -#define CFGD_GPIOEN 0x80 -#define CFGD_DIAG 0x40 -#define CFGD_MAGIC 0x10 -#define CFGD_RANDOM 0x08 -#define CFGD_CFDX 0x04 -#define CFGD_CEREN 0x02 -#define CFGD_CETEN 0x01 - -/* Bits in RSR */ -#define RSR_RERR 0x00000001 -#define RSR_CRC 0x00000002 -#define RSR_FAE 0x00000004 -#define RSR_FOV 0x00000008 -#define RSR_LONG 0x00000010 -#define RSR_RUNT 0x00000020 -#define RSR_SERR 0x00000040 -#define RSR_BUFF 0x00000080 -#define RSR_EDP 0x00000100 -#define RSR_STP 0x00000200 -#define RSR_CHN 0x00000400 -#define RSR_PHY 0x00000800 -#define RSR_BAR 0x00001000 -#define RSR_MAR 0x00002000 -#define RSR_RXOK 0x00008000 -#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT - -/* Bits in TSR */ -#define TSR_NCR0 0x00000001 -#define TSR_NCR1 0x00000002 -#define TSR_NCR2 0x00000004 -#define TSR_NCR3 0x00000008 -#define TSR_COLS 0x00000010 -#define TSR_CDH 0x00000080 -#define TSR_ABT 0x00000100 -#define TSR_OWC 0x00000200 -#define TSR_CRS 0x00000400 -#define TSR_UDF 0x00000800 -#define TSR_TBUFF 0x00001000 -#define TSR_SERR 0x00002000 -#define TSR_JAB 0x00004000 -#define TSR_TERR 0x00008000 -#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS -#define TSR_OWN_BIT 0x80000000 - -#define CB_DELAY_LOOP_WAIT 10 /* 10ms */ -/* enabled mask value of irq */ - -#define W_IMR_MASK_VALUE 0x1BFF /* initial value of IMR */ - -/* Ethernet address filter type */ -#define PKT_TYPE_DIRECTED 0x0001 /* obsolete, directed address is always accepted */ -#define PKT_TYPE_MULTICAST 0x0002 -#define PKT_TYPE_ALL_MULTICAST 0x0004 -#define PKT_TYPE_BROADCAST 0x0008 -#define PKT_TYPE_PROMISCUOUS 0x0020 -#define PKT_TYPE_LONG 0x2000 -#define PKT_TYPE_RUNT 0x4000 -#define PKT_TYPE_ERROR 0x8000 /* accept error packets, e.g. CRC error */ - -/* Loopback mode */ - -#define NIC_LB_NONE 0x00 -#define NIC_LB_INTERNAL 0x01 -#define NIC_LB_PHY 0x02 /* MII or Internal-10BaseT loopback */ - -#define TX_RING_SIZE 2 -#define RX_RING_SIZE 2 -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ - -#define PCI_REG_MODE3 0x53 -#define MODE3_MIION 0x04 /* in PCI_REG_MOD3 OF PCI space */ - -enum rhine_revs { - VT86C100A = 0x00, - VTunknown0 = 0x20, - VT6102 = 0x40, - VT8231 = 0x50, /* Integrated MAC */ - VT8233 = 0x60, /* Integrated MAC */ - VT8235 = 0x74, /* Integrated MAC */ - VT8237 = 0x78, /* Integrated MAC */ - VTunknown1 = 0x7C, - VT6105 = 0x80, - VT6105_B0 = 0x83, - VT6105L = 0x8A, - VT6107 = 0x8C, - VTunknown2 = 0x8E, - VT6105M = 0x90, -}; - -/* Transmit and receive descriptors definition */ - -struct rhine_tx_desc -{ - union VTC_tx_status_tag - { - struct - { - unsigned long ncro:1; - unsigned long ncr1:1; - unsigned long ncr2:1; - unsigned long ncr3:1; - unsigned long cols:1; - unsigned long reserve_1:2; - unsigned long cdh:1; - unsigned long abt:1; - unsigned long owc:1; - unsigned long crs:1; - unsigned long udf:1; - unsigned long tbuff:1; - unsigned long serr:1; - unsigned long jab:1; - unsigned long terr:1; - unsigned long reserve_2:15; - unsigned long own_bit:1; - } - bits; - unsigned long lw; - } - tx_status; - - union VTC_tx_ctrl_tag - { - struct - { - unsigned long tx_buf_size:11; - unsigned long extend_tx_buf_size:4; - unsigned long chn:1; - unsigned long crc:1; - unsigned long reserve_1:4; - unsigned long stp:1; - unsigned long edp:1; - unsigned long ic:1; - unsigned long reserve_2:8; - } - bits; - unsigned long lw; - } - tx_ctrl; - - unsigned long buf_addr_1:32; - unsigned long buf_addr_2:32; - -}; - -struct rhine_rx_desc -{ - union VTC_rx_status_tag - { - struct - { - unsigned long rerr:1; - unsigned long crc_error:1; - unsigned long fae:1; - unsigned long fov:1; - unsigned long toolong:1; - unsigned long runt:1; - unsigned long serr:1; - unsigned long buff:1; - unsigned long edp:1; - unsigned long stp:1; - unsigned long chn:1; - unsigned long phy:1; - unsigned long bar:1; - unsigned long mar:1; - unsigned long reserve_1:1; - unsigned long rxok:1; - unsigned long frame_length:11; - unsigned long reverve_2:4; - unsigned long own_bit:1; - } - bits; - unsigned long lw; - } - rx_status; - - union VTC_rx_ctrl_tag - { - struct - { - unsigned long rx_buf_size:11; - unsigned long extend_rx_buf_size:4; - unsigned long reserved_1:17; - } - bits; - unsigned long lw; - } - rx_ctrl; - - unsigned long buf_addr_1:32; - unsigned long buf_addr_2:32; - -}; - -struct { - char txbuf[TX_RING_SIZE * PKT_BUF_SZ + 32]; - char rxbuf[RX_RING_SIZE * PKT_BUF_SZ + 32]; - char txdesc[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; - char rxdesc[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32]; -} rhine_buffers __shared; - -/* The I/O extent. */ -#define rhine_TOTAL_SIZE 0x80 - -#ifdef HAVE_DEVLIST -struct netdev_entry rhine_drv = - { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL }; -#endif - -static int rhine_debug = 1; - -/* - Theory of Operation - -I. Board Compatibility - -This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet -controller. - -II. Board-specific settings - -Boards with this chip are functional only in a bus-master PCI slot. - -Many operational settings are loaded from the EEPROM to the Config word at -offset 0x78. This driver assumes that they are correct. -If this driver is compiled to use PCI memory space operations the EEPROM -must be configured to enable memory ops. - -III. Driver operation - -IIIa. Ring buffers - -This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -IIIb/c. Transmit/Receive Structure - -This driver attempts to use a zero-copy receive and transmit scheme. - -Alas, all data buffers are required to start on a 32 bit boundary, so -the driver must often copy transmit packets into bounce buffers. - -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the chip as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack. Buffers consumed this way are replaced by newly allocated -skbuffs in the last phase of netdev_rx(). - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. New boards are typically used in generously configured machines -and the underfilled buffers have negligible impact compared to the benefit of -a single allocation size, so the default value of zero results in never -copying packets. When copying is done, the cost is usually mitigated by using -a combined copy/checksum routine. Copying also preloads the cache, which is -most useful with small frames. - -Since the VIA chips are only able to transfer data to buffers on 32 bit -boundaries, the the IP header at offset 14 in an ethernet frame isn't -longword aligned for further processing. Copying these unaligned buffers -has the beneficial effect of 16-byte aligning the IP header. - -IIId. Synchronization - -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and interrupt handling software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'lp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -IVb. References - -Preliminary VT86C100A manual from http://www.via.com.tw/ -http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html - -IVc. Errata - -The VT86C100A manual is not reliable information. -The chip does not handle unaligned transmit or receive buffers, resulting -in significant performance degradation for bounce buffer copies on transmit -and unaligned IP headers on receive. -The chip does not pad to minimum transmit length. - -*/ - -/* The rest of these values should never change. */ -#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */ - -static struct rhine_private -{ - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; - struct rhine_rx_desc *rx_ring; - struct rhine_tx_desc *tx_ring; - char *rx_buffs[RX_RING_SIZE]; - char *tx_buffs[TX_RING_SIZE]; - - /* temporary Rx buffers. */ - - int chip_id; - int chip_revision; - unsigned short ioaddr; - unsigned int cur_rx, cur_tx; /* The next free and used entries */ - unsigned int dirty_rx, dirty_tx; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - unsigned char mc_filter[8]; /* Current multicast filter. */ - char phys[4]; /* MII device addresses. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int media2:4; /* Secondary monitored media port. */ - unsigned int medialock:1; /* Don't sense media type. */ - unsigned int mediasense:1; /* Media sensing in progress. */ -} -rhine; - -static void rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, - int chip_id, int options); -static int QueryAuto (int); -static int ReadMII (int byMIIIndex, int); -static void WriteMII (char, char, char, int); -static void MIIDelay (void); -static void rhine_init_ring (struct nic *dev); -static void rhine_disable (struct nic *nic); -static void rhine_reset (struct nic *nic); -static int rhine_poll (struct nic *nic, int retrieve); -static void rhine_transmit (struct nic *nic, const char *d, unsigned int t, - unsigned int s, const char *p); -static void reload_eeprom(int ioaddr); - - -static void reload_eeprom(int ioaddr) -{ - int i; - outb(0x20, byEECSR); - /* Typically 2 cycles to reload. */ - for (i = 0; i < 150; i++) - if (! (inb(byEECSR) & 0x20)) - break; -} -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -rhine_init_ring (struct nic *nic) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int i; - - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) - { - - tp->rx_ring[i].rx_status.bits.own_bit = 1; - tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536; - - tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]); - tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); */ - } - /* Mark the last entry as wrapping the ring. */ - /* tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; */ - tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]); - /*printf("[%d]buf1=%hX,buf2=%hX",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); */ - - /* The Tx buffer descriptor is filled in as needed, but we - do need to clear the ownership bit. */ - - for (i = 0; i < TX_RING_SIZE; i++) - { - - tp->tx_ring[i].tx_status.lw = 0; - tp->tx_ring[i].tx_ctrl.lw = 0x00e08000; - tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]); - tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); */ - } - - tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); */ -} - -int -QueryAuto (int ioaddr) -{ - int byMIIIndex; - int MIIReturn; - - int advertising,mii_reg5; - int negociated; - - byMIIIndex = 0x04; - MIIReturn = ReadMII (byMIIIndex, ioaddr); - advertising=MIIReturn; - - byMIIIndex = 0x05; - MIIReturn = ReadMII (byMIIIndex, ioaddr); - mii_reg5=MIIReturn; - - negociated=mii_reg5 & advertising; - - if ( (negociated & 0x100) || (negociated & 0x1C0) == 0x40 ) - return 1; - else - return 0; - -} - -int -ReadMII (int byMIIIndex, int ioaddr) -{ - int ReturnMII; - char byMIIAdrbak; - char byMIICRbak; - char byMIItemp; - unsigned long ct; - - byMIIAdrbak = inb (byMIIAD); - byMIICRbak = inb (byMIICR); - outb (byMIICRbak & 0x7f, byMIICR); - MIIDelay (); - - outb (byMIIIndex, byMIIAD); - MIIDelay (); - - outb (inb (byMIICR) | 0x40, byMIICR); - - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - } - MIIDelay (); - - ReturnMII = inw (wMIIDATA); - - outb (byMIIAdrbak, byMIIAD); - outb (byMIICRbak, byMIICR); - MIIDelay (); - - return (ReturnMII); - -} - -void -WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr) -{ - int ReadMIItmp; - int MIIMask; - char byMIIAdrbak; - char byMIICRbak; - char byMIItemp; - unsigned long ct; - - - byMIIAdrbak = inb (byMIIAD); - - byMIICRbak = inb (byMIICR); - outb (byMIICRbak & 0x7f, byMIICR); - MIIDelay (); - outb (byMIISetByte, byMIIAD); - MIIDelay (); - - outb (inb (byMIICR) | 0x40, byMIICR); - - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - } - MIIDelay (); - - ReadMIItmp = inw (wMIIDATA); - MIIMask = 0x0001; - MIIMask = MIIMask << byMIISetBit; - - - if (byMIIOP == 0) - { - MIIMask = ~MIIMask; - ReadMIItmp = ReadMIItmp & MIIMask; - } - else - { - ReadMIItmp = ReadMIItmp | MIIMask; - - } - outw (ReadMIItmp, wMIIDATA); - MIIDelay (); - - outb (inb (byMIICR) | 0x20, byMIICR); - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x20; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x20; - } - MIIDelay (); - - outb (byMIIAdrbak & 0x7f, byMIIAD); - outb (byMIICRbak, byMIICR); - MIIDelay (); - -} - -void -MIIDelay (void) -{ - int i; - for (i = 0; i < 0x7fff; i++) - { - ( void ) inb (0x61); - ( void ) inb (0x61); - ( void ) inb (0x61); - ( void ) inb (0x61); - } -} - -/* Offsets to the device registers. */ -enum register_offsets { - StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, - IntrStatus=0x0C, IntrEnable=0x0E, - MulticastFilter0=0x10, MulticastFilter1=0x14, - RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54, - MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E, - MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74, - ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B, - RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81, - StickyHW=0x83, IntrStatus2=0x84, WOLcrClr=0xA4, WOLcgClr=0xA7, - PwrcsrClr=0xAC, -}; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, - IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0210, - IntrPCIErr=0x0040, - IntrStatsMax=0x0080, IntrRxEarly=0x0100, - IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, - IntrTxAborted=0x2000, IntrLinkChange=0x4000, - IntrRxWakeUp=0x8000, - IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260, - IntrTxDescRace=0x080000, /* mapped from IntrStatus2 */ - IntrTxErrSummary=0x082218, -}; -#define DEFAULT_INTR (IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | \ - IntrRxDropped | IntrRxNoBuf) - -/*************************************************************************** - IRQ - PXE IRQ Handler -***************************************************************************/ -void rhine_irq ( struct nic *nic, irq_action_t action ) { - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - /* Enable interrupts by setting the interrupt mask. */ - unsigned int intr_status; - - switch ( action ) { - case DISABLE : - case ENABLE : - intr_status = inw(nic->ioaddr + IntrStatus); - /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ - - /* added comment by guard */ - /* For supporting VT6107, please use revision id to recognize different chips in driver */ - // if (tp->chip_id == 0x3065) - if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 ) - intr_status |= inb(nic->ioaddr + IntrStatus2) << 16; - intr_status = (intr_status & ~DEFAULT_INTR); - if ( action == ENABLE ) - intr_status = intr_status | DEFAULT_INTR; - outw(intr_status, nic->ioaddr + IntrEnable); - break; - case FORCE : - outw(0x0010, nic->ioaddr + 0x84); - break; - } -} - -static struct nic_operations rhine_operations; - -static int -rhine_probe ( struct nic *nic, struct pci_device *pci ) { - - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - - if (!pci->ioaddr) - return 0; - - rhine_probe1 (nic, pci, pci->ioaddr, pci->device, -1); - - adjust_pci_device ( pci ); - - rhine_reset (nic); - - nic->nic_op = &rhine_operations; - - nic->irqno = pci->irq; - nic->ioaddr = tp->ioaddr; - - return 1; -} - -static void set_rx_mode(struct nic *nic __unused) { - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - unsigned char rx_mode; - int ioaddr = tp->ioaddr; - - /* ! IFF_PROMISC */ - outl(0xffffffff, byMAR0); - outl(0xffffffff, byMAR4); - rx_mode = 0x0C; - - outb(0x60 /* thresh */ | rx_mode, byRCR ); -} - -static void -rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, int chip_id, int options) -{ - struct rhine_private *tp; - static int did_version = 0; /* Already printed version info. */ - unsigned int i, ww; - unsigned int timeout; - int FDXFlag; - int byMIIvalue, LineSpeed, MIICRbak; - uint8_t revision_id; - unsigned char mode3_reg; - - if (rhine_debug > 0 && did_version++ == 0) - printf ("%s",version); - - // get revision id. - pci_read_config_byte(pci, PCI_REVISION, &revision_id); - - /* D-Link provided reset code (with comment additions) */ - if (revision_id >= 0x40) { - unsigned char byOrgValue; - - if(rhine_debug > 0) - printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n" - , chip_id); - /* clear sticky bit before reset & read ethernet address */ - byOrgValue = inb(bySTICKHW); - byOrgValue = byOrgValue & 0xFC; - outb(byOrgValue, bySTICKHW); - - /* (bits written are cleared?) */ - /* disable force PME-enable */ - outb(0x80, byWOLcgClr); - /* disable power-event config bit */ - outb(0xFF, byWOLcrClr); - /* clear power status (undocumented in vt6102 docs?) */ - outb(0xFF, byPwrcsrClr); - - } - - /* Reset the chip to erase previous misconfiguration. */ - outw(CR_SFRST, byCR0); - // if vt3043 delay after reset - if (revision_id <0x40) { - udelay(10000); - } - // polling till software reset complete - // W_MAX_TIMEOUT is the timeout period - for(ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if ((inw(byCR0) & CR_SFRST) == 0) - break; - } - - // issue AUTOLoad in EECSR to reload eeprom - outb(0x20, byEECSR ); - - // if vt3065 delay after reset - if (revision_id >=0x40) { - // delay 8ms to let MAC stable - mdelay(8); - /* - * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA - * turned on. it makes MAC receive magic packet - * automatically. So, we turn it off. (D-Link) - */ - outb(inb(byCFGA) & 0xFE, byCFGA); - } - - /* turn on bit2 in PCI configuration register 0x53 , only for 3065*/ - if (revision_id >= 0x40) { - pci_read_config_byte(pci, PCI_REG_MODE3, &mode3_reg); - pci_write_config_byte(pci, PCI_REG_MODE3, mode3_reg|MODE3_MIION); - } - - - /* back off algorithm ,disable the right-most 4-bit off CFGD*/ - outb(inb(byCFGD) & (~(CFGD_RANDOM | CFGD_CFDX | CFGD_CEREN | CFGD_CETEN)), byCFGD); - - /* reload eeprom */ - reload_eeprom(ioaddr); - - /* Perhaps this should be read from the EEPROM? */ - for (i = 0; i < ETH_ALEN; i++) - nic->node_addr[i] = inb (byPAR0 + i); - - DBG ( "IO address %#hX Ethernet Address: %s\n", ioaddr, eth_ntoa ( nic->node_addr ) ); - - /* restart MII auto-negotiation */ - WriteMII (0, 9, 1, ioaddr); - printf ("Analyzing Media type,this may take several seconds... "); - for (i = 0; i < 5; i++) - { - /* need to wait 1 millisecond - we will round it up to 50-100ms */ - timeout = currticks() + 2; - for (timeout = currticks() + 2; currticks() < timeout;) - /* nothing */; - if (ReadMII (1, ioaddr) & 0x0020) - break; - } - printf ("OK.\n"); - -#if 0 - /* JJM : for Debug */ - printf("MII : Address %hhX ",inb(ioaddr+0x6c)); - { - unsigned char st1,st2,adv1,adv2,l1,l2; - - st1=ReadMII(1,ioaddr)>>8; - st2=ReadMII(1,ioaddr)&0xFF; - adv1=ReadMII(4,ioaddr)>>8; - adv2=ReadMII(4,ioaddr)&0xFF; - l1=ReadMII(5,ioaddr)>>8; - l2=ReadMII(5,ioaddr)&0xFF; - printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2); - } -#endif - - - /* query MII to know LineSpeed,duplex mode */ - byMIIvalue = inb (ioaddr + 0x6d); - LineSpeed = byMIIvalue & MIISR_SPEED; - if (LineSpeed != 0) //JJM - { - printf ("Linespeed=10Mbs"); - } - else - { - printf ("Linespeed=100Mbs"); - } - - FDXFlag = QueryAuto (ioaddr); - if (FDXFlag == 1) - { - printf (" Fullduplex\n"); - outw (CR_FDX, byCR0); - } - else - { - printf (" Halfduplex\n"); - } - - - /* set MII 10 FULL ON, only apply in vt3043 */ - if(chip_id == 0x3043) - WriteMII (0x17, 1, 1, ioaddr); - - /* turn on MII link change */ - MIICRbak = inb (byMIICR); - outb (MIICRbak & 0x7F, byMIICR); - MIIDelay (); - outb (0x41, byMIIAD); - MIIDelay (); - - /* while((inb(byMIIAD)&0x20)==0) ; */ - outb (MIICRbak | 0x80, byMIICR); - - nic->priv_data = &rhine; - tp = &rhine; - tp->chip_id = chip_id; - tp->ioaddr = ioaddr; - tp->phys[0] = -1; - tp->chip_revision = revision_id; - - /* The lower four bits are the media type. */ - if (options > 0) - { - tp->full_duplex = (options & 16) ? 1 : 0; - tp->default_port = options & 15; - if (tp->default_port) - tp->medialock = 1; - } - return; -} - -static void -rhine_disable ( struct nic *nic ) { - - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - - rhine_reset(nic); - - printf ("rhine disable\n"); - /* Switch to loopback mode to avoid hardware races. */ - outb(0x60 | 0x01, byTCR); - /* Stop the chip's Tx and Rx processes. */ - outw(CR_STOP, byCR0); -} - -/************************************************************************** -ETH_RESET - Reset adapter -***************************************************************************/ -static void -rhine_reset (struct nic *nic) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - int i, j; - int FDXFlag, CRbak; - void *rx_ring_tmp; - void *tx_ring_tmp; - void *rx_bufs_tmp; - void *tx_bufs_tmp; - unsigned long rx_ring_tmp1; - unsigned long tx_ring_tmp1; - unsigned long rx_bufs_tmp1; - unsigned long tx_bufs_tmp1; - - /* printf ("rhine_reset\n"); */ - /* Soft reset the chip. */ - /*outb(CmdReset, ioaddr + ChipCmd); */ - - tx_bufs_tmp = rhine_buffers.txbuf; - tx_ring_tmp = rhine_buffers.txdesc; - rx_bufs_tmp = rhine_buffers.rxbuf; - rx_ring_tmp = rhine_buffers.rxdesc; - - /* tune RD TD 32 byte alignment */ - rx_ring_tmp1 = virt_to_bus ( rx_ring_tmp ); - j = (rx_ring_tmp1 + 32) & (~0x1f); - /* printf ("txring[%d]", j); */ - tp->rx_ring = (struct rhine_rx_desc *) bus_to_virt (j); - - tx_ring_tmp1 = virt_to_bus ( tx_ring_tmp ); - j = (tx_ring_tmp1 + 32) & (~0x1f); - tp->tx_ring = (struct rhine_tx_desc *) bus_to_virt (j); - /* printf ("rxring[%X]", j); */ - - - tx_bufs_tmp1 = virt_to_bus ( tx_bufs_tmp ); - j = (int) (tx_bufs_tmp1 + 32) & (~0x1f); - tx_bufs_tmp = bus_to_virt (j); - /* printf ("txb[%X]", j); */ - - rx_bufs_tmp1 = virt_to_bus ( rx_bufs_tmp ); - j = (int) (rx_bufs_tmp1 + 32) & (~0x1f); - rx_bufs_tmp = bus_to_virt (j); - /* printf ("rxb[%X][%X]", rx_bufs_tmp1, j); */ - - for (i = 0; i < RX_RING_SIZE; i++) - { - tp->rx_buffs[i] = (char *) rx_bufs_tmp; - /* printf("r[%X]",tp->rx_buffs[i]); */ - rx_bufs_tmp += 1536; - } - - for (i = 0; i < TX_RING_SIZE; i++) - { - tp->tx_buffs[i] = (char *) tx_bufs_tmp; - /* printf("t[%X]",tp->tx_buffs[i]); */ - tx_bufs_tmp += 1536; - } - - /* software reset */ - outb (CR1_SFRST, byCR1); - MIIDelay (); - - /* printf ("init ring"); */ - rhine_init_ring (nic); - /*write TD RD Descriptor to MAC */ - outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr); - outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr); - - /* Setup Multicast */ - set_rx_mode(nic); - - /* set TCR RCR threshold to store and forward*/ - outb (0x3e, byBCR0); - outb (0x38, byBCR1); - outb (0x2c, byRCR); - outb (0x60, byTCR); - /* Set Fulldupex */ - FDXFlag = QueryAuto (ioaddr); - if (FDXFlag == 1) - { - outb (CFGD_CFDX, byCFGD); - outw (CR_FDX, byCR0); - } - - /* KICK NIC to WORK */ - CRbak = inw (byCR0); - CRbak = CRbak & 0xFFFB; /* not CR_STOP */ - outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0); - - /* disable all known interrupt */ - outw (0, byIMR0); -} -/* Beware of PCI posted writes */ -#define IOSYNC do { inb(nic->ioaddr + StationAddr); } while (0) - -static int -rhine_poll (struct nic *nic, int retrieve) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int rxstatus, good = 0;; - - if (tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit == 0) - { - unsigned int intr_status; - /* There is a packet ready */ - if(!retrieve) - return 1; - - intr_status = inw(nic->ioaddr + IntrStatus); - /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ -#if 0 - if (tp->chip_id == 0x3065) - intr_status |= inb(nic->ioaddr + IntrStatus2) << 16; -#endif - /* Acknowledge all of the current interrupt sources ASAP. */ - if (intr_status & IntrTxDescRace) - outb(0x08, nic->ioaddr + IntrStatus2); - outw(intr_status & 0xffff, nic->ioaddr + IntrStatus); - IOSYNC; - - rxstatus = tp->rx_ring[tp->cur_rx].rx_status.lw; - if ((rxstatus & 0x0300) != 0x0300) - { - printf("rhine_poll: bad status\n"); - } - else if (rxstatus & (RSR_ABNORMAL)) - { - printf ("rxerr[%X]\n", rxstatus); - } - else - good = 1; - - if (good) - { - nic->packetlen = tp->rx_ring[tp->cur_rx].rx_status.bits.frame_length; - memcpy (nic->packet, tp->rx_buffs[tp->cur_rx], nic->packetlen); - /* printf ("Packet RXed\n"); */ - } - tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit = 1; - tp->cur_rx++; - tp->cur_rx = tp->cur_rx % RX_RING_SIZE; - } - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(DEFAULT_INTR & ~IntrRxDone, nic->ioaddr + IntrStatus); - - IOSYNC; - - return good; -} - -static void -rhine_transmit (struct nic *nic, - const char *d, unsigned int t, unsigned int s, const char *p) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - int entry; - unsigned char CR1bak; - unsigned char CR0bak; - unsigned int nstype; - unsigned long ct; - - - /*printf ("rhine_transmit\n"); */ - /* setup ethernet header */ - - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; - - memcpy (tp->tx_buffs[entry], d, ETH_ALEN); /* dst */ - memcpy (tp->tx_buffs[entry] + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ - - nstype=htons(t); - memcpy(tp->tx_buffs[entry] + 2 * ETH_ALEN, (char*)&nstype, 2); - - memcpy (tp->tx_buffs[entry] + ETH_HLEN, p, s); - s += ETH_HLEN; - while (s < ETH_ZLEN) - *((char *) tp->tx_buffs[entry] + (s++)) = 0; - - tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = s; - - tp->tx_ring[entry].tx_status.bits.own_bit = 1; - - - CR1bak = inb (byCR1); - - CR1bak = CR1bak | CR1_TDMD1; - /*printf("tdsw=[%X]",tp->tx_ring[entry].tx_status.lw); */ - /*printf("tdcw=[%X]",tp->tx_ring[entry].tx_ctrl.lw); */ - /*printf("tdbuf1=[%X]",tp->tx_ring[entry].buf_addr_1); */ - /*printf("tdbuf2=[%X]",tp->tx_ring[entry].buf_addr_2); */ - /*printf("td1=[%X]",inl(dwCurrentTDSE0)); */ - /*printf("td2=[%X]",inl(dwCurrentTDSE1)); */ - /*printf("td3=[%X]",inl(dwCurrentTDSE2)); */ - /*printf("td4=[%X]",inl(dwCurrentTDSE3)); */ - - outb (CR1bak, byCR1); - do - { - ct = currticks(); - /* Wait until transmit is finished or timeout*/ - while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) && - ct + 10*1000 < currticks()) - ; - - if(tp->tx_ring[entry].tx_status.bits.terr == 0) - break; - - if(tp->tx_ring[entry].tx_status.bits.abt == 1) - { - // turn on TX - CR0bak = inb(byCR0); - CR0bak = CR0bak|CR_TXON; - outb(CR0bak,byCR0); - } - }while(0); - tp->cur_tx++; - - /*outw(IMRShadow,byIMR0); */ - /*dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); */ - /*tp->tx_skbuff[entry] = 0; */ -} - -static struct nic_operations rhine_operations = { - .connect = dummy_connect, - .poll = rhine_poll, - .transmit = rhine_transmit, - .irq = rhine_irq, - -}; - -static struct pci_device_id rhine_nics[] = { -PCI_ROM(0x1106, 0x3065, "dlink-530tx", "VIA 6102", 0), -PCI_ROM(0x1106, 0x3106, "via-rhine-6105", "VIA 6105", 0), -PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043", 0), /* Rhine-I 86c100a */ -PCI_ROM(0x1106, 0x3053, "via6105m", "VIA 6105M", 0), -PCI_ROM(0x1106, 0x6100, "via-rhine-old", "VIA 86C100A", 0), /* Rhine-II */ -}; - -PCI_DRIVER ( rhine_driver, rhine_nics, PCI_NO_CLASS ); - -DRIVER ( "VIA 86C100", nic_driver, pci_driver, rhine_driver, - rhine_probe, rhine_disable ); - -/* EOF via-rhine.c */ - -/* - * Local variables: - * c-basic-offset: 8 - * c-indent-level: 8 - * tab-width: 8 - * End: - */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 73e1d89f..2c4c9920 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -113,7 +113,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_sundance ( ERRFILE_DRIVER | 0x00410000 ) #define ERRFILE_tlan ( ERRFILE_DRIVER | 0x00420000 ) #define ERRFILE_tulip ( ERRFILE_DRIVER | 0x00430000 ) -#define ERRFILE_via_rhine ( ERRFILE_DRIVER | 0x00440000 ) +#define ERRFILE_rhine ( ERRFILE_DRIVER | 0x00440000 ) #define ERRFILE_via_velocity ( ERRFILE_DRIVER | 0x00450000 ) #define ERRFILE_w89c840 ( ERRFILE_DRIVER | 0x00460000 ) #define ERRFILE_ipoib ( ERRFILE_DRIVER | 0x00470000 ) From 18521a170c6b256d842d8f1e298edf61c9104a83 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jul 2013 19:07:38 +0200 Subject: [PATCH 17/65] [intel] Incorporate ring producer and consumer counters in diagnostics Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index c3a7d407..569f3911 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -247,10 +247,15 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) { */ static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) { - DBGC ( intel, "INTEL %p TDH=%04x TDT=%04x RDH=%04x RDT=%04x\n", intel, + DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " + "RX %04x(%02x)/%04x(%02x)\n", intel, + ( intel->tx.cons & 0xffff ), readl ( intel->regs + intel->tx.reg + INTEL_xDH ), + ( intel->tx.prod & 0xffff ), readl ( intel->regs + intel->tx.reg + INTEL_xDT ), + ( intel->rx.cons & 0xffff ), readl ( intel->regs + intel->rx.reg + INTEL_xDH ), + ( intel->rx.prod & 0xffff ), readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); } From 7f4a5c06a30d280d89d88efa906feb7d14f2f35f Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Sat, 13 Jul 2013 00:01:47 +0200 Subject: [PATCH 18/65] [linux] Add missing #include Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/interface/linux/linux_time.c | 1 + src/interface/linux/linux_timer.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/interface/linux/linux_time.c b/src/interface/linux/linux_time.c index 6d722aad..e3cbafec 100644 --- a/src/interface/linux/linux_time.c +++ b/src/interface/linux/linux_time.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #include +#include #include #include #include diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 6b4643a6..bf55cd18 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -18,6 +18,7 @@ FILE_LICENCE(GPL2_OR_LATER); +#include #include #include From 397d4ec3c8ebec86d254ca45453a8cd0522d4ec3 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Sun, 14 Jul 2013 23:19:15 +0200 Subject: [PATCH 19/65] [legal] Add FILE_LICENCE for valgrind headers Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/arch/x86/include/valgrind/memcheck.h | 2 ++ src/arch/x86/include/valgrind/valgrind.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/arch/x86/include/valgrind/memcheck.h b/src/arch/x86/include/valgrind/memcheck.h index 46d23432..7d4b56d3 100644 --- a/src/arch/x86/include/valgrind/memcheck.h +++ b/src/arch/x86/include/valgrind/memcheck.h @@ -60,6 +60,8 @@ #ifndef __MEMCHECK_H #define __MEMCHECK_H +FILE_LICENCE ( BSD3 ); + /* This file is for inclusion into client (your!) code. diff --git a/src/arch/x86/include/valgrind/valgrind.h b/src/arch/x86/include/valgrind/valgrind.h index d72754b0..d48bbcca 100644 --- a/src/arch/x86/include/valgrind/valgrind.h +++ b/src/arch/x86/include/valgrind/valgrind.h @@ -73,6 +73,8 @@ #ifndef __VALGRIND_H #define __VALGRIND_H +FILE_LICENCE ( BSD3 ); + /* ------------------------------------------------------------------ */ /* VERSION NUMBER OF VALGRIND */ From c5ece719722580cfe9ed4e844b029c7ddf582db8 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Sun, 14 Jul 2013 23:19:37 +0200 Subject: [PATCH 20/65] [legal] Add FILE_LICENCE for core/errno.c Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/core/errno.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/errno.c b/src/core/errno.c index b4f44cec..06905561 100644 --- a/src/core/errno.c +++ b/src/core/errno.c @@ -1,5 +1,7 @@ #include +FILE_LICENCE ( GPL2_OR_LATER ); + /** @file * * Error codes From 6ad05aa319f69c110ffe3bdd1bbb67b5f778cbe3 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Sun, 14 Jul 2013 23:19:55 +0200 Subject: [PATCH 21/65] [legal] Add FILE_LICENCE for ath9k driver headers Signed-off-by: Marin Hannache Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/ath/ath.h | 2 ++ src/drivers/net/ath/ath9k/ani.h | 2 ++ src/drivers/net/ath/ath9k/ar9003_eeprom.h | 2 ++ src/drivers/net/ath/ath9k/ath9k.h | 2 ++ src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c | 2 ++ src/drivers/net/ath/ath9k/ath9k_init.c | 2 ++ src/drivers/net/ath/ath9k/calib.h | 2 ++ src/drivers/net/ath/ath9k/common.h | 2 ++ src/drivers/net/ath/ath9k/eeprom.h | 2 ++ src/drivers/net/ath/ath9k/hw.h | 2 ++ src/drivers/net/ath/ath9k/mac.h | 2 ++ src/drivers/net/ath/reg.h | 2 ++ src/drivers/net/ath/regd.h | 2 ++ 13 files changed, 26 insertions(+) diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index 5a19a19c..42ad59f7 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -20,6 +20,8 @@ #ifndef ATH_H #define ATH_H +FILE_LICENCE ( BSD2 ); + #include #include diff --git a/src/drivers/net/ath/ath9k/ani.h b/src/drivers/net/ath/ath9k/ani.h index 9a333b5f..dbd4d4d5 100644 --- a/src/drivers/net/ath/ath9k/ani.h +++ b/src/drivers/net/ath/ath9k/ani.h @@ -20,6 +20,8 @@ #ifndef ANI_H #define ANI_H +FILE_LICENCE ( BSD2 ); + #define HAL_PROCESS_ANI 0x00000001 #define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan) diff --git a/src/drivers/net/ath/ath9k/ar9003_eeprom.h b/src/drivers/net/ath/ath9k/ar9003_eeprom.h index 393f2de5..f0387923 100644 --- a/src/drivers/net/ath/ath9k/ar9003_eeprom.h +++ b/src/drivers/net/ath/ath9k/ar9003_eeprom.h @@ -20,6 +20,8 @@ #ifndef AR9003_EEPROM_H #define AR9003_EEPROM_H +FILE_LICENCE ( BSD2 ); + #define AR9300_EEP_VER 0xD000 #define AR9300_EEP_VER_MINOR_MASK 0xFFF #define AR9300_EEP_MINOR_VER_1 0x1 diff --git a/src/drivers/net/ath/ath9k/ath9k.h b/src/drivers/net/ath/ath9k/ath9k.h index d2dce9b7..36dc97e9 100644 --- a/src/drivers/net/ath/ath9k/ath9k.h +++ b/src/drivers/net/ath/ath9k/ath9k.h @@ -20,6 +20,8 @@ #ifndef ATH9K_H #define ATH9K_H +FILE_LICENCE ( BSD2 ); + #include "common.h" /* diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c b/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c index cc4edece..85d0c7de 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include "hw.h" #include "ar5008_initvals.h" #include "ar9001_initvals.h" diff --git a/src/drivers/net/ath/ath9k/ath9k_init.c b/src/drivers/net/ath/ath9k/ath9k_init.c index 93fbe773..03de7701 100644 --- a/src/drivers/net/ath/ath9k/ath9k_init.c +++ b/src/drivers/net/ath/ath9k/ath9k_init.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include #include #include diff --git a/src/drivers/net/ath/ath9k/calib.h b/src/drivers/net/ath/ath9k/calib.h index 5bf50f4a..b811accf 100644 --- a/src/drivers/net/ath/ath9k/calib.h +++ b/src/drivers/net/ath/ath9k/calib.h @@ -20,6 +20,8 @@ #ifndef CALIB_H #define CALIB_H +FILE_LICENCE ( BSD2 ); + #include "hw.h" #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 diff --git a/src/drivers/net/ath/ath9k/common.h b/src/drivers/net/ath/ath9k/common.h index 009f8036..0fe3b5be 100644 --- a/src/drivers/net/ath/ath9k/common.h +++ b/src/drivers/net/ath/ath9k/common.h @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include "../ath.h" #include "hw.h" diff --git a/src/drivers/net/ath/ath9k/eeprom.h b/src/drivers/net/ath/ath9k/eeprom.h index b451ebd7..8a48d6e5 100644 --- a/src/drivers/net/ath/ath9k/eeprom.h +++ b/src/drivers/net/ath/ath9k/eeprom.h @@ -20,6 +20,8 @@ #ifndef EEPROM_H #define EEPROM_H +FILE_LICENCE ( BSD2 ); + #define AR_EEPROM_MODAL_SPURS 5 #include "../ath.h" diff --git a/src/drivers/net/ath/ath9k/hw.h b/src/drivers/net/ath/ath9k/hw.h index c10df388..05107469 100644 --- a/src/drivers/net/ath/ath9k/hw.h +++ b/src/drivers/net/ath/ath9k/hw.h @@ -20,6 +20,8 @@ #ifndef HW_H #define HW_H +FILE_LICENCE ( BSD2 ); + #include #include "mac.h" diff --git a/src/drivers/net/ath/ath9k/mac.h b/src/drivers/net/ath/ath9k/mac.h index 5899230e..0c0a7594 100644 --- a/src/drivers/net/ath/ath9k/mac.h +++ b/src/drivers/net/ath/ath9k/mac.h @@ -20,6 +20,8 @@ #ifndef MAC_H #define MAC_H +FILE_LICENCE ( BSD2 ); + #include #define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \ diff --git a/src/drivers/net/ath/reg.h b/src/drivers/net/ath/reg.h index 484a8f1c..7982f434 100644 --- a/src/drivers/net/ath/reg.h +++ b/src/drivers/net/ath/reg.h @@ -20,6 +20,8 @@ #ifndef ATH_REGISTERS_H #define ATH_REGISTERS_H +FILE_LICENCE ( BSD2 ); + #define AR_MIBC 0x0040 #define AR_MIBC_COW 0x00000001 #define AR_MIBC_FMC 0x00000002 diff --git a/src/drivers/net/ath/regd.h b/src/drivers/net/ath/regd.h index 6954c5f3..fd09a0c8 100644 --- a/src/drivers/net/ath/regd.h +++ b/src/drivers/net/ath/regd.h @@ -20,6 +20,8 @@ #ifndef REGD_H #define REGD_H +FILE_LICENCE ( BSD2 ); + #include "ath.h" enum ctl_group { From 49d14f0d8d9291e3a98c6bfd9005b3c6bb85e6c8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 00:06:45 +0200 Subject: [PATCH 22/65] [base16] Ensure base16_encode() always terminates its result string base16_encode() will fail to generate a terminating NUL if the length of the raw data is zero, since the loop calling sprintf() will never execute. Fix by explicitly terminating the result with a NUL. Reported-by: Marin Hannache Debugged-by: Marin Hannache Tested-by: Marin Hannache Signed-off-by: Michael Brown --- src/core/base16.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/base16.c b/src/core/base16.c index 7fa4b200..2544bd7f 100644 --- a/src/core/base16.c +++ b/src/core/base16.c @@ -51,10 +51,14 @@ void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { char *encoded_bytes = encoded; size_t remaining = len; + /* Encode each byte */ for ( ; remaining-- ; encoded_bytes += 2 ) { sprintf ( encoded_bytes, "%02x", *(raw_bytes++) ); } + /* Ensure terminating NUL exists even if length was zero */ + *encoded_bytes = '\0'; + DBG ( "Base16-encoded to \"%s\":\n", encoded ); DBG_HDA ( 0, raw, len ); assert ( strlen ( encoded ) == base16_encoded_len ( len ) ); From 3aafe5fc54a06cc5ebc890ef576d89d6e9064633 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 00:38:43 +0200 Subject: [PATCH 23/65] [realtek] Report RX error detail in debug messages Signed-off-by: Michael Brown --- src/drivers/net/realtek.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 498c233f..867efbcd 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -907,13 +907,15 @@ static void realtek_poll_rx ( struct net_device *netdev ) { len = ( le16_to_cpu ( rx->length ) & RTL_DESC_SIZE_MASK ); iob_put ( iobuf, ( len - 4 /* strip CRC */ ) ); - DBGC2 ( rtl, "REALTEK %p RX %d complete (length %zd)\n", - rtl, rx_idx, len ); - /* Hand off to network stack */ if ( rx->flags & cpu_to_le16 ( RTL_DESC_RES ) ) { + DBGC ( rtl, "REALTEK %p RX %d error (length %zd, " + "flags %04x)\n", rtl, rx_idx, len, + le16_to_cpu ( rx->flags ) ); netdev_rx_err ( netdev, iobuf, -EIO ); } else { + DBGC2 ( rtl, "REALTEK %p RX %d complete (length " + "%zd)\n", rtl, rx_idx, len ); netdev_rx ( netdev, iobuf ); } rtl->rx.cons++; From bba5a3902663a52670a47f48980843594af23bcd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 11:13:33 +0200 Subject: [PATCH 24/65] [script] Allow for backslash continuation of script lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow long script lines to be broken up using backslash continuation. For example: choose --default linux --timeout 3000 os \ && goto boot_${os} || goto cancelled Requested-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/image/script.c | 54 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/image/script.c b/src/image/script.c index e83180ab..8ef05854 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -57,9 +57,11 @@ static size_t script_offset; static int process_script ( struct image *image, int ( * process_line ) ( const char *line ), int ( * terminate ) ( int rc ) ) { + size_t len = 0; + char *line = NULL; off_t eol; - size_t len; - char *line; + size_t frag_len; + char *tmp; int rc; script_offset = 0; @@ -71,28 +73,54 @@ static int process_script ( struct image *image, ( image->len - script_offset ) ); if ( eol < 0 ) eol = image->len; - len = ( eol - script_offset ); + frag_len = ( eol - script_offset ); /* Allocate buffer for line */ - line = zalloc ( len + 1 /* NUL */ ); - if ( ! line ) - return -ENOMEM; + tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) ); + if ( ! tmp ) { + rc = -ENOMEM; + goto err_alloc; + } + line = tmp; /* Copy line */ - copy_from_user ( line, image->data, script_offset, len ); + copy_from_user ( ( line + len ), image->data, script_offset, + frag_len ); + len += frag_len; + + /* Move to next line in script */ + script_offset += ( frag_len + 1 ); + + /* Strip trailing CR, if present */ + if ( line[ len - 1 ] == '\r' ) + len--; + + /* Handle backslash continuations */ + if ( line[ len - 1 ] == '\\' ) { + len--; + rc = -EINVAL; + continue; + } + + /* Terminate line */ + line[len] = '\0'; DBG ( "$ %s\n", line ); - /* Move to next line */ - script_offset += ( len + 1 ); - - /* Process and free line */ + /* Process line */ rc = process_line ( line ); - free ( line ); if ( terminate ( rc ) ) - return rc; + goto err_process; + + /* Free line */ + free ( line ); + line = NULL; + len = 0; } while ( script_offset < image->len ); + err_process: + err_alloc: + free ( line ); return rc; } From 4fabc0012ac69c3a333a192ab97c790bed18b6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Jamr=C3=B3z?= Date: Sat, 13 Jul 2013 18:59:07 +0200 Subject: [PATCH 25/65] [velocity] Rewrite VIA Velocity driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Adrian Jamróz Modified-by: Thomas Miletich Signed-off-by: Thomas Miletich Signed-off-by: Michael Brown --- src/drivers/net/velocity.c | 807 +++++++++++++ src/drivers/net/velocity.h | 356 ++++++ src/drivers/net/via-velocity.c | 1927 ------------------------------- src/drivers/net/via-velocity.h | 1932 -------------------------------- src/include/ipxe/errfile.h | 2 +- 5 files changed, 1164 insertions(+), 3860 deletions(-) create mode 100644 src/drivers/net/velocity.c create mode 100644 src/drivers/net/velocity.h delete mode 100644 src/drivers/net/via-velocity.c delete mode 100644 src/drivers/net/via-velocity.h diff --git a/src/drivers/net/velocity.c b/src/drivers/net/velocity.c new file mode 100644 index 00000000..6d518520 --- /dev/null +++ b/src/drivers/net/velocity.c @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2012 Adrian Jamróz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "velocity.h" + +#define velocity_setbit(_reg, _mask) writeb ( readb ( _reg ) | _mask, _reg ) +#define virt_to_le32bus(x) ( cpu_to_le32 ( virt_to_bus ( x ) ) ) + +/** @file + * + * VIA Velocity network driver + * + */ + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Stop MII auto-polling + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_autopoll_stop ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Disable MII auto polling */ + writeb ( 0, vlc->regs + VELOCITY_MIICR ); + + /* Wait for disabling to take effect */ + while ( timeout-- ) { + udelay ( 1 ); + if ( readb ( vlc->regs + VELOCITY_MIISR ) & + VELOCITY_MIISR_IDLE ) + return 0; + } + + DBGC ( vlc, "MII autopoll stop timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Start MII auto-polling + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_autopoll_start ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Enable MII auto polling */ + writeb ( VELOCITY_MIICR_MAUTO, vlc->regs + VELOCITY_MIICR ); + + /* Wait for enabling to take effect */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIISR ) & + VELOCITY_MIISR_IDLE ) == 0 ) + return 0; + } + + DBGC ( vlc, "MII autopoll start timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct velocity_nic *vlc = + container_of ( mii, struct velocity_nic, mii ); + int timeout = VELOCITY_TIMEOUT_US; + int result; + + DBGC2 ( vlc, "VELOCITY %p MII read reg %d\n", vlc, reg ); + + /* Disable autopolling before we can access MII */ + velocity_autopoll_stop ( vlc ); + + /* Send read command and address */ + writeb ( reg, vlc->regs + VELOCITY_MIIADDR ); + velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_RCMD ); + + /* Wait for read to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIICR ) & + VELOCITY_MIICR_RCMD ) == 0 ) { + result = readw ( vlc->regs + VELOCITY_MIIDATA ); + velocity_autopoll_start ( vlc ); + return result; + } + } + + /* Restart autopolling */ + velocity_autopoll_start ( vlc ); + + DBGC ( vlc, "MII read timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int velocity_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data) { + struct velocity_nic *vlc = + container_of ( mii, struct velocity_nic, mii ); + int timeout = VELOCITY_TIMEOUT_US; + + DBGC2 ( vlc, "VELOCITY %p MII write reg %d data 0x%04x\n", + vlc, reg, data ); + + /* Disable autopolling before we can access MII */ + velocity_autopoll_stop ( vlc ); + + /* Send write command, data and destination register */ + writeb ( reg, vlc->regs + VELOCITY_MIIADDR ); + writew ( data, vlc->regs + VELOCITY_MIIDATA ); + velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_WCMD ); + + /* Wait for write to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIICR ) & + VELOCITY_MIICR_WCMD ) == 0 ) { + velocity_autopoll_start ( vlc ); + return 0; + } + } + + /* Restart autopolling */ + velocity_autopoll_start ( vlc ); + + DBGC ( vlc, "MII write timeout\n" ); + return -ETIMEDOUT; +} + +/** Velocity MII operations */ +static struct mii_operations velocity_mii_operations = { + .read = velocity_mii_read, + .write = velocity_mii_write, +}; + +/** + * Set Link speed + * + * @v vlc Velocity device + */ +static void velocity_set_link ( struct velocity_nic *vlc ) { + int tmp; + + /* Advertise 1000MBit */ + tmp = velocity_mii_read ( &vlc->mii, MII_CTRL1000 ); + tmp |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + velocity_mii_write ( &vlc->mii, MII_CTRL1000, tmp ); + + /* Enable GBit operation in MII Control Register */ + tmp = velocity_mii_read ( &vlc->mii, MII_BMCR ); + tmp |= BMCR_SPEED1000; + velocity_mii_write ( &vlc->mii, MII_BMCR, tmp ); +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reload eeprom contents + * + * @v vlc Velocity device + */ +static int velocity_reload_eeprom ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Initiate reload */ + velocity_setbit ( vlc->regs + VELOCITY_EECSR, VELOCITY_EECSR_RELOAD ); + + /* Wait for reload to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_EECSR ) & + VELOCITY_EECSR_RELOAD ) == 0 ) + return 0; + } + + DBGC ( vlc, "VELOCITY %p EEPROM reload timeout\n", vlc ); + return -ETIMEDOUT; +} + +/** + * Reset hardware + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_reset ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + uint8_t tmp; + + DBGC ( vlc, "VELOCITY %p reset\n", vlc ); + + /* clear sticky Power state bits */ + tmp = readb ( vlc->regs + VELOCITY_STICKY ); + tmp &= ~( VELOCITY_STICKY_DS0 | VELOCITY_STICKY_DS1 ); + writeb ( tmp, vlc->regs + VELOCITY_STICKY ); + + /* clear PACPI, which might have been enabled by the EEPROM reload */ + tmp = readb ( vlc->regs + VELOCITY_CFGA ); + tmp &= ~VELOCITY_CFGA_PACPI; + writeb ( tmp, vlc->regs + VELOCITY_CFGA ); + + velocity_setbit ( vlc->regs + VELOCITY_CRS1, VELOCITY_CR1_SFRST ); + + /* Wait for reset to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_CRS1 ) & + VELOCITY_CR1_SFRST ) == 0 ) + return 0; + } + + return -EINVAL; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void velocity_check_link ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + + if ( readb ( vlc->regs + VELOCITY_PHYSTS0 ) & VELOCITY_PHYSTS0_LINK ) { + netdev_link_up ( netdev ); + DBGC ( vlc, "VELOCITY %p link up\n", vlc ); + } else { + netdev_link_down ( netdev ); + DBGC ( vlc, "VELOCITY %p link down\n", vlc ); + } + + /* The card disables auto-poll after a link change */ + velocity_autopoll_start ( vlc ); +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Allocate descriptor rings + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_alloc_rings ( struct velocity_nic *vlc ) { + int rc = 0; + + /* Allocate RX descriptor ring */ + vlc->rx_prod = 0; + vlc->rx_cons = 0; + vlc->rx_commit = 0; + vlc->rx_ring = malloc_dma ( VELOCITY_RXDESC_SIZE, VELOCITY_RING_ALIGN ); + if ( ! vlc->rx_ring ) + return -ENOMEM; + + memset ( vlc->rx_ring, 0, VELOCITY_RXDESC_SIZE ); + + DBGC2 ( vlc, "VELOCITY %p RX ring start address: %p(phys: %#08lx)\n", + vlc, vlc->rx_ring, virt_to_bus ( vlc->rx_ring ) ); + + /* Allocate TX descriptor ring */ + vlc->tx_prod = 0; + vlc->tx_cons = 0; + vlc->tx_ring = malloc_dma ( VELOCITY_TXDESC_SIZE, VELOCITY_RING_ALIGN ); + if ( ! vlc->tx_ring ) { + rc = -ENOMEM; + goto err_tx_alloc; + } + + memset ( vlc->tx_ring, 0, VELOCITY_TXDESC_SIZE ); + + /* Send RX ring to the card */ + writel ( virt_to_bus ( vlc->rx_ring ), + vlc->regs + VELOCITY_RXDESC_ADDR_LO ); + writew ( VELOCITY_RXDESC_NUM - 1, vlc->regs + VELOCITY_RXDESCNUM ); + + /* Send TX ring to the card */ + writel ( virt_to_bus ( vlc->tx_ring ), + vlc->regs + VELOCITY_TXDESC_ADDR_LO0 ); + writew ( VELOCITY_TXDESC_NUM - 1, vlc->regs + VELOCITY_TXDESCNUM ); + + DBGC2 ( vlc, "VELOCITY %p TX ring start address: %p(phys: %#08lx)\n", + vlc, vlc->tx_ring, virt_to_bus ( vlc->tx_ring ) ); + + return 0; + +err_tx_alloc: + free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE ); + return rc; +} + +/** + * Refill receive descriptor ring + * + * @v vlc Velocity device + */ +static void velocity_refill_rx ( struct velocity_nic *vlc ) { + struct velocity_rx_descriptor *desc; + struct io_buffer *iobuf; + int rx_idx, i = 0; + + /* Check for new packets */ + while ( ( vlc->rx_prod - vlc->rx_cons ) < VELOCITY_RXDESC_NUM ) { + iobuf = alloc_iob ( VELOCITY_RX_MAX_LEN ); + + /* Memory pressure: try again next poll */ + if ( ! iobuf ) + break; + + rx_idx = ( vlc->rx_prod++ % VELOCITY_RXDESC_NUM ); + desc = &vlc->rx_ring[rx_idx]; + + /* Set descrptor fields */ + desc->des1 = 0; + desc->addr = virt_to_le32bus ( iobuf-> data ); + desc->des2 = cpu_to_le32 ( + VELOCITY_DES2_SIZE ( VELOCITY_RX_MAX_LEN - 1 ) | + VELOCITY_DES2_IC ); + + vlc->rx_buffs[rx_idx] = iobuf; + i++; + + /* Return RX descriptors in blocks of 4 (hw requirement) */ + if ( rx_idx % 4 == 3 ) { + int j; + for (j = 0; j < 4; j++) { + desc = &vlc->rx_ring[rx_idx - j]; + desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN ); + } + vlc->rx_commit += 4; + } + } + + wmb(); + + if ( vlc->rx_commit ) { + writew ( vlc->rx_commit, + vlc->regs + VELOCITY_RXDESC_RESIDUECNT ); + vlc->rx_commit = 0; + } + + if ( i > 0 ) + DBGC2 ( vlc, "VELOCITY %p refilled %d RX descriptors\n", + vlc, i ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int velocity_open ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + int rc; + + DBGC ( vlc, "VELOCITY %p open\n", vlc ); + DBGC ( vlc, "VELOCITY %p regs at: %p\n", vlc, vlc->regs ); + + /* Allocate descriptor rings */ + if ( ( rc = velocity_alloc_rings ( vlc ) ) != 0 ) + return rc; + + velocity_refill_rx ( vlc ); + + /* Enable TX/RX queue */ + writew ( VELOCITY_TXQCSRS_RUN0, vlc->regs + VELOCITY_TXQCSRS ); + writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK, + vlc->regs + VELOCITY_RXQCSRS ); + + /* Enable interrupts */ + writeb ( 0xff, vlc->regs + VELOCITY_IMR0 ); + writeb ( 0xff, vlc->regs + VELOCITY_IMR1 ); + + /* Start MAC */ + writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR1_DPOLL, vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR0_START | VELOCITY_CR0_TXON | VELOCITY_CR0_RXON, + vlc->regs + VELOCITY_CRS0 ); + + /* Receive all packets */ + writeb ( 0xff, vlc->regs + VELOCITY_RCR ); + + /* Set initial link state */ + velocity_check_link ( netdev ); + + velocity_autopoll_start ( vlc ); + + DBGC2 ( vlc, "VELOCITY %p CR3 %02x\n", + vlc, readb ( vlc->regs + 0x0B ) ); + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void velocity_close ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + int i; + + /* Stop NIC */ + writeb ( VELOCITY_CR0_TXON | VELOCITY_CR0_RXON, + vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRS0 ); + + /* Clear RX ring information */ + writel ( 0, vlc->regs + VELOCITY_RXDESC_ADDR_LO ); + writew ( 0, vlc->regs + VELOCITY_RXDESCNUM ); + + /* Destroy RX ring */ + free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE ); + vlc->rx_ring = NULL; + vlc->rx_prod = 0; + vlc->rx_cons = 0; + + /* Discard receive buffers */ + for ( i = 0 ; i < VELOCITY_RXDESC_NUM ; i++ ) { + if ( vlc->rx_buffs[i] ) + free_iob ( vlc->rx_buffs[i] ); + vlc->rx_buffs[i] = NULL; + } + + /* Clear TX ring information */ + writel ( 0, vlc->regs + VELOCITY_TXDESC_ADDR_LO0 ); + writew ( 0, vlc->regs + VELOCITY_TXDESCNUM ); + + /* Destroy TX ring */ + free_dma ( vlc->tx_ring, VELOCITY_TXDESC_SIZE ); + vlc->tx_ring = NULL; + vlc->tx_prod = 0; + vlc->tx_cons = 0; +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int velocity_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct velocity_nic *vlc = netdev->priv; + struct velocity_tx_descriptor *desc; + unsigned int tx_idx; + + /* Pad packet to minimum length */ + iob_pad ( iobuf, ETH_ZLEN ); + + tx_idx = ( vlc->tx_prod++ % VELOCITY_TXDESC_NUM ); + desc = &vlc->tx_ring[tx_idx]; + + /* Set packet size and transfer ownership to NIC */ + desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN | + VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) ); + /* Data in first desc fragment, only desc for packet, generate INT */ + desc->des1 = cpu_to_le32 ( VELOCITY_DES1_FRAG ( 1 ) | + VELOCITY_DES1_TCPLS | + VELOCITY_DES1_INTR ); + + desc->frags[0].addr = virt_to_le32bus ( iobuf->data ); + desc->frags[0].des2 = cpu_to_le32 ( + VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) ); + + wmb(); + + /* Initiate TX */ + velocity_setbit ( vlc->regs + VELOCITY_TXQCSRS, VELOCITY_TXQCSRS_WAK0 ); + + DBGC2 ( vlc, "VELOCITY %p tx_prod=%d desc=%p iobuf=%p len=%zd\n", + vlc, tx_idx, desc, iobuf->data, iob_len ( iobuf ) ); + + return 0; +} + +/** + * Poll for received packets. + * + * @v vlc Velocity device + */ +static void velocity_poll_rx ( struct velocity_nic *vlc ) { + struct velocity_rx_descriptor *desc; + struct io_buffer *iobuf; + int rx_idx; + size_t len; + uint32_t des0; + + /* Check for packets */ + while ( vlc->rx_cons != vlc->rx_prod ) { + rx_idx = ( vlc->rx_cons % VELOCITY_RXDESC_NUM ); + desc = &vlc->rx_ring[rx_idx]; + + des0 = cpu_to_le32 ( desc->des0 ); + + /* Return if descriptor still in use */ + if ( des0 & VELOCITY_DES0_OWN ) + return; + + iobuf = vlc->rx_buffs[rx_idx]; + + /* Get length, strip CRC */ + len = VELOCITY_DES0_RMBC ( des0 ) - 4; + iob_put ( iobuf, len ); + + DBGC2 ( vlc, "VELOCITY %p got packet on idx=%d (prod=%d), len %zd\n", + vlc, rx_idx, vlc->rx_prod % VELOCITY_RXDESC_NUM, len ); + + if ( des0 & VELOCITY_DES0_RX_ERR ) { + /* Report receive error */ + netdev_rx_err ( vlc->netdev, iobuf, -EINVAL ); + DBGC ( vlc, "VELOCITY %p receive error, status: %02x\n", + vlc, des0 ); + } else if ( des0 & VELOCITY_DES0_RXOK ) { + /* Report receive success */ + netdev_rx( vlc->netdev, iobuf ); + } else { + /* Card indicated neither success nor failure + * Technically this shouldn't happen, but we saw it + * in debugging once. */ + DBGC ( vlc, "VELOCITY %p RX neither ERR nor OK: %04x\n", + vlc, des0 ); + DBGC ( vlc, "packet len: %zd\n", len ); + DBGC_HD ( vlc, iobuf->data, 64 ); + + /* we don't know what it is, treat is as an error */ + netdev_rx_err ( vlc->netdev, iobuf, -EINVAL ); + } + + vlc->rx_cons++; + } +} + +/** + * Poll for completed packets. + * + * @v vlc Velocity device + */ +static void velocity_poll_tx ( struct velocity_nic *vlc ) { + struct velocity_tx_descriptor *desc; + int tx_idx; + + /* Check for packets */ + while ( vlc->tx_cons != vlc->tx_prod ) { + tx_idx = ( vlc->tx_cons % VELOCITY_TXDESC_NUM ); + desc = &vlc->tx_ring[tx_idx]; + + /* Return if descriptor still in use */ + if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_OWN ) + return; + + /* Report errors */ + if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_TERR ) { + netdev_tx_complete_next_err ( vlc->netdev, -EINVAL ); + return; + } + + netdev_tx_complete_next ( vlc->netdev ); + + DBGC2 ( vlc, "VELOCITY %p poll_tx cons=%d prod=%d tsr=%04x\n", + vlc, tx_idx, vlc->tx_prod % VELOCITY_TXDESC_NUM, + ( desc->des0 & 0xffff ) ); + vlc->tx_cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void velocity_poll ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + uint8_t isr1; + + isr1 = readb ( vlc->regs + VELOCITY_ISR1 ); + + /* ACK interrupts */ + writew ( 0xFFFF, vlc->regs + VELOCITY_ISR0 ); + + /* Check for competed packets */ + velocity_poll_rx ( vlc ); + velocity_poll_tx ( vlc ); + + if ( isr1 & VELOCITY_ISR1_SRCI ) { + /* Update linkstate */ + DBGC2 ( vlc, "VELOCITY %p link status interrupt\n", vlc ); + velocity_check_link ( netdev ); + } + + velocity_refill_rx ( vlc ); + + /* deal with potential RX stall caused by RX ring underrun */ + writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK, + vlc->regs + VELOCITY_RXQCSRS ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void velocity_irq ( struct net_device *netdev, int enable ) { + struct velocity_nic *vlc = netdev->priv; + + DBGC ( vlc, "VELOCITY %p interrupts %s\n", vlc, + enable ? "enable" : "disable" ); + + if (enable) { + /* Enable interrupts */ + writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRS3 ); + } else { + /* Disable interrupts */ + writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRC3 ); + } +} + +/** Velocity network device operations */ +static struct net_device_operations velocity_operations = { + .open = velocity_open, + .close = velocity_close, + .transmit = velocity_transmit, + .poll = velocity_poll, + .irq = velocity_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int velocity_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct velocity_nic *vlc; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *vlc ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &velocity_operations ); + vlc = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + vlc->regs = ioremap ( pci->membase, VELOCITY_BAR_SIZE ); + vlc->netdev = netdev; + + /* Reset the NIC */ + if ( ( rc = velocity_reset ( vlc ) ) != 0 ) + goto err_reset; + + /* Reload EEPROM */ + if ( ( rc = velocity_reload_eeprom ( vlc ) ) != 0 ) + goto err_reset; + + /* Get MAC address */ + netdev->hw_addr[0] = readb ( vlc->regs + VELOCITY_MAC0 ); + netdev->hw_addr[1] = readb ( vlc->regs + VELOCITY_MAC1 ); + netdev->hw_addr[2] = readb ( vlc->regs + VELOCITY_MAC2 ); + netdev->hw_addr[3] = readb ( vlc->regs + VELOCITY_MAC3 ); + netdev->hw_addr[4] = readb ( vlc->regs + VELOCITY_MAC4 ); + netdev->hw_addr[5] = readb ( vlc->regs + VELOCITY_MAC5 ); + + /* Initialise and reset MII interface */ + mii_init ( &vlc->mii, &velocity_mii_operations ); + if ( ( rc = mii_reset ( &vlc->mii ) ) != 0 ) { + DBGC ( vlc, "VELOCITY %p could not reset MII: %s\n", + vlc, strerror ( rc ) ); + goto err_mii_reset; + } + + /* Enable proper link advertising */ + velocity_set_link ( vlc ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + return 0; + + err_register_netdev: + err_mii_reset: + velocity_reset ( vlc ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void velocity_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct velocity_nic *vlc = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + velocity_reset ( vlc ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Velocity PCI device IDs */ +static struct pci_device_id velocity_nics[] = { + PCI_ROM ( 0x1106, 0x3119, "vt6122", "VIA Velocity", 0 ), +}; + +/** Velocity PCI driver */ +struct pci_driver velocity_driver __pci_driver = { + .ids = velocity_nics, + .id_count = ( sizeof ( velocity_nics ) / sizeof ( velocity_nics[0] ) ), + .probe = velocity_probe, + .remove = velocity_remove, +}; diff --git a/src/drivers/net/velocity.h b/src/drivers/net/velocity.h new file mode 100644 index 00000000..04e6a146 --- /dev/null +++ b/src/drivers/net/velocity.h @@ -0,0 +1,356 @@ +#ifndef _VELOCITY_H +#define _VELOCITY_H + +/** @file + * + * VIA Velocity network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Skeleton BAR size */ +#define VELOCITY_BAR_SIZE 256 + +/** Default timeout */ +#define VELOCITY_TIMEOUT_US 10 * 1000 + +struct velocity_frag { + uint32_t addr; + uint32_t des2; +} __attribute__ ((packed)); + +/** Velocity descriptor format */ +struct velocity_tx_descriptor { + uint32_t des0; + uint32_t des1; + /* We only use the first fragment, the HW requires us to have 7 */ + struct velocity_frag frags[7]; +} __attribute__ ((packed)); + +struct velocity_rx_descriptor { + uint32_t des0; + uint32_t des1; + uint32_t addr; + uint32_t des2; +} __attribute__ ((packed)); + +#define VELOCITY_DES0_RMBC(_n) (((_n) >> 16) & 0x1fff) +#define VELOCITY_DES0_OWN (1 << 31) +#define VELOCITY_DES0_TERR (1 << 15) +#define VELOCITY_DES0_RXOK (1 << 15) +#define VELOCITY_DES0_FDX (1 << 14) +#define VELOCITY_DES0_GMII (1 << 13) +#define VELOCITY_DES0_LNKFL (1 << 12) +#define VELOCITY_DES0_SHDN (1 << 10) +#define VELOCITY_DES0_CRS (1 << 9) +#define VELOCITY_DES0_CDH (1 << 8) +#define VELOCITY_DES0_ABT (1 << 7) +#define VELOCITY_DES0_OWT (1 << 6) +#define VELOCITY_DES0_OWC (1 << 5) +#define VELOCITY_DES0_COLS (1 << 4) + +#define VELOCITY_DES0_RXSHDN (1 << 30) +#define VELOCITY_DES0_RXER (1 << 5) +#define VELOCITY_DES0_RLE (1 << 4) +#define VELOCITY_DES0_CE (1 << 3) +#define VELOCITY_DES0_FAE (1 << 2) +#define VELOCITY_DES0_CRC (1 << 1) +#define VELOCITY_DES0_RX_ERR ( VELOCITY_DES0_RXER | \ + VELOCITY_DES0_RLE | \ + VELOCITY_DES0_CE | \ + VELOCITY_DES0_FAE | \ + VELOCITY_DES0_CRC ) + +/** TX descriptor fragment number */ +#define VELOCITY_DES1_FRAG(_n) (((_n + 1) & 0xf) << 28) +#define VELOCITY_DES1_TCPLS ((1 << 24) | (1 << 25)) +#define VELOCITY_DES1_INTR (1 << 23) +#define VELOCITY_DES1_PIC (1 << 22) +#define VELOCITY_DES1_VETAG (1 << 21) +#define VELOCITY_DES1_IPCK (1 << 20) +#define VELOCITY_DES1_UDPCK (1 << 19) +#define VELOCITY_DES1_TCPCK (1 << 18) +#define VELOCITY_DES1_JMBO (1 << 17) +#define VELOCITY_DES1_CRC (1 << 16) + +#define VELOCITY_DES2_IC (1 << 31) +#define VELOCITY_DES2_SIZE(_n) (((_n) & 0x1fff) << 16) + +/** Number of receive descriptors + * + * Must be a multiple of 4 (hardware requirement). + */ +#define VELOCITY_RXDESC_NUM 8 +#define VELOCITY_RXDESC_SIZE \ + ( VELOCITY_RXDESC_NUM * sizeof ( struct velocity_rx_descriptor ) ) + +/** Number of transmit descriptors */ +#define VELOCITY_TXDESC_NUM 8 +#define VELOCITY_TXDESC_SIZE \ + ( VELOCITY_TXDESC_NUM * sizeof ( struct velocity_tx_descriptor ) ) + +/** Descriptor alignment */ +#define VELOCITY_RING_ALIGN 64 + +/** Receive buffer length */ +#define VELOCITY_RX_MAX_LEN 1536 + +/** MAC address registers */ +#define VELOCITY_MAC0 0x00 +#define VELOCITY_MAC1 0x01 +#define VELOCITY_MAC2 0x02 +#define VELOCITY_MAC3 0x03 +#define VELOCITY_MAC4 0x04 +#define VELOCITY_MAC5 0x05 + +/** Receive control register */ +#define VELOCITY_RCR 0x06 +#define RHINE_RCR_SYMERR_ACCEPT (1 << 7) /*< Accept symbol error */ +#define RHINE_RCR_FILTER_ACCEPT (1 << 6) /*< Accept based on filter */ +#define RHINE_RCR_LONG_ACCEPT (1 << 5) /*< Accept long packets */ +#define RHINE_RCR_PROMISC (1 << 4) /*< Promiscuous mode */ +#define RHINE_RCR_BCAST_ACCEPT (1 << 3) /*< Accept broadcast */ +#define RHINE_RCR_MCAST_ACCEPT (1 << 2) /*< Accept multicast */ +#define RHINE_RCR_RUNT_ACCEPT (1 << 1) /*< Accept runt frames */ +#define RHINE_RCR_ERR_ACCEPT (1 << 0) /*< Accept erroneous frames */ + +/** Transmit control register */ +#define VELOCITY_TCR 0x07 +#define VELOCITY_TCR_LB0 (1 << 0) /*< Loopback control */ +#define VELOCITY_TCR_LB1 (1 << 1) /*< Loopback control */ +#define VELOCITY_TCR_COLTMC0 (1 << 2) /*< Collision retry control */ +#define VELOCITY_TCR_COLTMC1 (1 << 3) /*< Collision retry control */ + +/** Command register 0 (set) */ +#define VELOCITY_CRS0 0x08 +#define VELOCITY_CR0_TXON (1 << 3) /*< Transmit enable */ +#define VELOCITY_CR0_RXON (1 << 2) /*< Receive enable */ +#define VELOCITY_CR0_STOP (1 << 1) /*< Stop NIC */ +#define VELOCITY_CR0_START (1 << 0) /*< Start NIC */ + +/** Command register 1 (set) */ +#define VELOCITY_CRS1 0x09 +#define VELOCITY_CR1_SFRST (1 << 7) /*< Software reset */ +#define VELOCITY_CR1_TM1EN (1 << 6) /*< Perioding software counting */ +#define VELOCITY_CR1_TM0EN (1 << 5) /*< Single-shot software counting */ +#define VELOCITY_CR1_DPOLL (1 << 3) /*< Disable auto polling */ +#define VELOCITY_CR1_DISAU (1 << 0) /*< Unicast reception disable */ + +/** Command register 2 (set) */ +#define VELOCITY_CRS2 0x0A +#define VELOCITY_CR2_XONEN (1 << 7) /*< XON/XOFF mode enable */ +#define VELOCITY_CR2_FDXTFCEN (1 << 6) /*< FDX flow control TX */ +#define VELOCITY_CR2_FDXRFCEN (1 << 5) +#define VELOCITY_CR2_HDXFCEN (1 << 4) + +/** Command register 3 (set) */ +#define VELOCITY_CRS3 0x0B +#define VELOCITY_CR3_FOSRST (1 << 6) +#define VELOCITY_CR3_FPHYRST (1 << 5) +#define VELOCITY_CR3_DIAG (1 << 4) +#define VELOCITY_CR3_INTPCTL (1 << 2) +#define VELOCITY_CR3_GINTMSK1 (1 << 1) +#define VELOCITY_CR3_SWPEND (1 << 0) + +/** Command register 0 (clear) */ +#define VELOCITY_CRC0 0x0C + +/** Command register 1 (clear) */ +#define VELOCITY_CRC1 0x0D + +/** Command register 2 (clear */ +#define VELOCITY_CRC2 0x0E + +/** Command register 3 (clear */ +#define VELOCITY_CRC3 0x0F +#define VELOCITY_CAM0 0x10 +#define VELOCITY_CAM1 0x11 +#define VELOCITY_CAM2 0x12 +#define VELOCITY_CAM3 0x13 +#define VELOCITY_CAM4 0x14 +#define VELOCITY_CAM5 0x15 +#define VELOCITY_CAM6 0x16 +#define VELOCITY_CAM7 0x17 +#define VELOCITY_TXDESC_HI 0x18 /* Hi part of 64bit txdesc base addr */ +#define VELOCITY_DATABUF_HI 0x1D /* Hi part of 64bit data buffer addr */ +#define VELOCITY_INTCTL0 0x20 /* interrupt control register */ +#define VELOCITY_RXSUPPTHR 0x20 +#define VELOCITY_TXSUPPTHR 0x20 +#define VELOCITY_INTHOLDOFF 0x20 +#define VELOCITY_INTCTL1 0x21 /* interrupt control register */ +#define VELOCITY_TXHOSTERR 0x22 /* TX host error status */ +#define VELOCITY_RXHOSTERR 0x23 /* RX host error status */ + +/** Interrupt status register 0 */ +#define VELOCITY_ISR0 0x24 +#define VELOCITY_ISR0_PTX3 (1 << 7) +#define VELOCITY_ISR0_PTX2 (1 << 6) +#define VELOCITY_ISR0_PTX1 (1 << 5) +#define VELOCITY_ISR0_PTX0 (1 << 4) +#define VELOCITY_ISR0_PTXI (1 << 3) +#define VELOCITY_ISR0_PRXI (1 << 2) +#define VELOCITY_ISR0_PPTXI (1 << 1) +#define VELOCITY_ISR0_PPRXI (1 << 0) + +/** Interrupt status register 1 */ +#define VELOCITY_ISR1 0x25 +#define VELOCITY_ISR1_SRCI (1 << 7) +#define VELOCITY_ISR1_LSTPEI (1 << 6) +#define VELOCITY_ISR1_LSTEI (1 << 5) +#define VELOCITY_ISR1_OVFL (1 << 4) +#define VELOCITY_ISR1_FLONI (1 << 3) +#define VELOCITY_ISR1_RACEI (1 << 2) + +/** Interrupt status register 2 */ +#define VELOCITY_ISR2 0x26 +#define VELOCITY_ISR2_HFLD (1 << 7) +#define VELOCITY_ISR2_UDPI (1 << 6) +#define VELOCITY_ISR2_MIBFI (1 << 5) +#define VELOCITY_ISR2_SHDNII (1 << 4) +#define VELOCITY_ISR2_PHYI (1 << 3) +#define VELOCITY_ISR2_PWEI (1 << 2) +#define VELOCITY_ISR2_TMR1I (1 << 1) +#define VELOCITY_ISR2_TMR0I (1 << 0) + +/** Interrupt status register 3 */ +#define VELOCITY_ISR3 0x27 + +/** Interrupt mask register 0 */ +#define VELOCITY_IMR0 0x28 + +/** Interrupt mask register 1 */ +#define VELOCITY_IMR1 0x29 + +/** Interrupt mask register 2 */ +#define VELOCITY_IMR2 0x2a + +/** Interrupt mask register 3 */ +#define VELOCITY_IMR3 0x2b + +#define VELOCITY_TXSTS_PORT 0x2C /* Transmit status port (???) */ +#define VELOCITY_TXQCSRS 0x30 /* TX queue ctl/status set */ + +#define VELOCITY_TXQCSRS_DEAD3 (1 << 15) +#define VELOCITY_TXQCSRS_WAK3 (1 << 14) +#define VELOCITY_TXQCSRS_ACT3 (1 << 13) +#define VELOCITY_TXQCSRS_RUN3 (1 << 12) +#define VELOCITY_TXQCSRS_DEAD2 (1 << 11) +#define VELOCITY_TXQCSRS_WAK2 (1 << 10) +#define VELOCITY_TXQCSRS_ACT2 (1 << 9) +#define VELOCITY_TXQCSRS_RUN2 (1 << 8) +#define VELOCITY_TXQCSRS_DEAD1 (1 << 7) +#define VELOCITY_TXQCSRS_WAK1 (1 << 6) +#define VELOCITY_TXQCSRS_ACT1 (1 << 5) +#define VELOCITY_TXQCSRS_RUN1 (1 << 4) +#define VELOCITY_TXQCSRS_DEAD0 (1 << 3) +#define VELOCITY_TXQCSRS_WAK0 (1 << 2) +#define VELOCITY_TXQCSRS_ACT0 (1 << 1) +#define VELOCITY_TXQCSRS_RUN0 (1 << 0) + +#define VELOCITY_RXQCSRS 0x32 /* RX queue ctl/status set */ +#define VELOCITY_RXQCSRC 0x36 + +#define VELOCITY_RXQCSR_DEAD (1 << 3) +#define VELOCITY_RXQCSR_WAK (1 << 2) +#define VELOCITY_RXQCSR_ACT (1 << 1) +#define VELOCITY_RXQCSR_RUN (1 << 0) + +#define VELOCITY_TXQCSRC 0x34 /* TX queue ctl/status clear */ +#define VELOCITY_RXQCSRC 0x36 /* RX queue ctl/status clear */ +#define VELOCITY_RXDESC_ADDR_LO 0x38 /* RX desc base addr (lo 32 bits) */ +#define VELOCITY_RXDESC_CONSIDX 0x3C /* Current RX descriptor index */ +#define VELOCITY_TXQTIMER 0x3E /* TX queue timer pend register */ +#define VELOCITY_RXQTIMER 0x3F /* RX queue timer pend register */ +#define VELOCITY_TXDESC_ADDR_LO0 0x40 /* TX desc0 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO1 0x44 /* TX desc1 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO2 0x48 /* TX desc2 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO3 0x4C /* TX desc3 base addr (lo 32 bits) */ +#define VELOCITY_RXDESCNUM 0x50 /* Size of RX desc ring */ +#define VELOCITY_TXDESCNUM 0x52 /* Size of TX desc ring */ +#define VELOCITY_TXDESC_CONSIDX0 0x54 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX1 0x56 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX2 0x58 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX3 0x5A /* Current TX descriptor index */ +#define VELOCITY_TX_PAUSE_TIMER 0x5C /* TX pause frame timer */ +#define VELOCITY_RXDESC_RESIDUECNT 0x5E /* RX descriptor residue count */ +#define VELOCITY_FIFOTEST0 0x60 /* FIFO test register */ +#define VELOCITY_FIFOTEST1 0x64 /* FIFO test register */ +#define VELOCITY_CAMADDR 0x68 /* CAM address register */ +#define VELOCITY_CAMCTL 0x69 /* CAM control register */ +#define VELOCITY_MIICFG 0x6C /* MII port config register */ +#define VELOCITY_MIISR 0x6D /* MII port status register */ +#define VELOCITY_MIISR_IDLE (1 << 7) +#define VELOCITY_PHYSTS0 0x6E /* PHY status register */ +#define VELOCITY_PHYSTS0_LINK (1 << 6) +#define VELOCITY_PHYSTS1 0x6F /* PHY status register */ +#define VELOCITY_MIICR 0x70 /* MII command register */ +#define VELOCITY_MIICR_MAUTO (1 << 7) +#define VELOCITY_MIICR_RCMD (1 << 6) +#define VELOCITY_MIICR_WCMD (1 << 5) +#define VELOCITY_MIICR_MDPM (1 << 4) +#define VELOCITY_MIICR_MOUT (1 << 3) +#define VELOCITY_MIICR_MDO (1 << 2) +#define VELOCITY_MIICR_MDI (1 << 1) +#define VELOCITY_MIICR_MDC (1 << 0) + +#define VELOCITY_MIIADDR 0x71 /* MII address register */ +#define VELOCITY_MIIDATA 0x72 /* MII data register */ +#define VELOCITY_SSTIMER 0x74 /* single-shot timer */ +#define VELOCITY_PTIMER 0x76 /* periodic timer */ +#define VELOCITY_DMACFG0 0x7C /* DMA config 0 */ +#define VELOCITY_DMACFG1 0x7D /* DMA config 1 */ +#define VELOCITY_RXCFG 0x7E /* MAC RX config */ +#define VELOCITY_TXCFG 0x7F /* MAC TX config */ +#define VELOCITY_SWEEDATA 0x85 /* EEPROM software loaded data */ + +/** Chip Configuration Register A */ +#define VELOCITY_CFGA 0x78 +#define VELOCITY_CFGA_PACPI (1 << 0) + +/** Power Management Sticky Register */ +#define VELOCITY_STICKY 0x83 +#define VELOCITY_STICKY_DS0 (1 << 0) +#define VELOCITY_STICKY_DS1 (1 << 1) + +#define VELOCITY_EEWRDAT 0x8C /* EEPROM embedded write */ +#define VELOCITY_EECSUM 0x92 /* EEPROM checksum */ +#define VELOCITY_EECSR 0x93 /* EEPROM control/status */ +#define VELOCITY_EECSR_RELOAD (1 << 5) +#define VELOCITY_EERDDAT 0x94 /* EEPROM embedded read */ +#define VELOCITY_EEADDR 0x96 /* EEPROM address */ +#define VELOCITY_EECMD 0x97 /* EEPROM embedded command */ + +/** A Velocity network card */ +struct velocity_nic { + /** Registers */ + void *regs; + /** MII interface */ + struct mii_interface mii; + /** Netdev */ + struct net_device *netdev; + + /** Receive descriptor ring */ + struct velocity_rx_descriptor *rx_ring; + /** Receive I/O buffers */ + struct io_buffer *rx_buffs[VELOCITY_RXDESC_NUM]; + /** Receive producer index */ + unsigned int rx_prod; + /** Receive consumer index */ + unsigned int rx_cons; + /** Receive commit number + * + * Used to fullfill the hardware requirement of returning receive buffers + * to the hardware only in blocks of 4. + */ + unsigned int rx_commit; + + /** Transmit descriptor ring */ + struct velocity_tx_descriptor *tx_ring; + /** Transmit producer index */ + unsigned int tx_prod; + /** Transmit consumer index */ + unsigned int tx_cons; +}; + +#endif /* _VELOCITY_H */ diff --git a/src/drivers/net/via-velocity.c b/src/drivers/net/via-velocity.c deleted file mode 100644 index e98e81a2..00000000 --- a/src/drivers/net/via-velocity.c +++ /dev/null @@ -1,1927 +0,0 @@ -/************************************************************************** -* via-velocity.c: Etherboot device driver for the VIA 6120 Gigabit -* Changes for Etherboot port: -* Copyright (c) 2006 by Timothy Legge -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -* 02110-1301, USA. -* -* This driver is based on: -* via-velocity.c: VIA Velocity VT6120, VT6122 Ethernet driver -* The changes are (c) Copyright 2004, Red Hat Inc. -* -* Additional fixes and clean up: Francois Romieu -* -* Original code: -* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. -* All rights reserved. -* Author: Chuang Liang-Shing, AJ Jiang -* -* Linux Driver Version 2.6.15.4 -* -* REVISION HISTORY: -* ================ -* -* v1.0 03-06-2006 timlegge Initial port of Linux driver -* -* Indent Options: indent -kr -i8 -*************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "etherboot.h" -#include "nic.h" -#include -#include - -#include "via-velocity.h" - -typedef int pci_power_t; - -#define PCI_D0 ((int) 0) -#define PCI_D1 ((int) 1) -#define PCI_D2 ((int) 2) -#define PCI_D3hot ((int) 3) -#define PCI_D3cold ((int) 4) -#define PCI_POWER_ERROR ((int) -1) - - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) - -//FIXME: Move to pci.c -int pci_set_power_state(struct pci_device *dev, int state); - -/* FIXME: Move BASE to the private structure */ -static u32 BASE; - -/* NIC specific static variables go here */ -#define VELOCITY_PARAM(N,D) \ - static const int N[MAX_UNITS]=OPTION_DEFAULT; -/* MODULE_PARM(N, "1-" __MODULE_STRING(MAX_UNITS) "i");\ - MODULE_PARM_DESC(N, D); */ - -VELOCITY_PARAM(RxDescriptors, "Number of receive descriptors"); -VELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors"); - - -#define VLAN_ID_MIN 0 -#define VLAN_ID_MAX 4095 -#define VLAN_ID_DEF 0 -/* VID_setting[] is used for setting the VID of NIC. - 0: default VID. - 1-4094: other VIDs. -*/ -VELOCITY_PARAM(VID_setting, "802.1Q VLAN ID"); - -#define RX_THRESH_MIN 0 -#define RX_THRESH_MAX 3 -#define RX_THRESH_DEF 0 -/* rx_thresh[] is used for controlling the receive fifo threshold. - 0: indicate the rxfifo threshold is 128 bytes. - 1: indicate the rxfifo threshold is 512 bytes. - 2: indicate the rxfifo threshold is 1024 bytes. - 3: indicate the rxfifo threshold is store & forward. -*/ -VELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); - -#define DMA_LENGTH_MIN 0 -#define DMA_LENGTH_MAX 7 -#define DMA_LENGTH_DEF 0 - -/* DMA_length[] is used for controlling the DMA length - 0: 8 DWORDs - 1: 16 DWORDs - 2: 32 DWORDs - 3: 64 DWORDs - 4: 128 DWORDs - 5: 256 DWORDs - 6: SF(flush till emply) - 7: SF(flush till emply) -*/ -VELOCITY_PARAM(DMA_length, "DMA length"); - -#define TAGGING_DEF 0 -/* enable_tagging[] is used for enabling 802.1Q VID tagging. - 0: disable VID seeting(default). - 1: enable VID setting. -*/ -VELOCITY_PARAM(enable_tagging, "Enable 802.1Q tagging"); - -#define IP_ALIG_DEF 0 -/* IP_byte_align[] is used for IP header DWORD byte aligned - 0: indicate the IP header won't be DWORD byte aligned.(Default) . - 1: indicate the IP header will be DWORD byte aligned. - In some environment, the IP header should be DWORD byte aligned, - or the packet will be droped when we receive it. (eg: IPVS) -*/ -VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); - -#define TX_CSUM_DEF 1 -/* txcsum_offload[] is used for setting the checksum offload ability of NIC. - (We only support RX checksum offload now) - 0: disable csum_offload[checksum offload - 1: enable checksum offload. (Default) -*/ -VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload"); - -#define FLOW_CNTL_DEF 1 -#define FLOW_CNTL_MIN 1 -#define FLOW_CNTL_MAX 5 - -/* flow_control[] is used for setting the flow control ability of NIC. - 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR. - 2: enable TX flow control. - 3: enable RX flow control. - 4: enable RX/TX flow control. - 5: disable -*/ -VELOCITY_PARAM(flow_control, "Enable flow control ability"); - -#define MED_LNK_DEF 0 -#define MED_LNK_MIN 0 -#define MED_LNK_MAX 4 -/* speed_duplex[] is used for setting the speed and duplex mode of NIC. - 0: indicate autonegotiation for both speed and duplex mode - 1: indicate 100Mbps half duplex mode - 2: indicate 100Mbps full duplex mode - 3: indicate 10Mbps half duplex mode - 4: indicate 10Mbps full duplex mode - - Note: - if EEPROM have been set to the force mode, this option is ignored - by driver. -*/ -VELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); - -#define VAL_PKT_LEN_DEF 0 -/* ValPktLen[] is used for setting the checksum offload ability of NIC. - 0: Receive frame with invalid layer 2 length (Default) - 1: Drop frame with invalid layer 2 length -*/ -VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame"); - -#define WOL_OPT_DEF 0 -#define WOL_OPT_MIN 0 -#define WOL_OPT_MAX 7 -/* wol_opts[] is used for controlling wake on lan behavior. - 0: Wake up if recevied a magic packet. (Default) - 1: Wake up if link status is on/off. - 2: Wake up if recevied an arp packet. - 4: Wake up if recevied any unicast packet. - Those value can be sumed up to support more than one option. -*/ -VELOCITY_PARAM(wol_opts, "Wake On Lan options"); - -#define INT_WORKS_DEF 20 -#define INT_WORKS_MIN 10 -#define INT_WORKS_MAX 64 - -VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); - -/* The descriptors for this card are required to be aligned on -64 byte boundaries. As the align attribute does not guarantee alignment -greater than the alignment of the start address (which for Etherboot -is 16 bytes of alignment) it requires some extra steps. Add 64 to the -size of the array and the init_ring adjusts the alignment */ - -/* Define the TX Descriptor */ -static u8 tx_ring[TX_DESC_DEF * sizeof(struct tx_desc) + 64]; - -/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor. -All descriptors point to a part of this buffer */ -static u8 txb[(TX_DESC_DEF * PKT_BUF_SZ) + 64]; - -/* Define the RX Descriptor */ -static u8 rx_ring[RX_DESC_DEF * sizeof(struct rx_desc) + 64]; - -/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor - All descriptors point to a part of this buffer */ -static u8 rxb[(RX_DESC_DEF * PKT_BUF_SZ) + 64]; - -static void velocity_init_info(struct pci_device *pdev, - struct velocity_info *vptr, - struct velocity_info_tbl *info); -static int velocity_get_pci_info(struct velocity_info *, - struct pci_device *pdev); -static int velocity_open(struct nic *nic, struct pci_device *pci); - -static int velocity_soft_reset(struct velocity_info *vptr); -static void velocity_init_cam_filter(struct velocity_info *vptr); -static void mii_init(struct velocity_info *vptr, u32 mii_status); -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr); -static void velocity_print_link_status(struct velocity_info *vptr); -static void safe_disable_mii_autopoll(struct mac_regs *regs); -static void enable_flow_control_ability(struct velocity_info *vptr); -static void enable_mii_autopoll(struct mac_regs *regs); -static int velocity_mii_read(struct mac_regs *, u8 byIdx, u16 * pdata); -static int velocity_mii_write(struct mac_regs *, u8 byMiiAddr, u16 data); -static u32 mii_check_media_mode(struct mac_regs *regs); -static u32 check_connection_type(struct mac_regs *regs); -static int velocity_set_media_mode(struct velocity_info *vptr, - u32 mii_status); - - -/* - * Internal board variants. At the moment we have only one - */ - -static struct velocity_info_tbl chip_info_table[] = { - {CHIP_TYPE_VT6110, - "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1, - 0x00FFFFFFUL}, - {0, NULL, 0, 0, 0} -}; - -/** - * velocity_set_int_opt - parser for integer options - * @opt: pointer to option value - * @val: value the user requested (or -1 for default) - * @min: lowest value allowed - * @max: highest value allowed - * @def: default value - * @name: property name - * @dev: device name - * - * Set an integer property in the module options. This function does - * all the verification and checking as well as reporting so that - * we don't duplicate code for each option. - */ - -static void velocity_set_int_opt(int *opt, int val, int min, int max, - int def, char *name, const char *devname) -{ - if (val == -1) { - printf("%s: set value of parameter %s to %d\n", - devname, name, def); - *opt = def; - } else if (val < min || val > max) { - printf - ("%s: the value of parameter %s is invalid, the valid range is (%d-%d)\n", - devname, name, min, max); - *opt = def; - } else { - printf("%s: set value of parameter %s to %d\n", - devname, name, val); - *opt = val; - } -} - -/** - * velocity_set_bool_opt - parser for boolean options - * @opt: pointer to option value - * @val: value the user requested (or -1 for default) - * @def: default value (yes/no) - * @flag: numeric value to set for true. - * @name: property name - * @dev: device name - * - * Set a boolean property in the module options. This function does - * all the verification and checking as well as reporting so that - * we don't duplicate code for each option. - */ - -static void velocity_set_bool_opt(u32 * opt, int val, int def, u32 flag, - char *name, const char *devname) -{ - (*opt) &= (~flag); - if (val == -1) { - printf("%s: set parameter %s to %s\n", - devname, name, def ? "TRUE" : "FALSE"); - *opt |= (def ? flag : 0); - } else if (val < 0 || val > 1) { - printf - ("%s: the value of parameter %s is invalid, the valid range is (0-1)\n", - devname, name); - *opt |= (def ? flag : 0); - } else { - printf("%s: set parameter %s to %s\n", - devname, name, val ? "TRUE" : "FALSE"); - *opt |= (val ? flag : 0); - } -} - -/** - * velocity_get_options - set options on device - * @opts: option structure for the device - * @index: index of option to use in module options array - * @devname: device name - * - * Turn the module and command options into a single structure - * for the current device - */ - -static void velocity_get_options(struct velocity_opt *opts, int index, - const char *devname) -{ - - /* FIXME Do the options need to be configurable */ - velocity_set_int_opt(&opts->rx_thresh, -1, RX_THRESH_MIN, - RX_THRESH_MAX, RX_THRESH_DEF, "rx_thresh", - devname); - velocity_set_int_opt(&opts->DMA_length, DMA_length[index], - DMA_LENGTH_MIN, DMA_LENGTH_MAX, - DMA_LENGTH_DEF, "DMA_length", devname); - velocity_set_int_opt(&opts->numrx, RxDescriptors[index], - RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, - "RxDescriptors", devname); - velocity_set_int_opt(&opts->numtx, TxDescriptors[index], - TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, - "TxDescriptors", devname); - velocity_set_int_opt(&opts->vid, VID_setting[index], VLAN_ID_MIN, - VLAN_ID_MAX, VLAN_ID_DEF, "VID_setting", - devname); - velocity_set_bool_opt(&opts->flags, enable_tagging[index], - TAGGING_DEF, VELOCITY_FLAGS_TAGGING, - "enable_tagging", devname); - velocity_set_bool_opt(&opts->flags, txcsum_offload[index], - TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, - "txcsum_offload", devname); - velocity_set_int_opt(&opts->flow_cntl, flow_control[index], - FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, - "flow_control", devname); - velocity_set_bool_opt(&opts->flags, IP_byte_align[index], - IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, - "IP_byte_align", devname); - velocity_set_bool_opt(&opts->flags, ValPktLen[index], - VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, - "ValPktLen", devname); - velocity_set_int_opt((void *) &opts->spd_dpx, speed_duplex[index], - MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, - "Media link mode", devname); - velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], - WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, - "Wake On Lan options", devname); - velocity_set_int_opt((int *) &opts->int_works, int_works[index], - INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, - "Interrupt service works", devname); - opts->numrx = (opts->numrx & ~3); -} - -/** - * velocity_init_cam_filter - initialise CAM - * @vptr: velocity to program - * - * Initialize the content addressable memory used for filters. Load - * appropriately according to the presence of VLAN - */ - -static void velocity_init_cam_filter(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - - /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ - WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); - WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); - - /* Disable all CAMs */ - memset(vptr->vCAMmask, 0, sizeof(u8) * 8); - memset(vptr->mCAMmask, 0, sizeof(u8) * 8); - mac_set_cam_mask(regs, vptr->vCAMmask, VELOCITY_VLAN_ID_CAM); - mac_set_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM); - - /* Enable first VCAM */ - if (vptr->flags & VELOCITY_FLAGS_TAGGING) { - /* If Tagging option is enabled and VLAN ID is not zero, then - turn on MCFG_RTGOPT also */ - if (vptr->options.vid != 0) - WORD_REG_BITS_ON(MCFG_RTGOPT, ®s->MCFG); - - mac_set_cam(regs, 0, (u8 *) & (vptr->options.vid), - VELOCITY_VLAN_ID_CAM); - vptr->vCAMmask[0] |= 1; - mac_set_cam_mask(regs, vptr->vCAMmask, - VELOCITY_VLAN_ID_CAM); - } else { - u16 temp = 0; - mac_set_cam(regs, 0, (u8 *) & temp, VELOCITY_VLAN_ID_CAM); - temp = 1; - mac_set_cam_mask(regs, (u8 *) & temp, - VELOCITY_VLAN_ID_CAM); - } -} - -static inline void velocity_give_many_rx_descs(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - int avail, dirty, unusable; - - /* - * RD number must be equal to 4X per hardware spec - * (programming guide rev 1.20, p.13) - */ - if (vptr->rd_filled < 4) - return; - - wmb(); - - unusable = vptr->rd_filled & 0x0003; - dirty = vptr->rd_dirty - unusable; - for (avail = vptr->rd_filled & 0xfffc; avail; avail--) { - dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; -// printf("return dirty: %d\n", dirty); - vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC; - } - - writew(vptr->rd_filled & 0xfffc, ®s->RBRDU); - vptr->rd_filled = unusable; -} - -static int velocity_rx_refill(struct velocity_info *vptr) -{ - int dirty = vptr->rd_dirty, done = 0, ret = 0; - -// printf("rx_refill - rd_curr = %d, dirty = %d\n", vptr->rd_curr, dirty); - do { - struct rx_desc *rd = vptr->rd_ring + dirty; - - /* Fine for an all zero Rx desc at init time as well */ - if (rd->rdesc0.owner == OWNED_BY_NIC) - break; -// printf("rx_refill - after owner %d\n", dirty); - - rd->inten = 1; - rd->pa_high = 0; - rd->rdesc0.len = cpu_to_le32(vptr->rx_buf_sz);; - - done++; - dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; - } while (dirty != vptr->rd_curr); - - if (done) { -// printf("\nGive Back Desc\n"); - vptr->rd_dirty = dirty; - vptr->rd_filled += done; - velocity_give_many_rx_descs(vptr); - } - - return ret; -} - -extern void hex_dump(const char *data, const unsigned int len); -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int velocity_poll(struct nic *nic, int retrieve) -{ - /* Work out whether or not there's an ethernet packet ready to - * read. Return 0 if not. - */ - - int rd_curr = vptr->rd_curr % RX_DESC_DEF; - struct rx_desc *rd = &(vptr->rd_ring[rd_curr]); - - if (rd->rdesc0.owner == OWNED_BY_NIC) - return 0; - rmb(); - - if ( ! retrieve ) return 1; - - /* - * Don't drop CE or RL error frame although RXOK is off - */ - if ((rd->rdesc0.RSR & RSR_RXOK) - || (!(rd->rdesc0.RSR & RSR_RXOK) - && (rd->rdesc0.RSR & (RSR_CE | RSR_RL)))) { - - nic->packetlen = rd->rdesc0.len; - // ptr->rxb + (rd_curr * PKT_BUF_SZ) - memcpy(nic->packet, bus_to_virt(rd->pa_low), - nic->packetlen - 4); - - vptr->rd_curr++; - vptr->rd_curr = vptr->rd_curr % RX_DESC_DEF; - velocity_rx_refill(vptr); - return 1; /* Remove this line once this method is implemented */ - } - return 0; -} - -#define TX_TIMEOUT (1000); -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void velocity_transmit(struct nic *nic, const char *dest, /* Destination */ - unsigned int type, /* Type */ - unsigned int size, /* size */ - const char *packet) -{ /* Packet */ - u16 nstype; - u32 to; - u8 *ptxb; - unsigned int pktlen; - struct tx_desc *td_ptr; - - int entry = vptr->td_curr % TX_DESC_DEF; - td_ptr = &(vptr->td_rings[entry]); - - /* point to the current txb incase multiple tx_rings are used */ - ptxb = vptr->txb + (entry * PKT_BUF_SZ); - memcpy(ptxb, dest, ETH_ALEN); /* Destination */ - memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* Source */ - nstype = htons((u16) type); /* Type */ - memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* Type */ - memcpy(ptxb + ETH_HLEN, packet, size); - - td_ptr->tdesc1.TCPLS = TCPLS_NORMAL; - td_ptr->tdesc1.TCR = TCR0_TIC; - td_ptr->td_buf[0].queue = 0; - - size += ETH_HLEN; - while (size < ETH_ZLEN) /* pad to min length */ - ptxb[size++] = '\0'; - - if (size < ETH_ZLEN) { -// printf("Padd that packet\n"); - pktlen = ETH_ZLEN; -// memcpy(ptxb, skb->data, skb->len); - memset(ptxb + size, 0, ETH_ZLEN - size); - - vptr->td_rings[entry].tdesc0.pktsize = pktlen; - vptr->td_rings[entry].td_buf[0].pa_low = virt_to_bus(ptxb); - vptr->td_rings[entry].td_buf[0].pa_high &= - cpu_to_le32(0xffff0000UL); - vptr->td_rings[entry].td_buf[0].bufsize = - vptr->td_rings[entry].tdesc0.pktsize; - vptr->td_rings[entry].tdesc1.CMDZ = 2; - } else { -// printf("Correct size packet\n"); - td_ptr->tdesc0.pktsize = size; - td_ptr->td_buf[0].pa_low = virt_to_bus(ptxb); - td_ptr->td_buf[0].pa_high = 0; - td_ptr->td_buf[0].bufsize = td_ptr->tdesc0.pktsize; -// tdinfo->nskb_dma = 1; - td_ptr->tdesc1.CMDZ = 2; - } - - if (vptr->flags & VELOCITY_FLAGS_TAGGING) { - td_ptr->tdesc1.pqinf.VID = (vptr->options.vid & 0xfff); - td_ptr->tdesc1.pqinf.priority = 0; - td_ptr->tdesc1.pqinf.CFI = 0; - td_ptr->tdesc1.TCR |= TCR0_VETAG; - } - - vptr->td_curr = (entry + 1); - - { - - int prev = entry - 1; - - if (prev < 0) - prev = TX_DESC_DEF - 1; - td_ptr->tdesc0.owner |= OWNED_BY_NIC; - td_ptr = &(vptr->td_rings[prev]); - td_ptr->td_buf[0].queue = 1; - mac_tx_queue_wake(vptr->mac_regs, 0); - - } - - to = currticks() + TX_TIMEOUT; - while ((td_ptr->tdesc0.owner & OWNED_BY_NIC) && (currticks() < to)); /* wait */ - - if (currticks() >= to) { - printf("TX Time Out"); - } - -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void velocity_disable(struct nic *nic __unused) -{ - /* put the card in its initial state */ - /* This function serves 3 purposes. - * This disables DMA and interrupts so we don't receive - * unexpected packets or interrupts from the card after - * etherboot has finished. - * This frees resources so etherboot may use - * this driver on another interface - * This allows etherboot to reinitialize the interface - * if something is something goes wrong. - */ - struct mac_regs *regs = vptr->mac_regs; - mac_disable_int(regs); - writel(CR0_STOP, ®s->CR0Set); - writew(0xFFFF, ®s->TDCSRClr); - writeb(0xFF, ®s->RDCSRClr); - safe_disable_mii_autopoll(regs); - mac_clear_isr(regs); - - /* Power down the chip */ -// pci_set_power_state(vptr->pdev, PCI_D3hot); - - vptr->flags &= (~VELOCITY_FLAGS_OPENED); -} - -/************************************************************************** -IRQ - handle interrupts -***************************************************************************/ -static void velocity_irq(struct nic *nic __unused, irq_action_t action) -{ - /* This routine is somewhat optional. Etherboot itself - * doesn't use interrupts, but they are required under some - * circumstances when we're acting as a PXE stack. - * - * If you don't implement this routine, the only effect will - * be that your driver cannot be used via Etherboot's UNDI - * API. This won't affect programs that use only the UDP - * portion of the PXE API, such as pxelinux. - */ - - switch (action) { - case DISABLE: - case ENABLE: - /* Set receive interrupt enabled/disabled state */ - /* - outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled, - nic->ioaddr + IntrMaskRegister ); - */ - break; - case FORCE: - /* Force NIC to generate a receive interrupt */ - /* - outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister ); - */ - break; - } -} - -static struct nic_operations velocity_operations = { - .connect = dummy_connect, - .poll = velocity_poll, - .transmit = velocity_transmit, - .irq = velocity_irq, -}; - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -***************************************************************************/ -static int velocity_probe( struct nic *nic, struct pci_device *pci) -{ - int ret, i; - struct mac_regs *regs; - - printf("via-velocity.c: Found %s Vendor=0x%hX Device=0x%hX\n", - pci->id->name, pci->vendor, pci->device); - - /* point to private storage */ - vptr = &vptx; - info = chip_info_table; - - velocity_init_info(pci, vptr, info); - -//FIXME: pci_enable_device(pci); -//FIXME: pci_set_power_state(pci, PCI_D0); - - ret = velocity_get_pci_info(vptr, pci); - if (ret < 0) { - printf("Failed to find PCI device.\n"); - return 0; - } - - regs = ioremap(vptr->memaddr, vptr->io_size); - if (regs == NULL) { - printf("Unable to remap io\n"); - return 0; - } - - vptr->mac_regs = regs; - - BASE = vptr->ioaddr; - - printf("Chip ID: %hX\n", vptr->chip_id); - - for (i = 0; i < 6; i++) - nic->node_addr[i] = readb(®s->PAR[i]); - - DBG ( "%s: %s at ioaddr %#hX\n", pci->id->name, eth_ntoa ( nic->node_addr ), - (unsigned int) BASE ); - - velocity_get_options(&vptr->options, 0, pci->id->name); - - /* - * Mask out the options cannot be set to the chip - */ - vptr->options.flags &= 0x00FFFFFFUL; //info->flags = 0x00FFFFFFUL; - - /* - * Enable the chip specified capbilities - */ - - vptr->flags = - vptr->options. - flags | (0x00FFFFFFUL /*info->flags */ & 0xFF000000UL); - - vptr->wol_opts = vptr->options.wol_opts; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - - vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - - if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) { - printf("features missing\n"); - } - - /* and leave the chip powered down */ -// FIXME: pci_set_power_state(pci, PCI_D3hot); - - check_connection_type(vptr->mac_regs); - velocity_open(nic, pci); - - /* store NIC parameters */ - nic->nic_op = &velocity_operations; - return 1; -} - -//#define IORESOURCE_IO 0x00000100 /* Resource type */ - -/** - * velocity_init_info - init private data - * @pdev: PCI device - * @vptr: Velocity info - * @info: Board type - * - * Set up the initial velocity_info struct for the device that has been - * discovered. - */ - -static void velocity_init_info(struct pci_device *pdev, - struct velocity_info *vptr, - struct velocity_info_tbl *info) -{ - memset(vptr, 0, sizeof(struct velocity_info)); - - vptr->pdev = pdev; - vptr->chip_id = info->chip_id; - vptr->io_size = info->io_size; - vptr->num_txq = info->txqueue; - vptr->multicast_limit = MCAM_SIZE; - - printf - ("chip_id: 0x%hX, io_size: %d, num_txq %d, multicast_limit: %d\n", - vptr->chip_id, (unsigned int) vptr->io_size, vptr->num_txq, - vptr->multicast_limit); - printf("Name: %s\n", info->name); - -// spin_lock_init(&vptr->lock); -// INIT_LIST_HEAD(&vptr->list); -} - -/** - * velocity_get_pci_info - retrieve PCI info for device - * @vptr: velocity device - * @pdev: PCI device it matches - * - * Retrieve the PCI configuration space data that interests us from - * the kernel PCI layer - */ - -#define IORESOURCE_IO 0x00000100 /* Resource type */ -#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ - -#define IORESOURCE_MEM 0x00000200 -#define BAR_0 0 -#define BAR_1 1 -#define BAR_5 5 -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -//#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -// #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) - -unsigned long pci_resource_flags(struct pci_device *pdev, unsigned int bar) -{ - uint32_t l, sz; - unsigned long flags = 0; - - pci_read_config_dword(pdev, bar, &l); - pci_write_config_dword(pdev, bar, ~0); - pci_read_config_dword(pdev, bar, &sz); - pci_write_config_dword(pdev, bar, l); - - if (!sz || sz == 0xffffffff) - printf("Weird size\n"); - if (l == 0xffffffff) - l = 0; - if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { - /* sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK); - if (!sz) - continue; - res->start = l & PCI_BASE_ADDRESS_MEM_MASK; - */ flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; - printf("Memory Resource\n"); - } else { - // sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); - /// if (!sz) - /// continue; -// res->start = l & PCI_BASE_ADDRESS_IO_MASK; - flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; - printf("I/O Resource\n"); - } - if (flags & PCI_BASE_ADDRESS_SPACE_IO) { - printf("Why is it here\n"); - flags |= IORESOURCE_IO; - } else { - printf("here\n"); -//flags &= ~IORESOURCE_IO; - } - - - if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) - flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - - - return flags; -} -static int velocity_get_pci_info(struct velocity_info *vptr, - struct pci_device *pdev) -{ - if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) { - printf("DEBUG: pci_read_config_byte failed\n"); - return -1; - } - - adjust_pci_device(pdev); - - vptr->ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); - vptr->memaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_1); - - printf("Looking for I/O Resource - Found:"); - if (! - (pci_resource_flags(pdev, PCI_BASE_ADDRESS_0) & IORESOURCE_IO)) - { - printf - ("DEBUG: region #0 is not an I/O resource, aborting.\n"); - return -1; - } - - printf("Looking for Memory Resource - Found:"); - if ((pci_resource_flags(pdev, PCI_BASE_ADDRESS_1) & IORESOURCE_IO)) { - printf("DEBUG: region #1 is an I/O resource, aborting.\n"); - return -1; - } - - if (pci_bar_size(pdev, PCI_BASE_ADDRESS_1) < 256) { - printf("DEBUG: region #1 is too small.\n"); - return -1; - } - vptr->pdev = pdev; - - return 0; -} - -/** - * velocity_print_link_status - link status reporting - * @vptr: velocity to report on - * - * Turn the link status of the velocity card into a kernel log - * description of the new link state, detailing speed and duplex - * status - */ - -static void velocity_print_link_status(struct velocity_info *vptr) -{ - - if (vptr->mii_status & VELOCITY_LINK_FAIL) { - printf("failed to detect cable link\n"); - } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - printf("Link autonegation"); - - if (vptr->mii_status & VELOCITY_SPEED_1000) - printf(" speed 1000M bps"); - else if (vptr->mii_status & VELOCITY_SPEED_100) - printf(" speed 100M bps"); - else - printf(" speed 10M bps"); - - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - printf(" full duplex\n"); - else - printf(" half duplex\n"); - } else { - printf("Link forced"); - switch (vptr->options.spd_dpx) { - case SPD_DPX_100_HALF: - printf(" speed 100M bps half duplex\n"); - break; - case SPD_DPX_100_FULL: - printf(" speed 100M bps full duplex\n"); - break; - case SPD_DPX_10_HALF: - printf(" speed 10M bps half duplex\n"); - break; - case SPD_DPX_10_FULL: - printf(" speed 10M bps full duplex\n"); - break; - default: - break; - } - } -} - -/** - * velocity_rx_reset - handle a receive reset - * @vptr: velocity we are resetting - * - * Reset the ownership and status for the receive ring side. - * Hand all the receive queue to the NIC. - */ - -static void velocity_rx_reset(struct velocity_info *vptr) -{ - - struct mac_regs *regs = vptr->mac_regs; - int i; - -//ptr->rd_dirty = vptr->rd_filled = vptr->rd_curr = 0; - - /* - * Init state, all RD entries belong to the NIC - */ - for (i = 0; i < vptr->options.numrx; ++i) - vptr->rd_ring[i].rdesc0.owner = OWNED_BY_NIC; - - writew(RX_DESC_DEF, ®s->RBRDU); - writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); - writew(0, ®s->RDIdx); - writew(RX_DESC_DEF - 1, ®s->RDCSize); -} - -/** - * velocity_init_registers - initialise MAC registers - * @vptr: velocity to init - * @type: type of initialisation (hot or cold) - * - * Initialise the MAC on a reset or on first set up on the - * hardware. - */ - -static void velocity_init_registers(struct nic *nic, - struct velocity_info *vptr, - enum velocity_init_type type) -{ - struct mac_regs *regs = vptr->mac_regs; - int i, mii_status; - - mac_wol_reset(regs); - - switch (type) { - case VELOCITY_INIT_RESET: - case VELOCITY_INIT_WOL: - -//netif_stop_queue(vptr->dev); - - /* - * Reset RX to prevent RX pointer not on the 4X location - */ - velocity_rx_reset(vptr); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); - - mii_status = velocity_get_opt_media_mode(vptr); - - if (velocity_set_media_mode(vptr, mii_status) != - VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - printf("Link Failed\n"); -// netif_wake_queue(vptr->dev); - } - - enable_flow_control_ability(vptr); - - mac_clear_isr(regs); - writel(CR0_STOP, ®s->CR0Clr); - //writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - ®s->CR0Set); - break; - - case VELOCITY_INIT_COLD: - default: - /* - * Do reset - */ - velocity_soft_reset(vptr); - mdelay(5); - - mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) { - writeb(nic->node_addr[i], &(regs->PAR[i])); - } - /* - * clear Pre_ACPI bit. - */ - BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); - mac_set_rx_thresh(regs, vptr->options.rx_thresh); - mac_set_dma_length(regs, vptr->options.DMA_length); - - writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); - /* - * Back off algorithm use original IEEE standard - */ - BYTE_REG_BITS_SET(CFGB_OFSET, - (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | - CFGB_BAKOPT), ®s->CFGB); - - /* - * Init CAM filter - */ - velocity_init_cam_filter(vptr); - - /* - * Set packet filter: Receive directed and broadcast address - */ -//FIXME Multicast velocity_set_multi(nic); - - /* - * Enable MII auto-polling - */ - enable_mii_autopoll(regs); - - vptr->int_mask = INT_MASK_DEF; - - writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); - writew(vptr->options.numrx - 1, ®s->RDCSize); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); - - writew(vptr->options.numtx - 1, ®s->TDCSize); - -// for (i = 0; i < vptr->num_txq; i++) { - writel(virt_to_le32desc(vptr->td_rings), - &(regs->TDBaseLo[0])); - mac_tx_queue_run(regs, 0); -// } - - init_flow_control_register(vptr); - - writel(CR0_STOP, ®s->CR0Clr); - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - ®s->CR0Set); - - mii_status = velocity_get_opt_media_mode(vptr); -// netif_stop_queue(vptr->dev); - - mii_init(vptr, mii_status); - - if (velocity_set_media_mode(vptr, mii_status) != - VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - printf("Link Faaailll\n"); -// netif_wake_queue(vptr->dev); - } - - enable_flow_control_ability(vptr); - mac_hw_mibs_init(regs); - mac_write_int_mask(vptr->int_mask, regs); - mac_clear_isr(regs); - - - } - velocity_print_link_status(vptr); -} - -/** - * velocity_soft_reset - soft reset - * @vptr: velocity to reset - * - * Kick off a soft reset of the velocity adapter and then poll - * until the reset sequence has completed before returning. - */ - -static int velocity_soft_reset(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - unsigned int i = 0; - - writel(CR0_SFRST, ®s->CR0Set); - - for (i = 0; i < W_MAX_TIMEOUT; i++) { - udelay(5); - if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) - break; - } - - if (i == W_MAX_TIMEOUT) { - writel(CR0_FORSRST, ®s->CR0Set); - /* FIXME: PCI POSTING */ - /* delay 2ms */ - mdelay(2); - } - return 0; -} - -/** - * velocity_init_rings - set up DMA rings - * @vptr: Velocity to set up - * - * Allocate PCI mapped DMA rings for the receive and transmit layer - * to use. - */ - -static int velocity_init_rings(struct velocity_info *vptr) -{ - - int idx; - - vptr->rd_curr = 0; - vptr->td_curr = 0; - memset(vptr->td_rings, 0, TX_DESC_DEF * sizeof(struct tx_desc)); - memset(vptr->rd_ring, 0, RX_DESC_DEF * sizeof(struct rx_desc)); -// memset(vptr->tx_buffs, 0, TX_DESC_DEF * PKT_BUF_SZ); - - - for (idx = 0; idx < RX_DESC_DEF; idx++) { - vptr->rd_ring[idx].rdesc0.RSR = 0; - vptr->rd_ring[idx].rdesc0.len = 0; - vptr->rd_ring[idx].rdesc0.reserved = 0; - vptr->rd_ring[idx].rdesc0.owner = 0; - vptr->rd_ring[idx].len = cpu_to_le32(vptr->rx_buf_sz); - vptr->rd_ring[idx].inten = 1; - vptr->rd_ring[idx].pa_low = - virt_to_bus(vptr->rxb + (RX_DESC_DEF * idx)); - vptr->rd_ring[idx].pa_high = 0; - vptr->rd_ring[idx].rdesc0.owner = OWNED_BY_NIC; - } - -/* for (i = 0; idx < TX_DESC_DEF; idx++ ) { - vptr->td_rings[idx].tdesc1.TCPLS = TCPLS_NORMAL; - vptr->td_rings[idx].tdesc1.TCR = TCR0_TIC; - vptr->td_rings[idx].td_buf[0].queue = 0; - vptr->td_rings[idx].tdesc0.owner = ~OWNED_BY_NIC; - vptr->td_rings[idx].tdesc0.pktsize = 0; - vptr->td_rings[idx].td_buf[0].pa_low = cpu_to_le32(virt_to_bus(vptr->txb + (idx * PKT_BUF_SZ))); - vptr->td_rings[idx].td_buf[0].pa_high = 0; - vptr->td_rings[idx].td_buf[0].bufsize = 0; - vptr->td_rings[idx].tdesc1.CMDZ = 2; - } -*/ - return 0; -} - -/** - * velocity_open - interface activation callback - * @dev: network layer device to open - * - * Called when the network layer brings the interface up. Returns - * a negative posix error code on failure, or zero on success. - * - * All the ring allocation and set up is done on open for this - * adapter to minimise memory usage when inactive - */ - -#define PCI_BYTE_REG_BITS_ON(x,i,p) do{\ - u8 byReg;\ - pci_read_config_byte((p), (i), &(byReg));\ - (byReg) |= (x);\ - pci_write_config_byte((p), (i), (byReg));\ -} while (0) - -// -// Registers in the PCI configuration space -// -#define PCI_REG_COMMAND 0x04 // -#define PCI_REG_MODE0 0x60 // -#define PCI_REG_MODE1 0x61 // -#define PCI_REG_MODE2 0x62 // -#define PCI_REG_MODE3 0x63 // -#define PCI_REG_DELAY_TIMER 0x64 // - -// Bits in the (MODE2, 0x62) register -// -#define MODE2_PCEROPT 0x80 // take PCI bus ERror as a fatal and shutdown from software control -#define MODE2_TXQ16 0x40 // TX write-back Queue control. 0->32 entries available in Tx write-back queue, 1->16 entries -#define MODE2_TXPOST 0x08 // (Not support in VT3119) -#define MODE2_AUTOOPT 0x04 // (VT3119 GHCI without such behavior) -#define MODE2_MODE10T 0x02 // used to control tx Threshold for 10M case -#define MODE2_TCPLSOPT 0x01 // TCP large send field update disable, hardware will not update related fields, leave it to software. - -// -// Bits in the MODE3 register -// -#define MODE3_MIION 0x04 // MII symbol codine error detect enable ?? - -// Bits in the (COMMAND, 0x04) register -#define COMMAND_BUSM 0x04 -#define COMMAND_WAIT 0x80 -static int velocity_open(struct nic *nic, struct pci_device *pci __unused) -{ - u8 diff; - u32 TxPhyAddr, RxPhyAddr; - u32 TxBufPhyAddr, RxBufPhyAddr; - vptr->TxDescArrays = tx_ring; - if (vptr->TxDescArrays == NULL) - printf("Allot Error"); - - /* Tx Descriptor needs 64 bytes alignment; */ - TxPhyAddr = virt_to_bus(vptr->TxDescArrays); - printf("Unaligned Address : %X\n", TxPhyAddr); - diff = 64 - (TxPhyAddr - ((TxPhyAddr >> 6) << 6)); - TxPhyAddr += diff; - vptr->td_rings = (struct tx_desc *) (vptr->TxDescArrays + diff); - - printf("Aligned Address: %lX\n", virt_to_bus(vptr->td_rings)); - vptr->tx_buffs = txb; - /* Rx Buffer needs 64 bytes alignment; */ - TxBufPhyAddr = virt_to_bus(vptr->tx_buffs); - diff = 64 - (TxBufPhyAddr - ((TxBufPhyAddr >> 6) << 6)); - TxBufPhyAddr += diff; - vptr->txb = (unsigned char *) (vptr->tx_buffs + diff); - - vptr->RxDescArrays = rx_ring; - /* Rx Descriptor needs 64 bytes alignment; */ - RxPhyAddr = virt_to_bus(vptr->RxDescArrays); - diff = 64 - (RxPhyAddr - ((RxPhyAddr >> 6) << 6)); - RxPhyAddr += diff; - vptr->rd_ring = (struct rx_desc *) (vptr->RxDescArrays + diff); - - vptr->rx_buffs = rxb; - /* Rx Buffer needs 64 bytes alignment; */ - RxBufPhyAddr = virt_to_bus(vptr->rx_buffs); - diff = 64 - (RxBufPhyAddr - ((RxBufPhyAddr >> 6) << 6)); - RxBufPhyAddr += diff; - vptr->rxb = (unsigned char *) (vptr->rx_buffs + diff); - - if (vptr->RxDescArrays == NULL || vptr->RxDescArrays == NULL) { - printf("Allocate tx_ring or rd_ring failed\n"); - return 0; - } - - vptr->rx_buf_sz = PKT_BUF_SZ; -/* - // turn this on to avoid retry forever - PCI_BYTE_REG_BITS_ON(MODE2_PCEROPT, PCI_REG_MODE2, pci); - // for some legacy BIOS and OS don't open BusM - // bit in PCI configuration space. So, turn it on. - PCI_BYTE_REG_BITS_ON(COMMAND_BUSM, PCI_REG_COMMAND, pci); - // turn this on to detect MII coding error - PCI_BYTE_REG_BITS_ON(MODE3_MIION, PCI_REG_MODE3, pci); - */ - velocity_init_rings(vptr); - - /* Ensure chip is running */ -//FIXME: pci_set_power_state(vptr->pdev, PCI_D0); - - velocity_init_registers(nic, vptr, VELOCITY_INIT_COLD); - mac_write_int_mask(0, vptr->mac_regs); -// _int(vptr->mac_regs); - //mac_enable_int(vptr->mac_regs); - - vptr->flags |= VELOCITY_FLAGS_OPENED; - return 1; - -} - -/* - * MII access , media link mode setting functions - */ - - -/** - * mii_init - set up MII - * @vptr: velocity adapter - * @mii_status: links tatus - * - * Set up the PHY for the current link state. - */ - -static void mii_init(struct velocity_info *vptr, u32 mii_status __unused) -{ - u16 BMCR; - - switch (PHYID_GET_PHY_ID(vptr->phy_id)) { - case PHYID_CICADA_CS8201: - /* - * Reset to hardware default - */ - MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - /* - * Turn on ECHODIS bit in NWay-forced full mode and turn it - * off it in NWay-forced half mode for NWay-forced v.s. - * legacy-forced issue. - */ - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - else - MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - /* - * Turn on Link/Activity LED enable bit for CIS8201 - */ - MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs); - break; - case PHYID_VT3216_32BIT: - case PHYID_VT3216_64BIT: - /* - * Reset to hardware default - */ - MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - /* - * Turn on ECHODIS bit in NWay-forced full mode and turn it - * off it in NWay-forced half mode for NWay-forced v.s. - * legacy-forced issue - */ - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - else - MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - break; - - case PHYID_MARVELL_1000: - case PHYID_MARVELL_1000S: - /* - * Assert CRS on Transmit - */ - MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); - /* - * Reset to hardware default - */ - MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - break; - default: - ; - } - velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR); - if (BMCR & BMCR_ISO) { - BMCR &= ~BMCR_ISO; - velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR); - } -} - -/** - * safe_disable_mii_autopoll - autopoll off - * @regs: velocity registers - * - * Turn off the autopoll and wait for it to disable on the chip - */ - -static void safe_disable_mii_autopoll(struct mac_regs *regs) -{ - u16 ww; - - /* turn off MAUTO */ - writeb(0, ®s->MIICR); - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - udelay(1); - if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } -} - -/** - * enable_mii_autopoll - turn on autopolling - * @regs: velocity registers - * - * Enable the MII link status autopoll feature on the Velocity - * hardware. Wait for it to enable. - */ - -static void enable_mii_autopoll(struct mac_regs *regs) -{ - unsigned int ii; - - writeb(0, &(regs->MIICR)); - writeb(MIIADR_SWMPL, ®s->MIIADR); - - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } - - writeb(MIICR_MAUTO, ®s->MIICR); - - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } - -} - -/** - * velocity_mii_read - read MII data - * @regs: velocity registers - * @index: MII register index - * @data: buffer for received data - * - * Perform a single read of an MII 16bit register. Returns zero - * on success or -ETIMEDOUT if the PHY did not respond. - */ - -static int velocity_mii_read(struct mac_regs *regs, u8 index, u16 * data) -{ - u16 ww; - - /* - * Disable MIICR_MAUTO, so that mii addr can be set normally - */ - safe_disable_mii_autopoll(regs); - - writeb(index, ®s->MIIADR); - - BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(readb(®s->MIICR) & MIICR_RCMD)) - break; - } - - *data = readw(®s->MIIDATA); - - enable_mii_autopoll(regs); - if (ww == W_MAX_TIMEOUT) - return -1; - return 0; -} - -/** - * velocity_mii_write - write MII data - * @regs: velocity registers - * @index: MII register index - * @data: 16bit data for the MII register - * - * Perform a single write to an MII 16bit register. Returns zero - * on success or -ETIMEDOUT if the PHY did not respond. - */ - -static int velocity_mii_write(struct mac_regs *regs, u8 mii_addr, u16 data) -{ - u16 ww; - - /* - * Disable MIICR_MAUTO, so that mii addr can be set normally - */ - safe_disable_mii_autopoll(regs); - - /* MII reg offset */ - writeb(mii_addr, ®s->MIIADR); - /* set MII data */ - writew(data, ®s->MIIDATA); - - /* turn on MIICR_WCMD */ - BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); - - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - udelay(5); - if (!(readb(®s->MIICR) & MIICR_WCMD)) - break; - } - enable_mii_autopoll(regs); - - if (ww == W_MAX_TIMEOUT) - return -1; - return 0; -} - -/** - * velocity_get_opt_media_mode - get media selection - * @vptr: velocity adapter - * - * Get the media mode stored in EEPROM or module options and load - * mii_status accordingly. The requested link state information - * is also returned. - */ - -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) -{ - u32 status = 0; - - switch (vptr->options.spd_dpx) { - case SPD_DPX_AUTO: - status = VELOCITY_AUTONEG_ENABLE; - break; - case SPD_DPX_100_FULL: - status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; - break; - case SPD_DPX_10_FULL: - status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; - break; - case SPD_DPX_100_HALF: - status = VELOCITY_SPEED_100; - break; - case SPD_DPX_10_HALF: - status = VELOCITY_SPEED_10; - break; - } - vptr->mii_status = status; - return status; -} - -/** - * mii_set_auto_on - autonegotiate on - * @vptr: velocity - * - * Enable autonegotation on this interface - */ - -static void mii_set_auto_on(struct velocity_info *vptr) -{ - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs)) - MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); - else - MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); -} - - -/* -static void mii_set_auto_off(struct velocity_info * vptr) -{ - MII_REG_BITS_OFF(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); -} -*/ - -/** - * set_mii_flow_control - flow control setup - * @vptr: velocity interface - * - * Set up the flow control on this interface according to - * the supplied user/eeprom options. - */ - -static void set_mii_flow_control(struct velocity_info *vptr) -{ - /*Enable or Disable PAUSE in ANAR */ - switch (vptr->options.flow_cntl) { - case FLOW_CNTL_TX: - MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_RX: - MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_TX_RX: - MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_DISABLE: - MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_OFF(ANAR_ASMDIR, MII_REG_ANAR, - vptr->mac_regs); - break; - default: - break; - } -} - -/** - * velocity_set_media_mode - set media mode - * @mii_status: old MII link state - * - * Check the media link state and configure the flow control - * PHY and also velocity hardware setup accordingly. In particular - * we need to set up CD polling and frame bursting. - */ - -static int velocity_set_media_mode(struct velocity_info *vptr, - u32 mii_status) -{ - struct mac_regs *regs = vptr->mac_regs; - - vptr->mii_status = mii_check_media_mode(vptr->mac_regs); - - /* Set mii link status */ - set_mii_flow_control(vptr); - - if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) { - MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, - vptr->mac_regs); - } - - /* - * If connection type is AUTO - */ - if (mii_status & VELOCITY_AUTONEG_ENABLE) { - printf("Velocity is AUTO mode\n"); - /* clear force MAC mode bit */ - BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); - /* set duplex mode of MAC according to duplex mode of MII */ - MII_REG_BITS_ON(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10, - MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(G1000CR_1000FD | G1000CR_1000, - MII_REG_G1000CR, vptr->mac_regs); - MII_REG_BITS_ON(BMCR_SPEED1G, MII_REG_BMCR, - vptr->mac_regs); - - /* enable AUTO-NEGO mode */ - mii_set_auto_on(vptr); - } else { - u16 ANAR; - u8 CHIPGCR; - - /* - * 1. if it's 3119, disable frame bursting in halfduplex mode - * and enable it in fullduplex mode - * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR - * 3. only enable CD heart beat counter in 10HD mode - */ - - /* set force MAC mode bit */ - BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); - - CHIPGCR = readb(®s->CHIPGCR); - CHIPGCR &= ~CHIPGCR_FCGMII; - - if (mii_status & VELOCITY_DUPLEX_FULL) { - CHIPGCR |= CHIPGCR_FCFDX; - writeb(CHIPGCR, ®s->CHIPGCR); - printf - ("DEBUG: set Velocity to forced full mode\n"); - if (vptr->rev_id < REV_ID_VT3216_A0) - BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); - } else { - CHIPGCR &= ~CHIPGCR_FCFDX; - printf - ("DEBUG: set Velocity to forced half mode\n"); - writeb(CHIPGCR, ®s->CHIPGCR); - if (vptr->rev_id < REV_ID_VT3216_A0) - BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); - } - - MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000, - MII_REG_G1000CR, vptr->mac_regs); - - if (!(mii_status & VELOCITY_DUPLEX_FULL) - && (mii_status & VELOCITY_SPEED_10)) { - BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); - } else { - BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); - } - /* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */ - velocity_mii_read(vptr->mac_regs, MII_REG_ANAR, &ANAR); - ANAR &= (~(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)); - if (mii_status & VELOCITY_SPEED_100) { - if (mii_status & VELOCITY_DUPLEX_FULL) - ANAR |= ANAR_TXFD; - else - ANAR |= ANAR_TX; - } else { - if (mii_status & VELOCITY_DUPLEX_FULL) - ANAR |= ANAR_10FD; - else - ANAR |= ANAR_10; - } - velocity_mii_write(vptr->mac_regs, MII_REG_ANAR, ANAR); - /* enable AUTO-NEGO mode */ - mii_set_auto_on(vptr); - /* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */ - } - /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ - /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ - return VELOCITY_LINK_CHANGE; -} - -/** - * mii_check_media_mode - check media state - * @regs: velocity registers - * - * Check the current MII status and determine the link status - * accordingly - */ - -static u32 mii_check_media_mode(struct mac_regs *regs) -{ - u32 status = 0; - u16 ANAR; - - if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) - status |= VELOCITY_LINK_FAIL; - - if (MII_REG_BITS_IS_ON(G1000CR_1000FD, MII_REG_G1000CR, regs)) - status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; - else if (MII_REG_BITS_IS_ON(G1000CR_1000, MII_REG_G1000CR, regs)) - status |= (VELOCITY_SPEED_1000); - else { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if (ANAR & ANAR_TXFD) - status |= - (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); - else if (ANAR & ANAR_TX) - status |= VELOCITY_SPEED_100; - else if (ANAR & ANAR_10FD) - status |= - (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); - else - status |= (VELOCITY_SPEED_10); - } - - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) - == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { - if (MII_REG_BITS_IS_ON - (G1000CR_1000 | G1000CR_1000FD, - MII_REG_G1000CR, regs)) - status |= VELOCITY_AUTONEG_ENABLE; - } - } - - return status; -} - -static u32 check_connection_type(struct mac_regs *regs) -{ - u32 status = 0; - u8 PHYSR0; - u16 ANAR; - PHYSR0 = readb(®s->PHYSR0); - - /* - if (!(PHYSR0 & PHYSR0_LINKGD)) - status|=VELOCITY_LINK_FAIL; - */ - - if (PHYSR0 & PHYSR0_FDPX) - status |= VELOCITY_DUPLEX_FULL; - - if (PHYSR0 & PHYSR0_SPDG) - status |= VELOCITY_SPEED_1000; - if (PHYSR0 & PHYSR0_SPD10) - status |= VELOCITY_SPEED_10; - else - status |= VELOCITY_SPEED_100; - - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) - == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { - if (MII_REG_BITS_IS_ON - (G1000CR_1000 | G1000CR_1000FD, - MII_REG_G1000CR, regs)) - status |= VELOCITY_AUTONEG_ENABLE; - } - } - - return status; -} - -/** - * enable_flow_control_ability - flow control - * @vptr: veloity to configure - * - * Set up flow control according to the flow control options - * determined by the eeprom/configuration. - */ - -static void enable_flow_control_ability(struct velocity_info *vptr) -{ - - struct mac_regs *regs = vptr->mac_regs; - - switch (vptr->options.flow_cntl) { - - case FLOW_CNTL_DEFAULT: - if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) - writel(CR0_FDXRFCEN, ®s->CR0Set); - else - writel(CR0_FDXRFCEN, ®s->CR0Clr); - - if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) - writel(CR0_FDXTFCEN, ®s->CR0Set); - else - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_TX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_RX: - writel(CR0_FDXRFCEN, ®s->CR0Set); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_TX_RX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Set); - break; - - case FLOW_CNTL_DISABLE: - writel(CR0_FDXRFCEN, ®s->CR0Clr); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - default: - break; - } - -} - -/* FIXME: Move to pci.c */ -/** - * pci_set_power_state - Set the power state of a PCI device - * @dev: PCI device to be suspended - * @state: Power state we're entering - * - * Transition a device to a new power state, using the Power Management - * Capabilities in the device's config space. - * - * RETURN VALUE: - * -EINVAL if trying to enter a lower state than we're already in. - * 0 if we're already in the requested state. - * -EIO if device does not support PCI PM. - * 0 if we can successfully change the power state. - */ - -int pci_set_power_state(struct pci_device *dev, int state) -{ - int pm; - u16 pmcsr; - int current_state = 0; - - /* bound the state we're entering */ - if (state > 3) - state = 3; - - /* Validate current state: - * Can enter D0 from any state, but if we can only go deeper - * to sleep if we're already in a low power state - */ - if (state > 0 && current_state > state) - return -1; - else if (current_state == state) - return 0; /* we're already there */ - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - - /* abort if the device doesn't support PM capabilities */ - if (!pm) - return -2; - - /* check if this device supports the desired state */ - if (state == 1 || state == 2) { - u16 pmc; - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - if (state == 1 && !(pmc & PCI_PM_CAP_D1)) - return -2; - else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) - return -2; - } - - /* If we're in D3, force entire word to 0. - * This doesn't affect PME_Status, disables PME_En, and - * sets PowerState to 0. - */ - if (current_state >= 3) - pmcsr = 0; - else { - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); - pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - pmcsr |= state; - } - - /* enter specified state */ - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); - - /* Mandatory power management transition delays */ - /* see PCI PM 1.1 5.6.1 table 18 */ - if (state == 3 || current_state == 3) - mdelay(10); - else if (state == 2 || current_state == 2) - udelay(200); - current_state = state; - - return 0; -} - -static struct pci_device_id velocity_nics[] = { - PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter", 0), -}; - -PCI_DRIVER ( velocity_driver, velocity_nics, PCI_NO_CLASS ); - -DRIVER ( "VIA-VELOCITY/PCI", nic_driver, pci_driver, velocity_driver, - velocity_probe, velocity_disable ); diff --git a/src/drivers/net/via-velocity.h b/src/drivers/net/via-velocity.h deleted file mode 100644 index 753fe445..00000000 --- a/src/drivers/net/via-velocity.h +++ /dev/null @@ -1,1932 +0,0 @@ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * This software may be redistributed and/or modified under - * the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * File: via-velocity.h - * - * Purpose: Header file to define driver's private structures. - * - * Author: Chuang Liang-Shing, AJ Jiang - * - * Date: Jan 24, 2003 - * - * Changes for Etherboot Port: - * Copyright (c) 2006 by Timothy Legge - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#ifndef VELOCITY_H -#define VELOCITY_H - -#define VELOCITY_TX_CSUM_SUPPORT - -#define VELOCITY_NAME "via-velocity" -#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver" -#define VELOCITY_VERSION "1.13" - -#define PKT_BUF_SZ 1564 - -#define MAX_UNITS 8 -#define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1} - -#define REV_ID_VT6110 (0) - -#define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0) -#define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0) -#define DWORD_REG_BITS_ON(x,p) do { writel(readl((p))|(x),(p));} while (0) - -#define BYTE_REG_BITS_IS_ON(x,p) (readb((p)) & (x)) -#define WORD_REG_BITS_IS_ON(x,p) (readw((p)) & (x)) -#define DWORD_REG_BITS_IS_ON(x,p) (readl((p)) & (x)) - -#define BYTE_REG_BITS_OFF(x,p) do { writeb(readb((p)) & (~(x)),(p));} while (0) -#define WORD_REG_BITS_OFF(x,p) do { writew(readw((p)) & (~(x)),(p));} while (0) -#define DWORD_REG_BITS_OFF(x,p) do { writel(readl((p)) & (~(x)),(p));} while (0) - -#define BYTE_REG_BITS_SET(x,m,p) do { writeb( (readb((p)) & (~(m))) |(x),(p));} while (0) -#define WORD_REG_BITS_SET(x,m,p) do { writew( (readw((p)) & (~(m))) |(x),(p));} while (0) -#define DWORD_REG_BITS_SET(x,m,p) do { writel( (readl((p)) & (~(m)))|(x),(p));} while (0) - -#define VAR_USED(p) do {(p)=(p);} while (0) - -/* - * Purpose: Structures for MAX RX/TX descriptors. - */ - - -#define B_OWNED_BY_CHIP 1 -#define B_OWNED_BY_HOST 0 - -/* - * Bits in the RSR0 register - */ - -#define RSR_DETAG 0x0080 -#define RSR_SNTAG 0x0040 -#define RSR_RXER 0x0020 -#define RSR_RL 0x0010 -#define RSR_CE 0x0008 -#define RSR_FAE 0x0004 -#define RSR_CRC 0x0002 -#define RSR_VIDM 0x0001 - -/* - * Bits in the RSR1 register - */ - -#define RSR_RXOK 0x8000 // rx OK -#define RSR_PFT 0x4000 // Perfect filtering address match -#define RSR_MAR 0x2000 // MAC accept multicast address packet -#define RSR_BAR 0x1000 // MAC accept broadcast address packet -#define RSR_PHY 0x0800 // MAC accept physical address packet -#define RSR_VTAG 0x0400 // 802.1p/1q tagging packet indicator -#define RSR_STP 0x0200 // start of packet -#define RSR_EDP 0x0100 // end of packet - -/* - * Bits in the RSR1 register - */ - -#define RSR1_RXOK 0x80 // rx OK -#define RSR1_PFT 0x40 // Perfect filtering address match -#define RSR1_MAR 0x20 // MAC accept multicast address packet -#define RSR1_BAR 0x10 // MAC accept broadcast address packet -#define RSR1_PHY 0x08 // MAC accept physical address packet -#define RSR1_VTAG 0x04 // 802.1p/1q tagging packet indicator -#define RSR1_STP 0x02 // start of packet -#define RSR1_EDP 0x01 // end of packet - -/* - * Bits in the CSM register - */ - -#define CSM_IPOK 0x40 //IP Checkusm validatiaon ok -#define CSM_TUPOK 0x20 //TCP/UDP Checkusm validatiaon ok -#define CSM_FRAG 0x10 //Fragment IP datagram -#define CSM_IPKT 0x04 //Received an IP packet -#define CSM_TCPKT 0x02 //Received a TCP packet -#define CSM_UDPKT 0x01 //Received a UDP packet - -/* - * Bits in the TSR0 register - */ - -#define TSR0_ABT 0x0080 // Tx abort because of excessive collision -#define TSR0_OWT 0x0040 // Jumbo frame Tx abort -#define TSR0_OWC 0x0020 // Out of window collision -#define TSR0_COLS 0x0010 // experience collision in this transmit event -#define TSR0_NCR3 0x0008 // collision retry counter[3] -#define TSR0_NCR2 0x0004 // collision retry counter[2] -#define TSR0_NCR1 0x0002 // collision retry counter[1] -#define TSR0_NCR0 0x0001 // collision retry counter[0] -#define TSR0_TERR 0x8000 // -#define TSR0_FDX 0x4000 // current transaction is serviced by full duplex mode -#define TSR0_GMII 0x2000 // current transaction is serviced by GMII mode -#define TSR0_LNKFL 0x1000 // packet serviced during link down -#define TSR0_SHDN 0x0400 // shutdown case -#define TSR0_CRS 0x0200 // carrier sense lost -#define TSR0_CDH 0x0100 // AQE test fail (CD heartbeat) - -/* - * Bits in the TSR1 register - */ - -#define TSR1_TERR 0x80 // -#define TSR1_FDX 0x40 // current transaction is serviced by full duplex mode -#define TSR1_GMII 0x20 // current transaction is serviced by GMII mode -#define TSR1_LNKFL 0x10 // packet serviced during link down -#define TSR1_SHDN 0x04 // shutdown case -#define TSR1_CRS 0x02 // carrier sense lost -#define TSR1_CDH 0x01 // AQE test fail (CD heartbeat) - -// -// Bits in the TCR0 register -// -#define TCR0_TIC 0x80 // assert interrupt immediately while descriptor has been send complete -#define TCR0_PIC 0x40 // priority interrupt request, INA# is issued over adaptive interrupt scheme -#define TCR0_VETAG 0x20 // enable VLAN tag -#define TCR0_IPCK 0x10 // request IP checksum calculation. -#define TCR0_UDPCK 0x08 // request UDP checksum calculation. -#define TCR0_TCPCK 0x04 // request TCP checksum calculation. -#define TCR0_JMBO 0x02 // indicate a jumbo packet in GMAC side -#define TCR0_CRC 0x01 // disable CRC generation - -#define TCPLS_NORMAL 3 -#define TCPLS_START 2 -#define TCPLS_END 1 -#define TCPLS_MED 0 - - -// max transmit or receive buffer size -#define CB_RX_BUF_SIZE 2048UL // max buffer size - // NOTE: must be multiple of 4 - -#define CB_MAX_RD_NUM 512 // MAX # of RD -#define CB_MAX_TD_NUM 256 // MAX # of TD - -#define CB_INIT_RD_NUM_3119 128 // init # of RD, for setup VT3119 -#define CB_INIT_TD_NUM_3119 64 // init # of TD, for setup VT3119 - -#define CB_INIT_RD_NUM 128 // init # of RD, for setup default -#define CB_INIT_TD_NUM 64 // init # of TD, for setup default - -// for 3119 -#define CB_TD_RING_NUM 4 // # of TD rings. -#define CB_MAX_SEG_PER_PKT 7 // max data seg per packet (Tx) - - -/* - * If collisions excess 15 times , tx will abort, and - * if tx fifo underflow, tx will fail - * we should try to resend it - */ - -#define CB_MAX_TX_ABORT_RETRY 3 - -/* - * Receive descriptor - */ - -struct rdesc0 { - u16 RSR; /* Receive status */ - u16 len:14; /* Received packet length */ - u16 reserved:1; - u16 owner:1; /* Who owns this buffer ? */ -}; - -struct rdesc1 { - u16 PQTAG; - u8 CSM; - u8 IPKT; -}; - -struct rx_desc { - struct rdesc0 rdesc0; - struct rdesc1 rdesc1; - u32 pa_low; /* Low 32 bit PCI address */ - u16 pa_high; /* Next 16 bit PCI address (48 total) */ - u16 len:15; /* Frame size */ - u16 inten:1; /* Enable interrupt */ -} __attribute__ ((__packed__)); - -/* - * Transmit descriptor - */ - -struct tdesc0 { - u16 TSR; /* Transmit status register */ - u16 pktsize:14; /* Size of frame */ - u16 reserved:1; - u16 owner:1; /* Who owns the buffer */ -}; - -struct pqinf { /* Priority queue info */ - u16 VID:12; - u16 CFI:1; - u16 priority:3; -} __attribute__ ((__packed__)); - -struct tdesc1 { - struct pqinf pqinf; - u8 TCR; - u8 TCPLS:2; - u8 reserved:2; - u8 CMDZ:4; -} __attribute__ ((__packed__)); - -struct td_buf { - u32 pa_low; - u16 pa_high; - u16 bufsize:14; - u16 reserved:1; - u16 queue:1; -} __attribute__ ((__packed__)); - -struct tx_desc { - struct tdesc0 tdesc0; - struct tdesc1 tdesc1; - struct td_buf td_buf[7]; -}; - -#ifdef LINUX -struct velocity_rd_info { - struct sk_buff *skb; - dma_addr_t skb_dma; -}; - - -/** - * alloc_rd_info - allocate an rd info block - * - * Alocate and initialize a receive info structure used for keeping - * track of kernel side information related to each receive - * descriptor we are using - */ - -static inline struct velocity_rd_info *alloc_rd_info(void) -{ - struct velocity_rd_info *ptr; - if ((ptr = - kmalloc(sizeof(struct velocity_rd_info), GFP_ATOMIC)) == NULL) - return NULL; - else { - memset(ptr, 0, sizeof(struct velocity_rd_info)); - return ptr; - } -} - -/* - * Used to track transmit side buffers. - */ - -struct velocity_td_info { - struct sk_buff *skb; - u8 *buf; - int nskb_dma; - dma_addr_t skb_dma[7]; - dma_addr_t buf_dma; -}; - -#endif -enum { - OWNED_BY_HOST = 0, - OWNED_BY_NIC = 1 -} velocity_owner; - - -/* - * MAC registers and macros. - */ - - -#define MCAM_SIZE 64 -#define VCAM_SIZE 64 -#define TX_QUEUE_NO 4 - -#define MAX_HW_MIB_COUNTER 32 -#define VELOCITY_MIN_MTU (1514-14) -#define VELOCITY_MAX_MTU (9000) - -/* - * Registers in the MAC - */ - -#define MAC_REG_PAR 0x00 // physical address -#define MAC_REG_RCR 0x06 -#define MAC_REG_TCR 0x07 -#define MAC_REG_CR0_SET 0x08 -#define MAC_REG_CR1_SET 0x09 -#define MAC_REG_CR2_SET 0x0A -#define MAC_REG_CR3_SET 0x0B -#define MAC_REG_CR0_CLR 0x0C -#define MAC_REG_CR1_CLR 0x0D -#define MAC_REG_CR2_CLR 0x0E -#define MAC_REG_CR3_CLR 0x0F -#define MAC_REG_MAR 0x10 -#define MAC_REG_CAM 0x10 -#define MAC_REG_DEC_BASE_HI 0x18 -#define MAC_REG_DBF_BASE_HI 0x1C -#define MAC_REG_ISR_CTL 0x20 -#define MAC_REG_ISR_HOTMR 0x20 -#define MAC_REG_ISR_TSUPTHR 0x20 -#define MAC_REG_ISR_RSUPTHR 0x20 -#define MAC_REG_ISR_CTL1 0x21 -#define MAC_REG_TXE_SR 0x22 -#define MAC_REG_RXE_SR 0x23 -#define MAC_REG_ISR 0x24 -#define MAC_REG_ISR0 0x24 -#define MAC_REG_ISR1 0x25 -#define MAC_REG_ISR2 0x26 -#define MAC_REG_ISR3 0x27 -#define MAC_REG_IMR 0x28 -#define MAC_REG_IMR0 0x28 -#define MAC_REG_IMR1 0x29 -#define MAC_REG_IMR2 0x2A -#define MAC_REG_IMR3 0x2B -#define MAC_REG_TDCSR_SET 0x30 -#define MAC_REG_RDCSR_SET 0x32 -#define MAC_REG_TDCSR_CLR 0x34 -#define MAC_REG_RDCSR_CLR 0x36 -#define MAC_REG_RDBASE_LO 0x38 -#define MAC_REG_RDINDX 0x3C -#define MAC_REG_TDBASE_LO 0x40 -#define MAC_REG_RDCSIZE 0x50 -#define MAC_REG_TDCSIZE 0x52 -#define MAC_REG_TDINDX 0x54 -#define MAC_REG_TDIDX0 0x54 -#define MAC_REG_TDIDX1 0x56 -#define MAC_REG_TDIDX2 0x58 -#define MAC_REG_TDIDX3 0x5A -#define MAC_REG_PAUSE_TIMER 0x5C -#define MAC_REG_RBRDU 0x5E -#define MAC_REG_FIFO_TEST0 0x60 -#define MAC_REG_FIFO_TEST1 0x64 -#define MAC_REG_CAMADDR 0x68 -#define MAC_REG_CAMCR 0x69 -#define MAC_REG_GFTEST 0x6A -#define MAC_REG_FTSTCMD 0x6B -#define MAC_REG_MIICFG 0x6C -#define MAC_REG_MIISR 0x6D -#define MAC_REG_PHYSR0 0x6E -#define MAC_REG_PHYSR1 0x6F -#define MAC_REG_MIICR 0x70 -#define MAC_REG_MIIADR 0x71 -#define MAC_REG_MIIDATA 0x72 -#define MAC_REG_SOFT_TIMER0 0x74 -#define MAC_REG_SOFT_TIMER1 0x76 -#define MAC_REG_CFGA 0x78 -#define MAC_REG_CFGB 0x79 -#define MAC_REG_CFGC 0x7A -#define MAC_REG_CFGD 0x7B -#define MAC_REG_DCFG0 0x7C -#define MAC_REG_DCFG1 0x7D -#define MAC_REG_MCFG0 0x7E -#define MAC_REG_MCFG1 0x7F - -#define MAC_REG_TBIST 0x80 -#define MAC_REG_RBIST 0x81 -#define MAC_REG_PMCC 0x82 -#define MAC_REG_STICKHW 0x83 -#define MAC_REG_MIBCR 0x84 -#define MAC_REG_EERSV 0x85 -#define MAC_REG_REVID 0x86 -#define MAC_REG_MIBREAD 0x88 -#define MAC_REG_BPMA 0x8C -#define MAC_REG_EEWR_DATA 0x8C -#define MAC_REG_BPMD_WR 0x8F -#define MAC_REG_BPCMD 0x90 -#define MAC_REG_BPMD_RD 0x91 -#define MAC_REG_EECHKSUM 0x92 -#define MAC_REG_EECSR 0x93 -#define MAC_REG_EERD_DATA 0x94 -#define MAC_REG_EADDR 0x96 -#define MAC_REG_EMBCMD 0x97 -#define MAC_REG_JMPSR0 0x98 -#define MAC_REG_JMPSR1 0x99 -#define MAC_REG_JMPSR2 0x9A -#define MAC_REG_JMPSR3 0x9B -#define MAC_REG_CHIPGSR 0x9C -#define MAC_REG_TESTCFG 0x9D -#define MAC_REG_DEBUG 0x9E -#define MAC_REG_CHIPGCR 0x9F -#define MAC_REG_WOLCR0_SET 0xA0 -#define MAC_REG_WOLCR1_SET 0xA1 -#define MAC_REG_PWCFG_SET 0xA2 -#define MAC_REG_WOLCFG_SET 0xA3 -#define MAC_REG_WOLCR0_CLR 0xA4 -#define MAC_REG_WOLCR1_CLR 0xA5 -#define MAC_REG_PWCFG_CLR 0xA6 -#define MAC_REG_WOLCFG_CLR 0xA7 -#define MAC_REG_WOLSR0_SET 0xA8 -#define MAC_REG_WOLSR1_SET 0xA9 -#define MAC_REG_WOLSR0_CLR 0xAC -#define MAC_REG_WOLSR1_CLR 0xAD -#define MAC_REG_PATRN_CRC0 0xB0 -#define MAC_REG_PATRN_CRC1 0xB2 -#define MAC_REG_PATRN_CRC2 0xB4 -#define MAC_REG_PATRN_CRC3 0xB6 -#define MAC_REG_PATRN_CRC4 0xB8 -#define MAC_REG_PATRN_CRC5 0xBA -#define MAC_REG_PATRN_CRC6 0xBC -#define MAC_REG_PATRN_CRC7 0xBE -#define MAC_REG_BYTEMSK0_0 0xC0 -#define MAC_REG_BYTEMSK0_1 0xC4 -#define MAC_REG_BYTEMSK0_2 0xC8 -#define MAC_REG_BYTEMSK0_3 0xCC -#define MAC_REG_BYTEMSK1_0 0xD0 -#define MAC_REG_BYTEMSK1_1 0xD4 -#define MAC_REG_BYTEMSK1_2 0xD8 -#define MAC_REG_BYTEMSK1_3 0xDC -#define MAC_REG_BYTEMSK2_0 0xE0 -#define MAC_REG_BYTEMSK2_1 0xE4 -#define MAC_REG_BYTEMSK2_2 0xE8 -#define MAC_REG_BYTEMSK2_3 0xEC -#define MAC_REG_BYTEMSK3_0 0xF0 -#define MAC_REG_BYTEMSK3_1 0xF4 -#define MAC_REG_BYTEMSK3_2 0xF8 -#define MAC_REG_BYTEMSK3_3 0xFC - -/* - * Bits in the RCR register - */ - -#define RCR_AS 0x80 -#define RCR_AP 0x40 -#define RCR_AL 0x20 -#define RCR_PROM 0x10 -#define RCR_AB 0x08 -#define RCR_AM 0x04 -#define RCR_AR 0x02 -#define RCR_SEP 0x01 - -/* - * Bits in the TCR register - */ - -#define TCR_TB2BDIS 0x80 -#define TCR_COLTMC1 0x08 -#define TCR_COLTMC0 0x04 -#define TCR_LB1 0x02 /* loopback[1] */ -#define TCR_LB0 0x01 /* loopback[0] */ - -/* - * Bits in the CR0 register - */ - -#define CR0_TXON 0x00000008UL -#define CR0_RXON 0x00000004UL -#define CR0_STOP 0x00000002UL /* stop MAC, default = 1 */ -#define CR0_STRT 0x00000001UL /* start MAC */ -#define CR0_SFRST 0x00008000UL /* software reset */ -#define CR0_TM1EN 0x00004000UL -#define CR0_TM0EN 0x00002000UL -#define CR0_DPOLL 0x00000800UL /* disable rx/tx auto polling */ -#define CR0_DISAU 0x00000100UL -#define CR0_XONEN 0x00800000UL -#define CR0_FDXTFCEN 0x00400000UL /* full-duplex TX flow control enable */ -#define CR0_FDXRFCEN 0x00200000UL /* full-duplex RX flow control enable */ -#define CR0_HDXFCEN 0x00100000UL /* half-duplex flow control enable */ -#define CR0_XHITH1 0x00080000UL /* TX XON high threshold 1 */ -#define CR0_XHITH0 0x00040000UL /* TX XON high threshold 0 */ -#define CR0_XLTH1 0x00020000UL /* TX pause frame low threshold 1 */ -#define CR0_XLTH0 0x00010000UL /* TX pause frame low threshold 0 */ -#define CR0_GSPRST 0x80000000UL -#define CR0_FORSRST 0x40000000UL -#define CR0_FPHYRST 0x20000000UL -#define CR0_DIAG 0x10000000UL -#define CR0_INTPCTL 0x04000000UL -#define CR0_GINTMSK1 0x02000000UL -#define CR0_GINTMSK0 0x01000000UL - -/* - * Bits in the CR1 register - */ - -#define CR1_SFRST 0x80 /* software reset */ -#define CR1_TM1EN 0x40 -#define CR1_TM0EN 0x20 -#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ -#define CR1_DISAU 0x01 - -/* - * Bits in the CR2 register - */ - -#define CR2_XONEN 0x80 -#define CR2_FDXTFCEN 0x40 /* full-duplex TX flow control enable */ -#define CR2_FDXRFCEN 0x20 /* full-duplex RX flow control enable */ -#define CR2_HDXFCEN 0x10 /* half-duplex flow control enable */ -#define CR2_XHITH1 0x08 /* TX XON high threshold 1 */ -#define CR2_XHITH0 0x04 /* TX XON high threshold 0 */ -#define CR2_XLTH1 0x02 /* TX pause frame low threshold 1 */ -#define CR2_XLTH0 0x01 /* TX pause frame low threshold 0 */ - -/* - * Bits in the CR3 register - */ - -#define CR3_GSPRST 0x80 -#define CR3_FORSRST 0x40 -#define CR3_FPHYRST 0x20 -#define CR3_DIAG 0x10 -#define CR3_INTPCTL 0x04 -#define CR3_GINTMSK1 0x02 -#define CR3_GINTMSK0 0x01 - -#define ISRCTL_UDPINT 0x8000 -#define ISRCTL_TSUPDIS 0x4000 -#define ISRCTL_RSUPDIS 0x2000 -#define ISRCTL_PMSK1 0x1000 -#define ISRCTL_PMSK0 0x0800 -#define ISRCTL_INTPD 0x0400 -#define ISRCTL_HCRLD 0x0200 -#define ISRCTL_SCRLD 0x0100 - -/* - * Bits in the ISR_CTL1 register - */ - -#define ISRCTL1_UDPINT 0x80 -#define ISRCTL1_TSUPDIS 0x40 -#define ISRCTL1_RSUPDIS 0x20 -#define ISRCTL1_PMSK1 0x10 -#define ISRCTL1_PMSK0 0x08 -#define ISRCTL1_INTPD 0x04 -#define ISRCTL1_HCRLD 0x02 -#define ISRCTL1_SCRLD 0x01 - -/* - * Bits in the TXE_SR register - */ - -#define TXESR_TFDBS 0x08 -#define TXESR_TDWBS 0x04 -#define TXESR_TDRBS 0x02 -#define TXESR_TDSTR 0x01 - -/* - * Bits in the RXE_SR register - */ - -#define RXESR_RFDBS 0x08 -#define RXESR_RDWBS 0x04 -#define RXESR_RDRBS 0x02 -#define RXESR_RDSTR 0x01 - -/* - * Bits in the ISR register - */ - -#define ISR_ISR3 0x80000000UL -#define ISR_ISR2 0x40000000UL -#define ISR_ISR1 0x20000000UL -#define ISR_ISR0 0x10000000UL -#define ISR_TXSTLI 0x02000000UL -#define ISR_RXSTLI 0x01000000UL -#define ISR_HFLD 0x00800000UL -#define ISR_UDPI 0x00400000UL -#define ISR_MIBFI 0x00200000UL -#define ISR_SHDNI 0x00100000UL -#define ISR_PHYI 0x00080000UL -#define ISR_PWEI 0x00040000UL -#define ISR_TMR1I 0x00020000UL -#define ISR_TMR0I 0x00010000UL -#define ISR_SRCI 0x00008000UL -#define ISR_LSTPEI 0x00004000UL -#define ISR_LSTEI 0x00002000UL -#define ISR_OVFI 0x00001000UL -#define ISR_FLONI 0x00000800UL -#define ISR_RACEI 0x00000400UL -#define ISR_TXWB1I 0x00000200UL -#define ISR_TXWB0I 0x00000100UL -#define ISR_PTX3I 0x00000080UL -#define ISR_PTX2I 0x00000040UL -#define ISR_PTX1I 0x00000020UL -#define ISR_PTX0I 0x00000010UL -#define ISR_PTXI 0x00000008UL -#define ISR_PRXI 0x00000004UL -#define ISR_PPTXI 0x00000002UL -#define ISR_PPRXI 0x00000001UL - -/* - * Bits in the IMR register - */ - -#define IMR_TXSTLM 0x02000000UL -#define IMR_UDPIM 0x00400000UL -#define IMR_MIBFIM 0x00200000UL -#define IMR_SHDNIM 0x00100000UL -#define IMR_PHYIM 0x00080000UL -#define IMR_PWEIM 0x00040000UL -#define IMR_TMR1IM 0x00020000UL -#define IMR_TMR0IM 0x00010000UL - -#define IMR_SRCIM 0x00008000UL -#define IMR_LSTPEIM 0x00004000UL -#define IMR_LSTEIM 0x00002000UL -#define IMR_OVFIM 0x00001000UL -#define IMR_FLONIM 0x00000800UL -#define IMR_RACEIM 0x00000400UL -#define IMR_TXWB1IM 0x00000200UL -#define IMR_TXWB0IM 0x00000100UL - -#define IMR_PTX3IM 0x00000080UL -#define IMR_PTX2IM 0x00000040UL -#define IMR_PTX1IM 0x00000020UL -#define IMR_PTX0IM 0x00000010UL -#define IMR_PTXIM 0x00000008UL -#define IMR_PRXIM 0x00000004UL -#define IMR_PPTXIM 0x00000002UL -#define IMR_PPRXIM 0x00000001UL - -/* 0x0013FB0FUL = initial value of IMR */ - -#define INT_MASK_DEF ( IMR_PPTXIM|IMR_PPRXIM| IMR_PTXIM|IMR_PRXIM | \ - IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM| \ - IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\ - IMR_SHDNIM |IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM ) - -/* - * Bits in the TDCSR0/1, RDCSR0 register - */ - -#define TRDCSR_DEAD 0x0008 -#define TRDCSR_WAK 0x0004 -#define TRDCSR_ACT 0x0002 -#define TRDCSR_RUN 0x0001 - -/* - * Bits in the CAMADDR register - */ - -#define CAMADDR_CAMEN 0x80 -#define CAMADDR_VCAMSL 0x40 - -/* - * Bits in the CAMCR register - */ - -#define CAMCR_PS1 0x80 -#define CAMCR_PS0 0x40 -#define CAMCR_AITRPKT 0x20 -#define CAMCR_AITR16 0x10 -#define CAMCR_CAMRD 0x08 -#define CAMCR_CAMWR 0x04 -#define CAMCR_PS_CAM_MASK 0x40 -#define CAMCR_PS_CAM_DATA 0x80 -#define CAMCR_PS_MAR 0x00 - -/* - * Bits in the MIICFG register - */ - -#define MIICFG_MPO1 0x80 -#define MIICFG_MPO0 0x40 -#define MIICFG_MFDC 0x20 - -/* - * Bits in the MIISR register - */ - -#define MIISR_MIDLE 0x80 - -/* - * Bits in the PHYSR0 register - */ - -#define PHYSR0_PHYRST 0x80 -#define PHYSR0_LINKGD 0x40 -#define PHYSR0_FDPX 0x10 -#define PHYSR0_SPDG 0x08 -#define PHYSR0_SPD10 0x04 -#define PHYSR0_RXFLC 0x02 -#define PHYSR0_TXFLC 0x01 - -/* - * Bits in the PHYSR1 register - */ - -#define PHYSR1_PHYTBI 0x01 - -/* - * Bits in the MIICR register - */ - -#define MIICR_MAUTO 0x80 -#define MIICR_RCMD 0x40 -#define MIICR_WCMD 0x20 -#define MIICR_MDPM 0x10 -#define MIICR_MOUT 0x08 -#define MIICR_MDO 0x04 -#define MIICR_MDI 0x02 -#define MIICR_MDC 0x01 - -/* - * Bits in the MIIADR register - */ - -#define MIIADR_SWMPL 0x80 - -/* - * Bits in the CFGA register - */ - -#define CFGA_PMHCTG 0x08 -#define CFGA_GPIO1PD 0x04 -#define CFGA_ABSHDN 0x02 -#define CFGA_PACPI 0x01 - -/* - * Bits in the CFGB register - */ - -#define CFGB_GTCKOPT 0x80 -#define CFGB_MIIOPT 0x40 -#define CFGB_CRSEOPT 0x20 -#define CFGB_OFSET 0x10 -#define CFGB_CRANDOM 0x08 -#define CFGB_CAP 0x04 -#define CFGB_MBA 0x02 -#define CFGB_BAKOPT 0x01 - -/* - * Bits in the CFGC register - */ - -#define CFGC_EELOAD 0x80 -#define CFGC_BROPT 0x40 -#define CFGC_DLYEN 0x20 -#define CFGC_DTSEL 0x10 -#define CFGC_BTSEL 0x08 -#define CFGC_BPS2 0x04 /* bootrom select[2] */ -#define CFGC_BPS1 0x02 /* bootrom select[1] */ -#define CFGC_BPS0 0x01 /* bootrom select[0] */ - -/* - * Bits in the CFGD register - */ - -#define CFGD_IODIS 0x80 -#define CFGD_MSLVDACEN 0x40 -#define CFGD_CFGDACEN 0x20 -#define CFGD_PCI64EN 0x10 -#define CFGD_HTMRL4 0x08 - -/* - * Bits in the DCFG1 register - */ - -#define DCFG_XMWI 0x8000 -#define DCFG_XMRM 0x4000 -#define DCFG_XMRL 0x2000 -#define DCFG_PERDIS 0x1000 -#define DCFG_MRWAIT 0x0400 -#define DCFG_MWWAIT 0x0200 -#define DCFG_LATMEN 0x0100 - -/* - * Bits in the MCFG0 register - */ - -#define MCFG_RXARB 0x0080 -#define MCFG_RFT1 0x0020 -#define MCFG_RFT0 0x0010 -#define MCFG_LOWTHOPT 0x0008 -#define MCFG_PQEN 0x0004 -#define MCFG_RTGOPT 0x0002 -#define MCFG_VIDFR 0x0001 - -/* - * Bits in the MCFG1 register - */ - -#define MCFG_TXARB 0x8000 -#define MCFG_TXQBK1 0x0800 -#define MCFG_TXQBK0 0x0400 -#define MCFG_TXQNOBK 0x0200 -#define MCFG_SNAPOPT 0x0100 - -/* - * Bits in the PMCC register - */ - -#define PMCC_DSI 0x80 -#define PMCC_D2_DIS 0x40 -#define PMCC_D1_DIS 0x20 -#define PMCC_D3C_EN 0x10 -#define PMCC_D3H_EN 0x08 -#define PMCC_D2_EN 0x04 -#define PMCC_D1_EN 0x02 -#define PMCC_D0_EN 0x01 - -/* - * Bits in STICKHW - */ - -#define STICKHW_SWPTAG 0x10 -#define STICKHW_WOLSR 0x08 -#define STICKHW_WOLEN 0x04 -#define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */ -#define STICKHW_DS0 0x01 /* suspend well DS write port */ - -/* - * Bits in the MIBCR register - */ - -#define MIBCR_MIBISTOK 0x80 -#define MIBCR_MIBISTGO 0x40 -#define MIBCR_MIBINC 0x20 -#define MIBCR_MIBHI 0x10 -#define MIBCR_MIBFRZ 0x08 -#define MIBCR_MIBFLSH 0x04 -#define MIBCR_MPTRINI 0x02 -#define MIBCR_MIBCLR 0x01 - -/* - * Bits in the EERSV register - */ - -#define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */ - -#define EERSV_BOOT_MASK ((u8) 0x06) -#define EERSV_BOOT_INT19 ((u8) 0x00) -#define EERSV_BOOT_INT18 ((u8) 0x02) -#define EERSV_BOOT_LOCAL ((u8) 0x04) -#define EERSV_BOOT_BEV ((u8) 0x06) - - -/* - * Bits in BPCMD - */ - -#define BPCMD_BPDNE 0x80 -#define BPCMD_EBPWR 0x02 -#define BPCMD_EBPRD 0x01 - -/* - * Bits in the EECSR register - */ - -#define EECSR_EMBP 0x40 /* eeprom embedded programming */ -#define EECSR_RELOAD 0x20 /* eeprom content reload */ -#define EECSR_DPM 0x10 /* eeprom direct programming */ -#define EECSR_ECS 0x08 /* eeprom CS pin */ -#define EECSR_ECK 0x04 /* eeprom CK pin */ -#define EECSR_EDI 0x02 /* eeprom DI pin */ -#define EECSR_EDO 0x01 /* eeprom DO pin */ - -/* - * Bits in the EMBCMD register - */ - -#define EMBCMD_EDONE 0x80 -#define EMBCMD_EWDIS 0x08 -#define EMBCMD_EWEN 0x04 -#define EMBCMD_EWR 0x02 -#define EMBCMD_ERD 0x01 - -/* - * Bits in TESTCFG register - */ - -#define TESTCFG_HBDIS 0x80 - -/* - * Bits in CHIPGCR register - */ - -#define CHIPGCR_FCGMII 0x80 -#define CHIPGCR_FCFDX 0x40 -#define CHIPGCR_FCRESV 0x20 -#define CHIPGCR_FCMODE 0x10 -#define CHIPGCR_LPSOPT 0x08 -#define CHIPGCR_TM1US 0x04 -#define CHIPGCR_TM0US 0x02 -#define CHIPGCR_PHYINTEN 0x01 - -/* - * Bits in WOLCR0 - */ - -#define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */ -#define WOLCR_MSWOLEN6 0x0040 -#define WOLCR_MSWOLEN5 0x0020 -#define WOLCR_MSWOLEN4 0x0010 -#define WOLCR_MSWOLEN3 0x0008 -#define WOLCR_MSWOLEN2 0x0004 -#define WOLCR_MSWOLEN1 0x0002 -#define WOLCR_MSWOLEN0 0x0001 -#define WOLCR_ARP_EN 0x0001 - -/* - * Bits in WOLCR1 - */ - -#define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */ -#define WOLCR_LINKON_EN 0x0400 /* link on detected enable */ -#define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */ -#define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */ - - -/* - * Bits in PWCFG - */ - -#define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */ -#define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */ -#define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */ -#define PWCFG_LEGCY_WOL 0x10 -#define PWCFG_PMCSR_PME_SR 0x08 -#define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */ -#define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */ -#define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */ - -/* - * Bits in WOLCFG - */ - -#define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */ -#define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */ -#define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */ -#define WOLCFG_SMIIACC 0x08 /* ?? */ -#define WOLCFG_SGENWH 0x02 -#define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII - to report status change */ -/* - * Bits in WOLSR1 - */ - -#define WOLSR_LINKOFF_INT 0x0800 -#define WOLSR_LINKON_INT 0x0400 -#define WOLSR_MAGIC_INT 0x0200 -#define WOLSR_UNICAST_INT 0x0100 - -/* - * Ethernet address filter type - */ - -#define PKT_TYPE_NONE 0x0000 /* Turn off receiver */ -#define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */ -#define PKT_TYPE_MULTICAST 0x0002 -#define PKT_TYPE_ALL_MULTICAST 0x0004 -#define PKT_TYPE_BROADCAST 0x0008 -#define PKT_TYPE_PROMISCUOUS 0x0020 -#define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */ -#define PKT_TYPE_RUNT 0x4000 -#define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */ - -/* - * Loopback mode - */ - -#define MAC_LB_NONE 0x00 -#define MAC_LB_INTERNAL 0x01 -#define MAC_LB_EXTERNAL 0x02 - -/* - * Enabled mask value of irq - */ - -#if defined(_SIM) -#define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR - set IMR0 to 0x0F according to spec */ - -#else -#define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR - ignore MIBFI,RACEI to - reduce intr. frequency - NOTE.... do not enable NoBuf int mask at driver driver - when (1) NoBuf -> RxThreshold = SF - (2) OK -> RxThreshold = original value - */ -#endif - -/* - * Revision id - */ - -#define REV_ID_VT3119_A0 0x00 -#define REV_ID_VT3119_A1 0x01 -#define REV_ID_VT3216_A0 0x10 - -/* - * Max time out delay time - */ - -#define W_MAX_TIMEOUT 0x0FFFU - - -/* - * MAC registers as a structure. Cannot be directly accessed this - * way but generates offsets for readl/writel() calls - */ - -struct mac_regs { - volatile u8 PAR[6]; /* 0x00 */ - volatile u8 RCR; - volatile u8 TCR; - - volatile u32 CR0Set; /* 0x08 */ - volatile u32 CR0Clr; /* 0x0C */ - - volatile u8 MARCAM[8]; /* 0x10 */ - - volatile u32 DecBaseHi; /* 0x18 */ - volatile u16 DbfBaseHi; /* 0x1C */ - volatile u16 reserved_1E; - - volatile u16 ISRCTL; /* 0x20 */ - volatile u8 TXESR; - volatile u8 RXESR; - - volatile u32 ISR; /* 0x24 */ - volatile u32 IMR; - - volatile u32 TDStatusPort; /* 0x2C */ - - volatile u16 TDCSRSet; /* 0x30 */ - volatile u8 RDCSRSet; - volatile u8 reserved_33; - volatile u16 TDCSRClr; - volatile u8 RDCSRClr; - volatile u8 reserved_37; - - volatile u32 RDBaseLo; /* 0x38 */ - volatile u16 RDIdx; /* 0x3C */ - volatile u16 reserved_3E; - - volatile u32 TDBaseLo[4]; /* 0x40 */ - - volatile u16 RDCSize; /* 0x50 */ - volatile u16 TDCSize; /* 0x52 */ - volatile u16 TDIdx[4]; /* 0x54 */ - volatile u16 tx_pause_timer; /* 0x5C */ - volatile u16 RBRDU; /* 0x5E */ - - volatile u32 FIFOTest0; /* 0x60 */ - volatile u32 FIFOTest1; /* 0x64 */ - - volatile u8 CAMADDR; /* 0x68 */ - volatile u8 CAMCR; /* 0x69 */ - volatile u8 GFTEST; /* 0x6A */ - volatile u8 FTSTCMD; /* 0x6B */ - - volatile u8 MIICFG; /* 0x6C */ - volatile u8 MIISR; - volatile u8 PHYSR0; - volatile u8 PHYSR1; - volatile u8 MIICR; - volatile u8 MIIADR; - volatile u16 MIIDATA; - - volatile u16 SoftTimer0; /* 0x74 */ - volatile u16 SoftTimer1; - - volatile u8 CFGA; /* 0x78 */ - volatile u8 CFGB; - volatile u8 CFGC; - volatile u8 CFGD; - - volatile u16 DCFG; /* 0x7C */ - volatile u16 MCFG; - - volatile u8 TBIST; /* 0x80 */ - volatile u8 RBIST; - volatile u8 PMCPORT; - volatile u8 STICKHW; - - volatile u8 MIBCR; /* 0x84 */ - volatile u8 reserved_85; - volatile u8 rev_id; - volatile u8 PORSTS; - - volatile u32 MIBData; /* 0x88 */ - - volatile u16 EEWrData; - - volatile u8 reserved_8E; - volatile u8 BPMDWr; - volatile u8 BPCMD; - volatile u8 BPMDRd; - - volatile u8 EECHKSUM; /* 0x92 */ - volatile u8 EECSR; - - volatile u16 EERdData; /* 0x94 */ - volatile u8 EADDR; - volatile u8 EMBCMD; - - - volatile u8 JMPSR0; /* 0x98 */ - volatile u8 JMPSR1; - volatile u8 JMPSR2; - volatile u8 JMPSR3; - volatile u8 CHIPGSR; /* 0x9C */ - volatile u8 TESTCFG; - volatile u8 DEBUG; - volatile u8 CHIPGCR; - - volatile u16 WOLCRSet; /* 0xA0 */ - volatile u8 PWCFGSet; - volatile u8 WOLCFGSet; - - volatile u16 WOLCRClr; /* 0xA4 */ - volatile u8 PWCFGCLR; - volatile u8 WOLCFGClr; - - volatile u16 WOLSRSet; /* 0xA8 */ - volatile u16 reserved_AA; - - volatile u16 WOLSRClr; /* 0xAC */ - volatile u16 reserved_AE; - - volatile u16 PatternCRC[8]; /* 0xB0 */ - volatile u32 ByteMask[4][4]; /* 0xC0 */ -} __attribute__ ((__packed__)); - - -enum hw_mib { - HW_MIB_ifRxAllPkts = 0, - HW_MIB_ifRxOkPkts, - HW_MIB_ifTxOkPkts, - HW_MIB_ifRxErrorPkts, - HW_MIB_ifRxRuntOkPkt, - HW_MIB_ifRxRuntErrPkt, - HW_MIB_ifRx64Pkts, - HW_MIB_ifTx64Pkts, - HW_MIB_ifRx65To127Pkts, - HW_MIB_ifTx65To127Pkts, - HW_MIB_ifRx128To255Pkts, - HW_MIB_ifTx128To255Pkts, - HW_MIB_ifRx256To511Pkts, - HW_MIB_ifTx256To511Pkts, - HW_MIB_ifRx512To1023Pkts, - HW_MIB_ifTx512To1023Pkts, - HW_MIB_ifRx1024To1518Pkts, - HW_MIB_ifTx1024To1518Pkts, - HW_MIB_ifTxEtherCollisions, - HW_MIB_ifRxPktCRCE, - HW_MIB_ifRxJumboPkts, - HW_MIB_ifTxJumboPkts, - HW_MIB_ifRxMacControlFrames, - HW_MIB_ifTxMacControlFrames, - HW_MIB_ifRxPktFAE, - HW_MIB_ifRxLongOkPkt, - HW_MIB_ifRxLongPktErrPkt, - HW_MIB_ifTXSQEErrors, - HW_MIB_ifRxNobuf, - HW_MIB_ifRxSymbolErrors, - HW_MIB_ifInRangeLengthErrors, - HW_MIB_ifLateCollisions, - HW_MIB_SIZE -}; - -enum chip_type { - CHIP_TYPE_VT6110 = 1, -}; - -struct velocity_info_tbl { - enum chip_type chip_id; - char *name; - int io_size; - int txqueue; - u32 flags; -}; - -static struct velocity_info_tbl *info; - -#define mac_hw_mibs_init(regs) {\ - BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\ - BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\ - do {}\ - while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\ - BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\ -} - -#define mac_read_isr(regs) readl(&((regs)->ISR)) -#define mac_write_isr(regs, x) writel((x),&((regs)->ISR)) -#define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR)) - -#define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR)); -#define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr)) -#define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set)) - -#define mac_hw_mibs_read(regs, MIBs) {\ - int i;\ - BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\ - for (i=0;iMIBData));\ - }\ -} - -#define mac_set_dma_length(regs, n) {\ - BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\ -} - -#define mac_set_rx_thresh(regs, n) {\ - BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\ -} - -#define mac_rx_queue_run(regs) {\ - writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\ -} - -#define mac_rx_queue_wake(regs) {\ - writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\ -} - -#define mac_tx_queue_run(regs, n) {\ - writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\ -} - -#define mac_tx_queue_wake(regs, n) {\ - writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\ -} - -#define mac_eeprom_reload(regs) {\ - int i=0;\ - BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\ - do {\ - udelay(10);\ - if (i++>0x1000) {\ - break;\ - }\ - }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\ -} - -enum velocity_cam_type { - VELOCITY_VLAN_ID_CAM = 0, - VELOCITY_MULTICAST_CAM -}; - -/** - * mac_get_cam_mask - Read a CAM mask - * @regs: register block for this velocity - * @mask: buffer to store mask - * @cam_type: CAM to fetch - * - * Fetch the mask bits of the selected CAM and store them into the - * provided mask buffer. - */ - -static inline void mac_get_cam_mask(struct mac_regs *regs, u8 * mask, - enum velocity_cam_type cam_type) -{ - int i; - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_VCAMSL, ®s->CAMADDR); - else - writeb(0, ®s->CAMADDR); - - /* read mask */ - for (i = 0; i < 8; i++) - *mask++ = readb(&(regs->MARCAM[i])); - - /* disable CAMEN */ - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - -} - -/** - * mac_set_cam_mask - Set a CAM mask - * @regs: register block for this velocity - * @mask: CAM mask to load - * @cam_type: CAM to store - * - * Store a new mask into a CAM - */ - -static inline void mac_set_cam_mask(struct mac_regs *regs, u8 * mask, - enum velocity_cam_type cam_type) -{ - int i; - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN, ®s->CAMADDR); - - for (i = 0; i < 8; i++) { - writeb(*mask++, &(regs->MARCAM[i])); - } - /* disable CAMEN */ - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_set_cam - set CAM data - * @regs: register block of this velocity - * @idx: Cam index - * @addr: 2 or 6 bytes of CAM data - * @cam_type: CAM to load - * - * Load an address or vlan tag into a CAM - */ - -static inline void mac_set_cam(struct mac_regs *regs, int idx, u8 * addr, - enum velocity_cam_type cam_type) -{ - int i; - - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - idx &= (64 - 1); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, - ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writew(*((u16 *) addr), ®s->MARCAM[0]); - else { - for (i = 0; i < 6; i++) { - writeb(*addr++, &(regs->MARCAM[i])); - } - } - BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); - - udelay(10); - - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_get_cam - fetch CAM data - * @regs: register block of this velocity - * @idx: Cam index - * @addr: buffer to hold up to 6 bytes of CAM data - * @cam_type: CAM to load - * - * Load an address or vlan tag from a CAM into the buffer provided by - * the caller. VLAN tags are 2 bytes the address cam entries are 6. - */ - -static inline void mac_get_cam(struct mac_regs *regs, int idx, u8 * addr, - enum velocity_cam_type cam_type) -{ - int i; - - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - idx &= (64 - 1); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, - ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); - - BYTE_REG_BITS_ON(CAMCR_CAMRD, ®s->CAMCR); - - udelay(10); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - *((u16 *) addr) = readw(&(regs->MARCAM[0])); - else - for (i = 0; i < 6; i++, addr++) - *((u8 *) addr) = readb(&(regs->MARCAM[i])); - - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_wol_reset - reset WOL after exiting low power - * @regs: register block of this velocity - * - * Called after we drop out of wake on lan mode in order to - * reset the Wake on lan features. This function doesn't restore - * the rest of the logic from the result of sleep/wakeup - */ - -inline static void mac_wol_reset(struct mac_regs *regs) -{ - - /* Turn off SWPTAG right after leaving power mode */ - BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW); - /* clear sticky bits */ - BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); - - BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR); - BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); - /* disable force PME-enable */ - writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr); - /* disable power-event config bit */ - writew(0xFFFF, ®s->WOLCRClr); - /* clear power status */ - writew(0xFFFF, ®s->WOLSRClr); -} - - -/* - * Header for WOL definitions. Used to compute hashes - */ - -typedef u8 MCAM_ADDR[ETH_ALEN]; - -struct arp_packet { - u8 dest_mac[ETH_ALEN]; - u8 src_mac[ETH_ALEN]; - u16 type; - u16 ar_hrd; - u16 ar_pro; - u8 ar_hln; - u8 ar_pln; - u16 ar_op; - u8 ar_sha[ETH_ALEN]; - u8 ar_sip[4]; - u8 ar_tha[ETH_ALEN]; - u8 ar_tip[4]; -} __attribute__ ((__packed__)); - -struct _magic_packet { - u8 dest_mac[6]; - u8 src_mac[6]; - u16 type; - u8 MAC[16][6]; - u8 password[6]; -} __attribute__ ((__packed__)); - -/* - * Store for chip context when saving and restoring status. Not - * all fields are saved/restored currently. - */ - -struct velocity_context { - u8 mac_reg[256]; - MCAM_ADDR cam_addr[MCAM_SIZE]; - u16 vcam[VCAM_SIZE]; - u32 cammask[2]; - u32 patcrc[2]; - u32 pattern[8]; -}; - - -/* - * MII registers. - */ - - -/* - * Registers in the MII (offset unit is WORD) - */ - -#define MII_REG_BMCR 0x00 // physical address -#define MII_REG_BMSR 0x01 // -#define MII_REG_PHYID1 0x02 // OUI -#define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID -#define MII_REG_ANAR 0x04 // -#define MII_REG_ANLPAR 0x05 // -#define MII_REG_G1000CR 0x09 // -#define MII_REG_G1000SR 0x0A // -#define MII_REG_MODCFG 0x10 // -#define MII_REG_TCSR 0x16 // -#define MII_REG_PLED 0x1B // -// NS, MYSON only -#define MII_REG_PCR 0x17 // -// ESI only -#define MII_REG_PCSR 0x17 // -#define MII_REG_AUXCR 0x1C // - -// Marvell 88E1000/88E1000S -#define MII_REG_PSCR 0x10 // PHY specific control register - -// -// Bits in the BMCR register -// -#define BMCR_RESET 0x8000 // -#define BMCR_LBK 0x4000 // -#define BMCR_SPEED100 0x2000 // -#define BMCR_AUTO 0x1000 // -#define BMCR_PD 0x0800 // -#define BMCR_ISO 0x0400 // -#define BMCR_REAUTO 0x0200 // -#define BMCR_FDX 0x0100 // -#define BMCR_SPEED1G 0x0040 // -// -// Bits in the BMSR register -// -#define BMSR_AUTOCM 0x0020 // -#define BMSR_LNK 0x0004 // - -// -// Bits in the ANAR register -// -#define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support -#define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support -#define ANAR_T4 0x0200 // -#define ANAR_TXFD 0x0100 // -#define ANAR_TX 0x0080 // -#define ANAR_10FD 0x0040 // -#define ANAR_10 0x0020 // -// -// Bits in the ANLPAR register -// -#define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support -#define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support -#define ANLPAR_T4 0x0200 // -#define ANLPAR_TXFD 0x0100 // -#define ANLPAR_TX 0x0080 // -#define ANLPAR_10FD 0x0040 // -#define ANLPAR_10 0x0020 // - -// -// Bits in the G1000CR register -// -#define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable -#define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable - -// -// Bits in the G1000SR register -// -#define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable -#define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable - -#define TCSR_ECHODIS 0x2000 // -#define AUXCR_MDPPS 0x0004 // - -// Bits in the PLED register -#define PLED_LALBE 0x0004 // - -// Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h) -#define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit - -#define PHYID_CICADA_CS8201 0x000FC410UL -#define PHYID_VT3216_32BIT 0x000FC610UL -#define PHYID_VT3216_64BIT 0x000FC600UL -#define PHYID_MARVELL_1000 0x01410C50UL -#define PHYID_MARVELL_1000S 0x01410C40UL - -#define PHYID_REV_ID_MASK 0x0000000FUL - -#define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK) -#define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) - -#define MII_REG_BITS_ON(x,i,p) do {\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - (w)|=(x);\ - velocity_mii_write((p),(i),(w));\ -} while (0) - -#define MII_REG_BITS_OFF(x,i,p) do {\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - (w)&=(~(x));\ - velocity_mii_write((p),(i),(w));\ -} while (0) - -#define MII_REG_BITS_IS_ON(x,i,p) ({\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - ((int) ((w) & (x)));}) - -#define MII_GET_PHY_ID(p) ({\ - u32 id; \ - u16 id2; \ - u16 id1; \ - velocity_mii_read((p),MII_REG_PHYID2, &id2);\ - velocity_mii_read((p),MII_REG_PHYID1, &id1);\ - id = ( ( (u32)id2 ) << 16 ) | id1; \ - (id);}) - -#ifdef LINUX -/* - * Inline debug routine - */ - - -enum velocity_msg_level { - MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation. - MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified. - MSG_LEVEL_INFO = 2, //Normal message. - MSG_LEVEL_VERBOSE = 3, //Will report all trival errors. - MSG_LEVEL_DEBUG = 4 //Only for debug purpose. -}; - -#ifdef VELOCITY_DEBUG -#define ASSERT(x) { \ - if (!(x)) { \ - printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\ - __FUNCTION__, __LINE__);\ - BUG(); \ - }\ -} -#define VELOCITY_DBG(p,args...) printk(p, ##args) -#else -#define ASSERT(x) -#define VELOCITY_DBG(x) -#endif - -#define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printf( p ,##args);} while (0) - -#define VELOCITY_PRT_CAMMASK(p,t) {\ - int i;\ - if ((t)==VELOCITY_MULTICAST_CAM) {\ - for (i=0;i<(MCAM_SIZE/8);i++)\ - printk("%02X",(p)->mCAMmask[i]);\ - }\ - else {\ - for (i=0;i<(VCAM_SIZE/8);i++)\ - printk("%02X",(p)->vCAMmask[i]);\ - }\ - printk("\n");\ -} - -#endif - -#define VELOCITY_WOL_MAGIC 0x00000000UL -#define VELOCITY_WOL_PHY 0x00000001UL -#define VELOCITY_WOL_ARP 0x00000002UL -#define VELOCITY_WOL_UCAST 0x00000004UL -#define VELOCITY_WOL_BCAST 0x00000010UL -#define VELOCITY_WOL_MCAST 0x00000020UL -#define VELOCITY_WOL_MAGIC_SEC 0x00000040UL - -/* - * Flags for options - */ - -#define VELOCITY_FLAGS_TAGGING 0x00000001UL -#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL -#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL -#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL -#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL - -#define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL - -/* - * Flags for driver status - */ - -#define VELOCITY_FLAGS_OPENED 0x00010000UL -#define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL -#define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL -#define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL - -/* - * Flags for MII status - */ - -#define VELOCITY_LINK_FAIL 0x00000001UL -#define VELOCITY_SPEED_10 0x00000002UL -#define VELOCITY_SPEED_100 0x00000004UL -#define VELOCITY_SPEED_1000 0x00000008UL -#define VELOCITY_DUPLEX_FULL 0x00000010UL -#define VELOCITY_AUTONEG_ENABLE 0x00000020UL -#define VELOCITY_FORCED_BY_EEPROM 0x00000040UL - -/* - * For velocity_set_media_duplex - */ - -#define VELOCITY_LINK_CHANGE 0x00000001UL - -enum speed_opt { - SPD_DPX_AUTO = 0, - SPD_DPX_100_HALF = 1, - SPD_DPX_100_FULL = 2, - SPD_DPX_10_HALF = 3, - SPD_DPX_10_FULL = 4 -}; - -enum velocity_init_type { - VELOCITY_INIT_COLD = 0, - VELOCITY_INIT_RESET, - VELOCITY_INIT_WOL -}; - -enum velocity_flow_cntl_type { - FLOW_CNTL_DEFAULT = 1, - FLOW_CNTL_TX, - FLOW_CNTL_RX, - FLOW_CNTL_TX_RX, - FLOW_CNTL_DISABLE, -}; - -struct velocity_opt { - int numrx; /* Number of RX descriptors */ - int numtx; /* Number of TX descriptors */ - enum speed_opt spd_dpx; /* Media link mode */ - int vid; /* vlan id */ - int DMA_length; /* DMA length */ - int rx_thresh; /* RX_THRESH */ - int flow_cntl; - int wol_opts; /* Wake on lan options */ - int td_int_count; - int int_works; - int rx_bandwidth_hi; - int rx_bandwidth_lo; - int rx_bandwidth_en; - u32 flags; -}; - -#define RX_DESC_MIN 4 -#define RX_DESC_MAX 255 -#define RX_DESC_DEF RX_DESC_MIN - -#define TX_DESC_MIN 1 -#define TX_DESC_MAX 256 -#define TX_DESC_DEF TX_DESC_MIN - -static struct velocity_info { -// struct list_head list; - - struct pci_device *pdev; -// struct net_device *dev; -// struct net_device_stats stats; - -#ifdef CONFIG_PM - u32 pci_state[16]; -#endif - -// dma_addr_t rd_pool_dma; -// dma_addr_t td_pool_dma[TX_QUEUE_NO]; - -// dma_addr_t tx_bufs_dma; - u8 *tx_bufs; - - u8 ip_addr[4]; - enum chip_type chip_id; - - struct mac_regs *mac_regs; - unsigned long memaddr; - unsigned long ioaddr; - u32 io_size; - - u8 rev_id; - -#define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)])) - - int num_txq; - - volatile int td_used[TX_QUEUE_NO]; - int td_curr; - int td_tail[TX_QUEUE_NO]; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ - unsigned char *tx_buffs; - unsigned char *rx_buffs; - - unsigned char *txb; - unsigned char *rxb; - struct tx_desc *td_rings; - struct velocity_td_info *td_infos[TX_QUEUE_NO]; - - int rd_curr; - int rd_dirty; - u32 rd_filled; - struct rx_desc *rd_ring; - struct velocity_rd_info *rd_info; /* It's an array */ - -#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) - u32 mib_counter[MAX_HW_MIB_COUNTER]; - struct velocity_opt options; - - u32 int_mask; - - u32 flags; - - int rx_buf_sz; - u32 mii_status; - u32 phy_id; - int multicast_limit; - - u8 vCAMmask[(VCAM_SIZE / 8)]; - u8 mCAMmask[(MCAM_SIZE / 8)]; - -// spinlock_t lock; - - int wol_opts; - u8 wol_passwd[6]; - - struct velocity_context context; - - u32 ticks; - u32 rx_bytes; - -} vptx; - -static struct velocity_info *vptr; - -#ifdef LINUX -/** - * velocity_get_ip - find an IP address for the device - * @vptr: Velocity to query - * - * Dig out an IP address for this interface so that we can - * configure wakeup with WOL for ARP. If there are multiple IP - * addresses on this chain then we use the first - multi-IP WOL is not - * supported. - * - * CHECK ME: locking - */ - -inline static int velocity_get_ip(struct velocity_info *vptr) -{ - struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr; - struct in_ifaddr *ifa; - - if (in_dev != NULL) { - ifa = (struct in_ifaddr *) in_dev->ifa_list; - if (ifa != NULL) { - memcpy(vptr->ip_addr, &ifa->ifa_address, 4); - return 0; - } - } - return -ENOENT; -} - -/** - * velocity_update_hw_mibs - fetch MIB counters from chip - * @vptr: velocity to update - * - * The velocity hardware keeps certain counters in the hardware - * side. We need to read these when the user asks for statistics - * or when they overflow (causing an interrupt). The read of the - * statistic clears it, so we keep running master counters in user - * space. - */ - -static inline void velocity_update_hw_mibs(struct velocity_info *vptr) -{ - u32 tmp; - int i; - BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)); - - while (BYTE_REG_BITS_IS_ON - (MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR))); - - BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR)); - for (i = 0; i < HW_MIB_SIZE; i++) { - tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL; - vptr->mib_counter[i] += tmp; - } -} -#endif -/** - * init_flow_control_register - set up flow control - * @vptr: velocity to configure - * - * Configure the flow control registers for this velocity device. - */ - -static inline void init_flow_control_register(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - - /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1} - depend on RD=64, and Turn on XNOEN in FlowCR1 */ - writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0), - ®s->CR0Set); - writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0), - ®s->CR0Clr); - - /* Set TxPauseTimer to 0xFFFF */ - writew(0xFFFF, ®s->tx_pause_timer); - - /* Initialize RBRDU to Rx buffer count. */ - writew(vptr->options.numrx, ®s->RBRDU); -} - - -#endif diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2c4c9920..c6b0e794 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -114,7 +114,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_tlan ( ERRFILE_DRIVER | 0x00420000 ) #define ERRFILE_tulip ( ERRFILE_DRIVER | 0x00430000 ) #define ERRFILE_rhine ( ERRFILE_DRIVER | 0x00440000 ) -#define ERRFILE_via_velocity ( ERRFILE_DRIVER | 0x00450000 ) +#define ERRFILE_velocity ( ERRFILE_DRIVER | 0x00450000 ) #define ERRFILE_w89c840 ( ERRFILE_DRIVER | 0x00460000 ) #define ERRFILE_ipoib ( ERRFILE_DRIVER | 0x00470000 ) #define ERRFILE_e1000_main ( ERRFILE_DRIVER | 0x00480000 ) From 75bd5b54a8673ea4843903d0e7c743aa5b440d38 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 11:59:13 +0200 Subject: [PATCH 26/65] [settings] Add support for navigation keys in "config" user interface Add support for page up, page down, home and end keys, matching the navigation logic used in the menu user interface. Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/hci/tui/settings_ui.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c index eb82ae54..f7efbbb4 100644 --- a/src/hci/tui/settings_ui.c +++ b/src/hci/tui/settings_ui.c @@ -508,13 +508,25 @@ static int main_loop ( struct settings *settings ) { key = getkey ( 0 ); move = 0; switch ( key ) { - case KEY_DOWN: - if ( widget.current < ( widget.num_rows - 1 ) ) - move = +1; - break; case KEY_UP: - if ( widget.current > 0 ) - move = -1; + move = -1; + break; + case KEY_DOWN: + move = +1; + break; + case KEY_PPAGE: + move = ( widget.first_visible - + widget.current - 1 ); + break; + case KEY_NPAGE: + move = ( widget.first_visible - widget.current + + SETTINGS_LIST_ROWS ); + break; + case KEY_HOME: + move = -widget.num_rows; + break; + case KEY_END: + move = +widget.num_rows; break; case CTRL_D: if ( ! widget.row.setting ) @@ -545,10 +557,16 @@ static int main_loop ( struct settings *settings ) { } if ( move ) { next = ( widget.current + move ); - draw_setting_row ( &widget ); - redraw = 1; - reveal_setting_row ( &widget, next ); - select_setting_row ( &widget, next ); + if ( ( int ) next < 0 ) + next = 0; + if ( next >= widget.num_rows ) + next = ( widget.num_rows - 1 ); + if ( next != widget.current ) { + draw_setting_row ( &widget ); + redraw = 1; + reveal_setting_row ( &widget, next ); + select_setting_row ( &widget, next ); + } } } } From 918fb437439d1bfb172e7fbdfc03b2b25664bee7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 12:15:48 +0200 Subject: [PATCH 27/65] [realtek] Allow extra space in RX buffers Some hardware (observed with an onboard RTL8168) will erroneously report a buffer overflow error if the received packet exactly fills the receive buffer. Fix by adding an extra four bytes of padding to each receive buffer. Debugged-by: Thomas Miletich Signed-off-by: Michael Brown --- src/drivers/net/realtek.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index 1cd85d95..a17f963f 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -226,7 +226,8 @@ enum realtek_legacy_status { #define RTL_NUM_RX_DESC 4 /** Receive buffer length */ -#define RTL_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define RTL_RX_MAX_LEN \ + ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ + 4 /* extra space */ ) /** A Realtek descriptor ring */ struct realtek_ring { From 9b93b669d13611763470f190954cbb711448f749 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Mon, 15 Jul 2013 12:30:57 +0200 Subject: [PATCH 28/65] [legal] Add missing FILE_LICENCE declarations Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/config/defaults/efi.h | 2 ++ src/config/defaults/linux.h | 2 ++ src/drivers/net/ath/ath9k/ar5008_initvals.h | 2 ++ src/drivers/net/ath/ath9k/ar9001_initvals.h | 2 ++ src/drivers/net/ath/ath9k/ar9002_initvals.h | 2 ++ src/drivers/net/ath/ath9k/ar9002_phy.h | 2 ++ src/drivers/net/ath/ath9k/hw-ops.h | 2 ++ src/drivers/net/ath/ath9k/phy.h | 2 ++ src/drivers/net/ath/ath9k/reg.h | 2 ++ src/include/ipxe/efi/ProcessorBind.h | 2 ++ src/include/ipxe/efi/efi.h | 2 ++ 11 files changed, 22 insertions(+) diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 7385aeab..4276d936 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #define UACCESS_EFI #define IOAPI_X86 #define PCIAPI_EFI diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 2b565943..bc5ba785 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #define CONSOLE_LINUX #define TIMER_LINUX #define UACCESS_LINUX diff --git a/src/drivers/net/ath/ath9k/ar5008_initvals.h b/src/drivers/net/ath/ath9k/ar5008_initvals.h index 479aed54..fcc15565 100644 --- a/src/drivers/net/ath/ath9k/ar5008_initvals.h +++ b/src/drivers/net/ath/ath9k/ar5008_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar5416Modes[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9001_initvals.h b/src/drivers/net/ath/ath9k/ar9001_initvals.h index 7dca4744..6c1ccd50 100644 --- a/src/drivers/net/ath/ath9k/ar9001_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9001_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar5416Modes_9100[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9002_initvals.h b/src/drivers/net/ath/ath9k/ar9002_initvals.h index dd121513..d7a5ac09 100644 --- a/src/drivers/net/ath/ath9k/ar9002_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9002_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar9280Modes_9280_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9002_phy.h b/src/drivers/net/ath/ath9k/ar9002_phy.h index 453af6dc..71d9162c 100644 --- a/src/drivers/net/ath/ath9k/ar9002_phy.h +++ b/src/drivers/net/ath/ath9k/ar9002_phy.h @@ -16,6 +16,8 @@ #ifndef AR9002_PHY_H #define AR9002_PHY_H +FILE_LICENCE ( BSD2 ); + #define AR_PHY_TEST 0x9800 #define PHY_AGC_CLR 0x10000000 #define RFSILENT_BB 0x00002000 diff --git a/src/drivers/net/ath/ath9k/hw-ops.h b/src/drivers/net/ath/ath9k/hw-ops.h index 65cff3b0..51c7b08e 100644 --- a/src/drivers/net/ath/ath9k/hw-ops.h +++ b/src/drivers/net/ath/ath9k/hw-ops.h @@ -17,6 +17,8 @@ #ifndef ATH9K_HW_OPS_H #define ATH9K_HW_OPS_H +FILE_LICENCE ( BSD2 ); + #include "hw.h" /* Hardware core and driver accessible callbacks */ diff --git a/src/drivers/net/ath/ath9k/phy.h b/src/drivers/net/ath/ath9k/phy.h index 8b380305..28f59ecd 100644 --- a/src/drivers/net/ath/ath9k/phy.h +++ b/src/drivers/net/ath/ath9k/phy.h @@ -17,6 +17,8 @@ #ifndef PHY_H #define PHY_H +FILE_LICENCE ( BSD2 ); + #define CHANSEL_DIV 15 #define CHANSEL_2G(_freq) (((_freq) * 0x10000) / CHANSEL_DIV) #define CHANSEL_5G(_freq) (((_freq) * 0x8000) / CHANSEL_DIV) diff --git a/src/drivers/net/ath/ath9k/reg.h b/src/drivers/net/ath/ath9k/reg.h index c18ee992..67762b6d 100644 --- a/src/drivers/net/ath/ath9k/reg.h +++ b/src/drivers/net/ath/ath9k/reg.h @@ -17,6 +17,8 @@ #ifndef REG_H #define REG_H +FILE_LICENCE ( BSD2 ); + #include "../reg.h" #define AR_CR 0x0008 diff --git a/src/include/ipxe/efi/ProcessorBind.h b/src/include/ipxe/efi/ProcessorBind.h index 535cd4ce..1294459f 100644 --- a/src/include/ipxe/efi/ProcessorBind.h +++ b/src/include/ipxe/efi/ProcessorBind.h @@ -1,6 +1,8 @@ #ifndef _IPXE_EFI_PROCESSOR_BIND_H #define _IPXE_EFI_PROCESSOR_BIND_H +FILE_LICENCE ( GPL2_OR_LATER ); + /* * EFI header files rely on having the CPU architecture directory * present in the search path in order to pick up ProcessorBind.h. We diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 0a21c6e7..a98b5588 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -21,6 +21,8 @@ * trailing whitespace. */ +FILE_LICENCE ( GPL2_OR_LATER ); + /* EFI headers rudely redefine NULL */ #undef NULL From c0af8c04333e499d2ed91dcb98b2dfe1aec1c7e3 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Sat, 13 Jul 2013 14:31:15 +0200 Subject: [PATCH 29/65] [cmdline] Add "poweroff" command Modified-by: Michael Brown Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/arch/i386/interface/pcbios/apm.c | 108 +++++++++++++++++++++++++++ src/arch/x86/include/bits/errfile.h | 1 + src/config/config.c | 3 + src/config/general.h | 1 + src/core/null_reboot.c | 12 +++ src/hci/commands/poweroff_cmd.c | 72 ++++++++++++++++++ src/include/ipxe/errfile.h | 2 + src/include/ipxe/reboot.h | 10 +++ src/interface/efi/efi_reboot.c | 17 +++++ 9 files changed, 226 insertions(+) create mode 100644 src/arch/i386/interface/pcbios/apm.c create mode 100644 src/hci/commands/poweroff_cmd.c diff --git a/src/arch/i386/interface/pcbios/apm.c b/src/arch/i386/interface/pcbios/apm.c new file mode 100644 index 00000000..3b13e1cd --- /dev/null +++ b/src/arch/i386/interface/pcbios/apm.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Advanced Power Management + * + */ + +#include +#include +#include + +/** + * Power off the computer using APM + * + * @ret rc Return status code + */ +static int apm_poweroff ( void ) { + uint16_t apm_version; + uint16_t apm_signature; + uint16_t apm_flags; + uint16_t carry; + + /* APM check */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=a" ( apm_version ), "=b" ( apm_signature ), + "=c" ( apm_flags ), "=d" ( carry ) + : "a" ( 0x5300 ), "b" ( 0x0000 ), + "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM not present\n" ); + return -ENOTSUP; + } + if ( apm_signature != 0x504d ) { /* signature 'PM' */ + DBG ( "APM not present\n" ); + return -ENOTSUP; + } + if ( apm_version < 0x0101 ) { /* Need version 1.1+ */ + DBG ( "APM 1.1+ not supported\n" ); + return -ENOTSUP; + } + if ( ( apm_flags & 0x8 ) == 0x8 ) { + DBG ( "APM power management disabled\n" ); + return -EPERM; + } + DBG2 ( "APM check completed\n" ); + + /* APM initialisation */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x5301 ), "b" ( 0x0000 ), + "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM initialisation failed\n" ); + return -EIO; + } + DBG2 ( "APM initialisation completed\n" ); + + /* Set APM driver version */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x530e ), "b" ( 0x0000 ), + "c" ( 0x0101 ), "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM setting driver version failed\n" ); + return -EIO; + } + DBG2 ( "APM driver version set\n" ); + + /* Setting power state to off */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x5307 ), "b" ( 0x0001 ), + "c" ( 0x0003 ), "d" ( 0x0000) ); + if ( carry ) { + DBG ( "APM setting power state failed\n" ); + return -ENOTTY; + } + + /* Should never happen */ + return -ECANCELED; +} + +PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff ); diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 5f676c87..bfd22385 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_vmware ( ERRFILE_ARCH | ERRFILE_CORE | 0x00080000 ) #define ERRFILE_guestrpc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00090000 ) #define ERRFILE_guestinfo ( ERRFILE_ARCH | ERRFILE_CORE | 0x000a0000 ) +#define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/config/config.c b/src/config/config.c index 1de3db43..45dc29bc 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -242,6 +242,9 @@ REQUIRE_OBJECT ( lotest_cmd ); #ifdef VLAN_CMD REQUIRE_OBJECT ( vlan_cmd ); #endif +#ifdef POWEROFF_CMD +REQUIRE_OBJECT ( poweroff_cmd ); +#endif #ifdef REBOOT_CMD REQUIRE_OBJECT ( reboot_cmd ); #endif diff --git a/src/config/general.h b/src/config/general.h index 9f0bb521..62edd3fe 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -126,6 +126,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define VLAN_CMD /* VLAN commands */ //#define PXE_CMD /* PXE commands */ //#define REBOOT_CMD /* Reboot command */ +//#define POWEROFF_CMD /* Power off command */ //#define IMAGE_TRUST_CMD /* Image trust management commands */ /* diff --git a/src/core/null_reboot.c b/src/core/null_reboot.c index 8e3ed0bb..a3d5b2ef 100644 --- a/src/core/null_reboot.c +++ b/src/core/null_reboot.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #include +#include #include /** @@ -40,4 +41,15 @@ static void null_reboot ( int warm __unused ) { while ( 1 ) {} } +/** + * Power off system + * + * @ret rc Return status code + */ +static int null_poweroff ( void ) { + + return -ENOTSUP; +} + PROVIDE_REBOOT ( null, reboot, null_reboot ); +PROVIDE_REBOOT ( null, poweroff, null_poweroff ); diff --git a/src/hci/commands/poweroff_cmd.c b/src/hci/commands/poweroff_cmd.c new file mode 100644 index 00000000..159fe618 --- /dev/null +++ b/src/hci/commands/poweroff_cmd.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Power off command + * + */ + +/** "poweroff" options */ +struct poweroff_options {}; + +/** "poweroff" option list */ +static struct option_descriptor poweroff_opts[] = {}; + +/** "poweroff" command descriptor */ +static struct command_descriptor poweroff_cmd = + COMMAND_DESC ( struct poweroff_options, poweroff_opts, 0, 0, "" ); + +/** + * The "poweroff" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int poweroff_exec ( int argc, char **argv ) { + struct poweroff_options opts; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &poweroff_cmd, &opts ) ) != 0 ) + return rc; + + /* Power off system */ + rc = poweroff(); + if ( rc != 0 ) + printf ( "Could not power off: %s\n", strerror ( rc ) ); + + return rc; +} + +/** "poweroff" command */ +struct command poweroff_command __command = { + .name = "poweroff", + .exec = poweroff_exec, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index c6b0e794..aad3f358 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_test ( ERRFILE_CORE | 0x00170000 ) #define ERRFILE_xferbuf ( ERRFILE_CORE | 0x00180000 ) #define ERRFILE_pending ( ERRFILE_CORE | 0x00190000 ) +#define ERRFILE_null_reboot ( ERRFILE_CORE | 0x001a0000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) @@ -277,6 +278,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_efi_umalloc ( ERRFILE_OTHER | 0x003b0000 ) #define ERRFILE_linux_pci ( ERRFILE_OTHER | 0x003c0000 ) #define ERRFILE_pci_settings ( ERRFILE_OTHER | 0x003d0000 ) +#define ERRFILE_efi_reboot ( ERRFILE_OTHER | 0x003e0000 ) /** @} */ diff --git a/src/include/ipxe/reboot.h b/src/include/ipxe/reboot.h index 5d882d3d..97e0d5fb 100644 --- a/src/include/ipxe/reboot.h +++ b/src/include/ipxe/reboot.h @@ -55,4 +55,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ void reboot ( int warm ); +/** + * Power off system + * + * @ret rc Return status code + * + * This function may fail, since not all systems support being powered + * off by software. + */ +int poweroff ( void ); + #endif /* _IPXE_REBOOT_H */ diff --git a/src/interface/efi/efi_reboot.c b/src/interface/efi/efi_reboot.c index bfee36aa..96638c48 100644 --- a/src/interface/efi/efi_reboot.c +++ b/src/interface/efi/efi_reboot.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +#include #include #include @@ -41,4 +42,20 @@ static void efi_reboot ( int warm ) { rs->ResetSystem ( ( warm ? EfiResetWarm : EfiResetCold ), 0, 0, NULL ); } +/** + * Power off system + * + * @ret rc Return status code + */ +static int efi_poweroff ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + + /* Use runtime services to power off system */ + rs->ResetSystem ( EfiResetShutdown, 0, 0, NULL ); + + /* Should never happen */ + return -ECANCELED; +} + PROVIDE_REBOOT ( efi, reboot, efi_reboot ); +PROVIDE_REBOOT ( efi, poweroff, efi_poweroff ); From 51d14424bfce4f36c3361cfad5f06a0c7d5ef3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Smidsr=C3=B8d?= Date: Mon, 15 Jul 2013 14:21:04 +0200 Subject: [PATCH 30/65] [build] Include ipxe.pxe in default build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c6f090d9..792fad99 100644 --- a/src/Makefile +++ b/src/Makefile @@ -108,7 +108,7 @@ INCDIRS += include . # helpfully suggestive message # ALL := bin/blib.a bin/ipxe.dsk bin/ipxe.lkrn bin/ipxe.iso \ - bin/ipxe.usb bin/undionly.kpxe bin/rtl8139.rom + bin/ipxe.usb bin/ipxe.pxe bin/undionly.kpxe bin/rtl8139.rom all : $(ALL) @$(ECHO) '===========================================================' @$(ECHO) From ed28c8304c1653a340088137966fff56bae15ad0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 17:30:39 +0200 Subject: [PATCH 31/65] [ifmgmt] Avoid relying on global variable within ifcommon_exec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The getopt API defines optind as a global variable. When used by the "autoboot" command, the payload function passed to ifcommon_exec() may result in a new iPXE script being executed; the commands therein would then overwrite the value of optind. On returning, ifcommon_exec() would continue processing the list of interfaces from an undefined point. Fix by using a local variable to hold the index within the list of interfaces. Reported-by: Robin Smidsrød Signed-off-by: Michael Brown --- src/hci/commands/ifmgmt_cmd.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/hci/commands/ifmgmt_cmd.c b/src/hci/commands/ifmgmt_cmd.c index 3f3f6b51..350f14d0 100644 --- a/src/hci/commands/ifmgmt_cmd.c +++ b/src/hci/commands/ifmgmt_cmd.c @@ -53,6 +53,7 @@ int ifcommon_exec ( int argc, char **argv, int stop_on_first_success ) { struct ifcommon_options opts; struct net_device *netdev; + int i; int rc; /* Parse options */ @@ -61,11 +62,9 @@ int ifcommon_exec ( int argc, char **argv, if ( optind != argc ) { /* Treat arguments as a list of interfaces to try */ - while ( optind != argc ) { - if ( ( rc = parse_netdev ( argv[optind++], - &netdev ) ) != 0 ) { + for ( i = optind ; i < argc ; i++ ) { + if ( ( rc = parse_netdev ( argv[i], &netdev ) ) != 0 ) continue; - } if ( ( ( rc = payload ( netdev ) ) == 0 ) && stop_on_first_success ) { return 0; From 30de9e8300e97c4b56e29f5a3b08e4615b30606f Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Mon, 15 Jul 2013 17:56:29 +0200 Subject: [PATCH 32/65] [nfs] Add support for NFS protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Robin Smidsrød Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/Makefile | 2 +- src/config/config.c | 3 + src/config/general.h | 1 + src/include/big_bswap.h | 2 + src/include/ipxe/dhcp.h | 12 + src/include/ipxe/errfile.h | 6 + src/include/ipxe/features.h | 1 + src/include/ipxe/mount.h | 76 ++++ src/include/ipxe/nfs.h | 157 ++++++++ src/include/ipxe/nfs_open.h | 12 + src/include/ipxe/oncrpc.h | 128 ++++++ src/include/ipxe/oncrpc_iob.h | 102 +++++ src/include/ipxe/portmap.h | 63 +++ src/include/ipxe/uri.h | 18 +- src/include/little_bswap.h | 2 + src/net/oncrpc/mount.c | 119 ++++++ src/net/oncrpc/nfs.c | 288 ++++++++++++++ src/net/oncrpc/nfs_open.c | 711 ++++++++++++++++++++++++++++++++++ src/net/oncrpc/oncrpc_iob.c | 200 ++++++++++ src/net/oncrpc/portmap.c | 90 +++++ src/net/tcp/oncrpc.c | 250 ++++++++++++ 21 files changed, 2238 insertions(+), 5 deletions(-) create mode 100644 src/include/ipxe/mount.h create mode 100644 src/include/ipxe/nfs.h create mode 100644 src/include/ipxe/nfs_open.h create mode 100644 src/include/ipxe/oncrpc.h create mode 100644 src/include/ipxe/oncrpc_iob.h create mode 100644 src/include/ipxe/portmap.h create mode 100644 src/net/oncrpc/mount.c create mode 100644 src/net/oncrpc/nfs.c create mode 100644 src/net/oncrpc/nfs_open.c create mode 100644 src/net/oncrpc/oncrpc_iob.c create mode 100644 src/net/oncrpc/portmap.c create mode 100644 src/net/tcp/oncrpc.c diff --git a/src/Makefile b/src/Makefile index 792fad99..acd45fcf 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,7 +61,7 @@ ZLIB_DIR := /usr SRCDIRS := SRCDIRS += libgcc SRCDIRS += core -SRCDIRS += net net/tcp net/udp net/infiniband net/80211 +SRCDIRS += net net/oncrpc net/tcp net/udp net/infiniband net/80211 SRCDIRS += image SRCDIRS += drivers/bus SRCDIRS += drivers/net diff --git a/src/config/config.c b/src/config/config.c index 45dc29bc..b17b335b 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -129,6 +129,9 @@ REQUIRE_OBJECT ( https ); #ifdef DOWNLOAD_PROTO_FTP REQUIRE_OBJECT ( ftp ); #endif +#ifdef DOWNLOAD_PROTO_NFS +REQUIRE_OBJECT ( nfs_open ); +#endif #ifdef DOWNLOAD_PROTO_SLAM REQUIRE_OBJECT ( slam ); #endif diff --git a/src/config/general.h b/src/config/general.h index 62edd3fe..3fd7408d 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -59,6 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ +#undef DOWNLOAD_PROTO_NFS /* Network File System Protocol */ /* * SAN boot protocols diff --git a/src/include/big_bswap.h b/src/include/big_bswap.h index 3775fac1..6c375a57 100644 --- a/src/include/big_bswap.h +++ b/src/include/big_bswap.h @@ -1,6 +1,8 @@ #ifndef ETHERBOOT_BIG_BSWAP_H #define ETHERBOOT_BIG_BSWAP_H +#define htonll(x) (x) +#define ntohll(x) (x) #define ntohl(x) (x) #define htonl(x) (x) #define ntohs(x) (x) diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index b97dfe32..6c028466 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -450,6 +450,18 @@ struct dhcp_netdev_desc { */ #define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 ) +/** User ID + * + * This will be used as the user id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_UID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc2 ) + +/** Group ID + * + * This will be used as the group id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_GID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc3 ) + /** iPXE version number */ #define DHCP_EB_VERSION DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xeb ) diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index aad3f358..0fd3faca 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -208,6 +208,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 ) #define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 ) #define ERRFILE_vlan ( ERRFILE_NET | 0x00300000 ) +#define ERRFILE_oncrpc ( ERRFILE_NET | 0x00310000 ) +#define ERRFILE_portmap ( ERRFILE_NET | 0x00320000 ) +#define ERRFILE_nfs ( ERRFILE_NET | 0x00330000 ) +#define ERRFILE_nfs_open ( ERRFILE_NET | 0x00340000 ) +#define ERRFILE_mount ( ERRFILE_NET | 0x00350000 ) +#define ERRFILE_oncrpc_iob ( ERRFILE_NET | 0x00360000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/features.h b/src/include/ipxe/features.h index 0c92f5be..d8b8b218 100644 --- a/src/include/ipxe/features.h +++ b/src/include/ipxe/features.h @@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DHCP_EB_FEATURE_VLAN 0x26 /**< VLAN support */ #define DHCP_EB_FEATURE_MENU 0x27 /**< Menu support */ #define DHCP_EB_FEATURE_SDI 0x28 /**< SDI image support */ +#define DHCP_EB_FEATURE_NFS 0x29 /**< NFS protocol */ /** @} */ diff --git a/src/include/ipxe/mount.h b/src/include/ipxe/mount.h new file mode 100644 index 00000000..ca958117 --- /dev/null +++ b/src/include/ipxe/mount.h @@ -0,0 +1,76 @@ +#ifndef _IPXE_MOUNT_H +#define _IPXE_MOUNT_H + +#include + +/** @file + * + * NFS MOUNT protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS MOUNT protocol number */ +#define ONCRPC_MOUNT 100005 +/** NFS MOUNT protocol version */ +#define MOUNT_VERS 3 + + +/** No error */ +#define MNT3_OK 0 +/** Not owner */ +#define MNT3ERR_PERM 1 +/** No such file or directory */ +#define MNT3ERR_NOENT 2 +/** I/O error */ +#define MNT3ERR_IO 5 +/** Permission denied */ +#define MNT3ERR_ACCES 13 +/** Not a directory */ +#define MNT3ERR_NOTDIR 20 +/** Invalid argument */ +#define MNT3ERR_INVAL 22 +/** Filename too long */ +#define MNT3ERR_NAMETOOLONG 63 +/** Operation not supported */ +#define MNT3ERR_NOTSUPP 10004 +/** A failure on the server */ +#define MNT3ERR_SERVERFAULT 10006 + + +/** + * A MOUNT MNT reply + * + */ +struct mount_mnt_reply { + /** Reply status */ + uint32_t status; + /** Root file handle */ + struct nfs_fh fh; +}; + +/** + * Prepare an ONC RPC session to be used as a MOUNT session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void mount_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_MOUNT, MOUNT_VERS ); +} + +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); + +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_MOUNT_H */ diff --git a/src/include/ipxe/nfs.h b/src/include/ipxe/nfs.h new file mode 100644 index 00000000..498ed5a2 --- /dev/null +++ b/src/include/ipxe/nfs.h @@ -0,0 +1,157 @@ +#ifndef _IPXE_NFS_H +#define _IPXE_NFS_H + +#include +#include + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS protocol number */ +#define ONCRPC_NFS 100003 + +/** NFS protocol version */ +#define NFS_VERS 3 + +/** No error*/ +#define NFS3_OK 0 +/** Not owner */ +#define NFS3ERR_PERM 1 +/** No such file or directory */ +#define NFS3ERR_NOENT 2 +/** I/O error */ +#define NFS3ERR_IO 5 +/** No such device or address */ +#define NFS3ERR_NXIO 6 +/** Permission denied */ +#define NFS3ERR_ACCES 13 +/** The file specified already exists */ +#define NFS3ERR_EXIST 17 +/** Attempt to do a cross-device hard link */ +#define NFS3ERR_XDEV 18 +/** No such device */ +#define NFS3ERR_NODEV 19 +/** Not a directory */ +#define NFS3ERR_NOTDIR 20 + /**Is a directory */ +#define NFS3ERR_ISDIR 21 +/** Invalid argument */ +#define NFS3ERR_INVAL 22 +/** Filename too long */ +#define NFS3ERR_NAMETOOLONG 63 +/** Invalid file handle */ +#define NFS3ERR_STALE 70 +/** Too many levels of remote in path */ +#define NFS3ERR_REMOTE 71 +/** Illegal NFS file handle */ +#define NFS3ERR_BADHANDLE 10001 +/** READDIR or READDIRPLUS cookie is stale */ +#define NFS3ERR_BAD_COOKIE 10003 +/** Operation not supported */ +#define NFS3ERR_NOTSUPP 10004 +/** Buffer or request is too small */ +#define NFS3ERR_TOOSMALL 10005 +/** An error occurred on the server which does not map to any of the legal NFS + * version 3 protocol error values */ +#define NFS3ERR_SERVERFAULT 10006 +/** The server initiated the request, but was not able to complete it in a + * timely fashion */ +#define NFS3ERR_JUKEBOX 10008 + +enum nfs_attr_type { + NFS_ATTR_SYMLINK = 5, +}; + +/** + * A NFS file handle + * + */ +struct nfs_fh { + uint8_t fh[64]; + size_t size; +}; + +/** + * A NFS LOOKUP reply + * + */ +struct nfs_lookup_reply { + /** Reply status */ + uint32_t status; + /** Entity type */ + enum nfs_attr_type ent_type; + /** File handle */ + struct nfs_fh fh; +}; + +/** + * A NFS READLINK reply + * + */ +struct nfs_readlink_reply { + /** Reply status */ + uint32_t status; + /** File path length */ + uint32_t path_len; + /** File path */ + char *path; +}; + + +/** + * A NFS READ reply + * + */ +struct nfs_read_reply { + /** Reply status */ + uint32_t status; + /** File size */ + uint64_t filesize; + /** Bytes read */ + uint32_t count; + /** End-of-File indicator */ + uint32_t eof; + /** Data length */ + uint32_t data_len; + /** Data read */ + void *data; +}; + +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ); +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ); + +/** + * Prepare an ONC RPC session to be used as a NFS session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void nfs_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_NFS, NFS_VERS ); +} + +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ); +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ); +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ); + +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ); +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ); +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_NFS_H */ diff --git a/src/include/ipxe/nfs_open.h b/src/include/ipxe/nfs_open.h new file mode 100644 index 00000000..caba977f --- /dev/null +++ b/src/include/ipxe/nfs_open.h @@ -0,0 +1,12 @@ +#ifndef _IPXE_NFS_OPEN_H +#define _IPXE_NFS_OPEN_H + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#endif /* _IPXE_NFS_OPEN_H */ diff --git a/src/include/ipxe/oncrpc.h b/src/include/ipxe/oncrpc.h new file mode 100644 index 00000000..76c1260f --- /dev/null +++ b/src/include/ipxe/oncrpc.h @@ -0,0 +1,128 @@ +#ifndef _IPXE_ONCRPC_H +#define _IPXE_ONCRPC_H + +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** ONC RCP Version */ +#define ONCRPC_VERS 2 + +/** ONC RPC Null Authentication */ +#define ONCRPC_AUTH_NONE 0 + +/** ONC RPC System Authentication (also called UNIX Authentication) */ +#define ONCRPC_AUTH_SYS 1 + +/** Size of an ONC RPC header */ +#define ONCRPC_HEADER_SIZE ( 11 * sizeof ( uint32_t ) ) + +#define ONCRPC_FIELD( type, value ) { oncrpc_ ## type, { .type = value } } +#define ONCRPC_SUBFIELD( type, args... ) \ + { oncrpc_ ## type, { .type = { args } } } + +#define ONCRPC_FIELD_END { oncrpc_none, { } } + +/** Enusure that size is a multiple of four */ +#define oncrpc_align( size ) ( ( (size) + 3 ) & ~3 ) + +/** + * Calculate the length of a string, including padding bytes. + * + * @v str String + * @ret size Length of the padded string + */ +#define oncrpc_strlen( str ) ( oncrpc_align ( strlen ( str ) ) + \ + sizeof ( uint32_t ) ) + +struct oncrpc_cred { + uint32_t flavor; + uint32_t length; +}; + +struct oncrpc_cred_sys { + struct oncrpc_cred credential; + uint32_t stamp; + char *hostname; + uint32_t uid; + uint32_t gid; + uint32_t aux_gid_len; + uint32_t aux_gid[16]; +}; + +struct oncrpc_reply +{ + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t reply_state; + uint32_t accept_state; + uint32_t frame_size; + struct io_buffer *data; +}; + +struct oncrpc_session { + struct oncrpc_reply pending_reply; + struct oncrpc_cred *credential; + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t prog_name; + uint32_t prog_vers; +}; + +enum oncrpc_field_type { + oncrpc_none = 0, + oncrpc_int32, + oncrpc_int64, + oncrpc_str, + oncrpc_array, + oncrpc_intarray, + oncrpc_cred, +}; + +union oncrpc_field_value { + struct { + size_t length; + const void *ptr; + } array; + + struct { + size_t length; + const uint32_t *ptr; + } intarray; + + int64_t int64; + int32_t int32; + const char *str; + const struct oncrpc_cred *cred; +}; + +struct oncrpc_field { + enum oncrpc_field_type type; + union oncrpc_field_value value; +}; + +extern struct oncrpc_cred oncrpc_auth_none; + +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ); +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ); + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ); + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ); + +int oncrpc_get_reply ( struct oncrpc_session *session, + struct oncrpc_reply *reply, struct io_buffer *io_buf ); + +#endif /* _IPXE_ONCRPC_H */ diff --git a/src/include/ipxe/oncrpc_iob.h b/src/include/ipxe/oncrpc_iob.h new file mode 100644 index 00000000..4858d96b --- /dev/null +++ b/src/include/ipxe/oncrpc_iob.h @@ -0,0 +1,102 @@ +#ifndef _IPXE_ONCRPC_IOB_H +#define _IPXE_ONCRPC_IOB_H + +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Add a string to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + */ +#define oncrpc_iob_add_string( buf, str ) \ +( { \ + const char * _str = (str); \ + oncrpc_iob_add_array ( (buf), strlen ( _str ), _str ); \ +} ) + +/** + * Get a 32 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ + +#define oncrpc_iob_get_int( buf ) \ +( { \ + uint32_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint32_t ) ); \ + ntohl ( *_val ); \ +} ) + +/** + * Get a 64 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ +#define oncrpc_iob_get_int64( buf ) \ +( { \ + uint64_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint64_t ) ); \ + ntohll ( *_val ); \ +} ) + + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ); + +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ); + +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ); + +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ); + +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ); + +/** + * Add a 32 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int ( struct io_buffer *io_buf, + uint32_t val ) { + * ( uint32_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonl ( val ); + return ( sizeof ( val) ); +} + +/** + * Add a 64 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int64 ( struct io_buffer *io_buf, + uint64_t val ) { + * ( uint64_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonll ( val ); + return ( sizeof ( val) ); +} + +#endif /* _IPXE_ONCRPC_IOB_H */ diff --git a/src/include/ipxe/portmap.h b/src/include/ipxe/portmap.h new file mode 100644 index 00000000..9b735bbc --- /dev/null +++ b/src/include/ipxe/portmap.h @@ -0,0 +1,63 @@ +#ifndef _IPXE_PORTMAP_H +#define _IPXE_PORTMAP_H + +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** PORTMAP default port */ +#define PORTMAP_PORT 111 + +/** PORTMAP protocol number */ +#define ONCRPC_PORTMAP 100000 + +/** PORTMAP version */ +#define PORTMAP_VERS 2 + + +/** TCP protocol number */ +#define PORTMAP_PROTO_TCP 6 +/** UDB protocol number */ +#define PORTMAP_PROTO_UDP 17 + + +/** + * A PORTMAP GETPORT reply + * + */ +struct portmap_getport_reply { + /** Port returned */ + uint32_t port; +}; + + +/** + * Prepare an ONC RPC session to be used as a PORTMAP session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void portmap_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_PORTMAP, PORTMAP_VERS ); +} + + +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ); +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ); + + +#endif /* _IPXE_PORTMAP_H */ diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index b7b8b441..9a134690 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -109,17 +109,27 @@ enum { * Note that this is a separate concept from a URI with an absolute * path. */ -static inline int uri_is_absolute ( struct uri *uri ) { +static inline int uri_is_absolute ( const struct uri *uri ) { return ( uri->scheme != NULL ); } +/** + * URI has an opaque part + * + * @v uri URI + * @ret has_opaque URI has an opaque part + */ +static inline int uri_has_opaque ( const struct uri *uri ) { + return ( uri->opaque && ( uri->opaque[0] != '\0' ) ); + +} /** * URI has a path * * @v uri URI * @ret has_path URI has a path */ -static inline int uri_has_path ( struct uri *uri ) { +static inline int uri_has_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '\0' ) ); } @@ -133,7 +143,7 @@ static inline int uri_has_path ( struct uri *uri ) { * concept from an absolute URI. Note also that a URI may not have a * path at all. */ -static inline int uri_has_absolute_path ( struct uri *uri ) { +static inline int uri_has_absolute_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] == '/' ) ); } @@ -147,7 +157,7 @@ static inline int uri_has_absolute_path ( struct uri *uri ) { * this is a separate concept from a relative URI. Note also that a * URI may not have a path at all. */ -static inline int uri_has_relative_path ( struct uri *uri ) { +static inline int uri_has_relative_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '/' ) ); } diff --git a/src/include/little_bswap.h b/src/include/little_bswap.h index a5dc9c87..92dd26ba 100644 --- a/src/include/little_bswap.h +++ b/src/include/little_bswap.h @@ -3,6 +3,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#define htonll(x) __bswap_64(x) +#define ntohll(x) __bswap_64(x) #define ntohl(x) __bswap_32(x) #define htonl(x) __bswap_32(x) #define ntohs(x) __bswap_16(x) diff --git a/src/net/oncrpc/mount.c b/src/net/oncrpc/mount.c new file mode 100644 index 00000000..8838a147 --- /dev/null +++ b/src/net/oncrpc/mount.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * NFS MOUNT protocol + * + */ + +/** MNT procedure number */ +#define MOUNT_MNT 1 +/** UMNT procedure number */ +#define MOUNT_UMNT 3 + +/** + * Send a MNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to mount. + * @ret rc Return status code + */ +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_MNT, fields ); +} + +/** + * Send a UMNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to unmount. + * @ret rc Return status code + */ +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_UMNT, fields ); +} + +/** + * Parse an MNT reply + * + * @v mnt_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ) { + if ( ! mnt_reply || ! reply ) + return -EINVAL; + + mnt_reply->status = oncrpc_iob_get_int ( reply->data ); + + switch ( mnt_reply->status ) + { + case MNT3_OK: + break; + case MNT3ERR_NOENT: + return -ENOENT; + case MNT3ERR_IO: + return -EIO; + case MNT3ERR_ACCES: + return -EACCES; + case MNT3ERR_NOTDIR: + return -ENOTDIR; + case MNT3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &mnt_reply->fh ); + + return 0; +} diff --git a/src/net/oncrpc/nfs.c b/src/net/oncrpc/nfs.c new file mode 100644 index 00000000..b6118f91 --- /dev/null +++ b/src/net/oncrpc/nfs.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol + * + */ + +/** NFS LOOKUP procedure */ +#define NFS_LOOKUP 3 +/** NFS READLINK procedure */ +#define NFS_READLINK 5 +/** NFS READ procedure */ +#define NFS_READ 6 + +/** + * Extract a file handle from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data read + */ +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ) { + fh->size = oncrpc_iob_get_int ( io_buf ); + + if ( fh->size > 64 ) + return sizeof ( uint32_t ); + + memcpy (fh->fh, io_buf->data, fh->size ); + iob_pull ( io_buf, fh->size ); + + return fh->size + sizeof ( uint32_t ); +} + +/** + * Add a file handle to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data written + */ +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ) { + size_t s; + + s = oncrpc_iob_add_int ( io_buf, fh->size ); + memcpy ( iob_put ( io_buf, fh->size ), &fh->fh, fh->size ); + + return s + fh->size; +} + +/** + * Send a LOOKUP request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle of the the directory + * @v filename The file name + * @ret rc Return status code + */ +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( str, filename ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_LOOKUP, fields ); +} + +/** + * Send a READLINK request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The symlink file handle + * @ret rc Return status code + */ +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READLINK, fields ); +} + +/** + * Send a READ request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle + * @v offset Offset + * @v count Byte count + * @ret rc Return status code + */ +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( int64, offset ), + ONCRPC_FIELD ( int32, count ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READ, fields ); +} + +/** + * Parse a LOOKUP reply + * + * @v lookup_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ) { + if ( ! lookup_reply || ! reply ) + return -EINVAL; + + lookup_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( lookup_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_NOTDIR: + return -ENOTDIR; + case NFS3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &lookup_reply->fh ); + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + lookup_reply->ent_type = oncrpc_iob_get_int ( reply->data ); + + return 0; +} +/** + * Parse a READLINK reply + * + * @v readlink_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ) { + if ( ! readlink_reply || ! reply ) + return -EINVAL; + + readlink_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( readlink_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_NOTSUPP: + return -ENOTSUP; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) + + 8 * sizeof ( uint64_t ) ); + + readlink_reply->path_len = oncrpc_iob_get_int ( reply->data ); + readlink_reply->path = reply->data->data; + + return 0; +} + +/** + * Parse a READ reply + * + * @v read_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ) { + if ( ! read_reply || ! reply ) + return -EINVAL; + + read_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( read_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_NXIO: + return -ENXIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + { + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) ); + read_reply->filesize = oncrpc_iob_get_int64 ( reply->data ); + iob_pull ( reply->data, 7 * sizeof ( uint64_t ) ); + } + + read_reply->count = oncrpc_iob_get_int ( reply->data ); + read_reply->eof = oncrpc_iob_get_int ( reply->data ); + read_reply->data_len = oncrpc_iob_get_int ( reply->data ); + read_reply->data = reply->data->data; + + if ( read_reply->count != read_reply->data_len ) + return -EPROTO; + + return 0; +} + diff --git a/src/net/oncrpc/nfs_open.c b/src/net/oncrpc/nfs_open.c new file mode 100644 index 00000000..ff2b7404 --- /dev/null +++ b/src/net/oncrpc/nfs_open.c @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 ); + +#define NFS_RSIZE 100000 + +enum nfs_pm_state { + NFS_PORTMAP_NONE = 0, + NFS_PORTMAP_MOUNTPORT, + NFS_PORTMAP_NFSPORT, + MFS_PORTMAP_CLOSED, +}; + +enum nfs_mount_state { + NFS_MOUNT_NONE = 0, + NFS_MOUNT_MNT, + NFS_MOUNT_UMNT, + NFS_MOUNT_CLOSED, +}; + +enum nfs_state { + NFS_NONE = 0, + NFS_LOOKUP, + NFS_LOOKUP_SENT, + NFS_READLINK, + NFS_READLINK_SENT, + NFS_READ, + NFS_READ_SENT, + NFS_CLOSED, +}; + +/** + * A NFS request + * + */ +struct nfs_request { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + + struct interface pm_intf; + struct interface mount_intf; + struct interface nfs_intf; + + enum nfs_pm_state pm_state; + enum nfs_mount_state mount_state; + enum nfs_state nfs_state; + + struct oncrpc_session pm_session; + struct oncrpc_session mount_session; + struct oncrpc_session nfs_session; + + struct oncrpc_cred_sys auth_sys; + + char * hostname; + char * path; + char * mountpoint; + char * filename; + size_t filename_offset; + + struct nfs_fh readlink_fh; + struct nfs_fh current_fh; + uint64_t file_offset; + + size_t remaining; + int eof; +}; + +static void nfs_step ( struct nfs_request *nfs ); + +/** + * Free NFS request + * + * @v refcnt Reference counter + */ +static void nfs_free ( struct refcnt *refcnt ) { + struct nfs_request *nfs; + + nfs = container_of ( refcnt, struct nfs_request, refcnt ); + DBGC ( nfs, "NFS_OPEN %p freed\n", nfs ); + + free ( nfs->hostname ); + free ( nfs->path ); + free ( nfs->auth_sys.hostname ); + free ( nfs ); +} + +/** + * Mark NFS operation as complete + * + * @v nfs NFS request + * @v rc Return status code + */ +static void nfs_done ( struct nfs_request *nfs, int rc ) { + if ( rc == 0 && nfs->nfs_state != NFS_CLOSED ) + rc = -ECONNRESET; + + DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) ); + + intf_shutdown ( &nfs->xfer, rc ); + intf_shutdown ( &nfs->pm_intf, rc ); + intf_shutdown ( &nfs->mount_intf, rc ); + intf_shutdown ( &nfs->nfs_intf, rc ); +} + +static int nfs_connect ( struct interface *intf, uint16_t port, + const char *hostname ) { + struct sockaddr_tcpip peer; + struct sockaddr_tcpip local; + + if ( ! intf || ! hostname || ! port ) + return -EINVAL; + + memset ( &peer, 0, sizeof ( peer ) ); + memset ( &peer, 0, sizeof ( local ) ); + peer.st_port = htons ( port ); + + /* Use a local port < 1024 to avoid using the 'insecure' option in + * /etc/exports file. */ + local.st_port = htons ( 1 + ( rand() % 1023 ) ); + + return xfer_open_named_socket ( intf, SOCK_STREAM, + ( struct sockaddr * ) &peer, hostname, + ( struct sockaddr * ) &local ); +} + +static void nfs_pm_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->pm_intf ) ) + return; + + if ( nfs->pm_state == NFS_PORTMAP_NONE ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_MOUNT, MOUNT_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + return; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_NFS, NFS_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_pm_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + struct portmap_getport_reply getport_reply; + + oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->mount_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + nfs_pm_step ( nfs ); + + goto done; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + intf_shutdown ( &nfs->pm_intf, 0 ); + nfs->pm_state++; + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_mount_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->mount_intf ) ) + return; + + if ( nfs->mount_state == NFS_MOUNT_NONE ) { + DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs, + nfs->mountpoint ); + + rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, + nfs->mountpoint ); + if ( rc != 0 ) + goto err; + + nfs->mount_state++; + return; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs ); + + rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session, + nfs->mountpoint ); + if ( rc != 0 ) + goto err; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_mount_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + char *sep; + struct oncrpc_reply reply; + struct mount_mnt_reply mnt_reply; + + oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->mount_state == NFS_MOUNT_MNT ) { + DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs ); + rc = mount_get_mnt_reply ( &mnt_reply, &reply ); + if ( rc != 0 ) { + if ( mnt_reply.status != MNT3ERR_NOTDIR ) + goto err; + + if ( strcmp ( nfs->mountpoint, "/" ) == 0 ) + goto err; + + sep = strrchr ( nfs->mountpoint, '/' ); + nfs->filename[-1] = '/'; + nfs->filename = sep + 1; + *sep = '\0'; + + DBGC ( nfs, "NFS_OPEN %p ENOTDIR received retrying" \ + "with %s\n", nfs, nfs->mountpoint ); + goto done; + } + + nfs->current_fh = mnt_reply.fh; + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + + goto done; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs ); + nfs_done ( nfs, 0 ); + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_step ( struct nfs_request *nfs ) { + int rc; + char *path_component = NULL; + + if ( ! xfer_window ( &nfs->nfs_intf ) ) + return; + + if ( nfs->nfs_state == NFS_LOOKUP ) { + while ( path_component == NULL || path_component[0] == '\0') { + path_component = nfs->filename; + while ( *nfs->filename != '\0' ) { + nfs->filename_offset++; + if ( *nfs->filename++ == '/' ) { + *(nfs->filename - 1) = '\0'; + break; + } + } + } + + DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs, + path_component ); + + rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, path_component ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + + if ( nfs->nfs_state == NFS_READLINK ) { + DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs ); + + rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->readlink_fh ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + if ( nfs->nfs_state == NFS_READ ) { + DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs ); + + rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, nfs->file_offset, + NFS_RSIZE ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + + if ( nfs->remaining == 0 ) { + oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) { + rc = -EPROTO; + goto err; + } + } + + if ( nfs->nfs_state == NFS_LOOKUP_SENT ) { + struct nfs_lookup_reply lookup_reply; + + DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs ); + + rc = nfs_get_lookup_reply ( &lookup_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) { + nfs->readlink_fh = lookup_reply.fh; + nfs->nfs_state = NFS_READLINK; + } else { + nfs->current_fh = lookup_reply.fh; + + if ( nfs->filename[0] == '\0' ) + nfs->nfs_state = NFS_READ; + else + nfs->nfs_state--; + } + + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READLINK_SENT ) { + char *new_filename; + struct nfs_readlink_reply readlink_reply; + + DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs ); + + rc = nfs_get_readlink_reply ( &readlink_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( readlink_reply.path_len == 0 ) + return -EINVAL; + + if ( readlink_reply.path[0] == '/' ) { + if ( strncmp ( readlink_reply.path, nfs->mountpoint, + strlen ( nfs->mountpoint ) ) != 0 ) + return -EINVAL; + + /* The mountpoint part of the path is ended by a '/' */ + if ( strlen ( nfs->mountpoint ) != + readlink_reply.path_len ) { + readlink_reply.path += 1; + readlink_reply.path_len -= 1; + } + + /* We are considering the last part of the absolute + * path as a relative path from the mountpoint. + */ + readlink_reply.path += strlen ( nfs->mountpoint ); + readlink_reply.path_len -= strlen ( nfs->mountpoint ); + } + + new_filename = malloc ( readlink_reply.path_len + + strlen ( nfs->filename ) + 2 ); + if ( ! new_filename ) { + rc = -ENOMEM; + goto err; + } + + memcpy ( new_filename, readlink_reply.path, + readlink_reply.path_len ); + strcpy ( new_filename + readlink_reply.path_len + 1, + nfs->filename ); + new_filename[readlink_reply.path_len] = '/'; + + free ( nfs->filename - nfs->filename_offset ); + nfs->filename = new_filename; + nfs->filename_offset = 0; + + DBGC ( nfs, "NFS_OPEN %p new filename: %s\n", nfs, + nfs->filename ); + + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READ_SENT ) { + if ( nfs->remaining == 0 ) { + DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs ); + + struct nfs_read_reply read_reply; + + rc = nfs_get_read_reply ( &read_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( nfs->file_offset == 0 ) { + DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n", + nfs, read_reply.filesize ); + + xfer_seek ( &nfs->xfer, read_reply.filesize ); + xfer_seek ( &nfs->xfer, 0 ); + } + + nfs->file_offset += read_reply.count; + nfs->remaining = read_reply.count; + nfs->eof = read_reply.eof; + } + + size_t len = iob_len ( io_buf ); + if ( len > nfs->remaining ) + iob_unput ( io_buf, len - nfs->remaining ); + + nfs->remaining -= iob_len ( io_buf ); + + DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs, + iob_len ( io_buf ) ); + + rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) ); + if ( rc != 0 ) + goto err; + + if ( nfs->remaining == 0 ) { + if ( ! nfs->eof ) { + nfs->nfs_state--; + nfs_step ( nfs ); + } else { + intf_shutdown ( &nfs->nfs_intf, 0 ); + nfs->nfs_state++; + nfs->mount_state++; + nfs_mount_step ( nfs ); + } + } + + return 0; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +/***************************************************************************** + * Interfaces + * + */ + +static struct interface_operation nfs_xfer_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), +}; + +/** NFS data transfer interface descriptor */ +static struct interface_descriptor nfs_xfer_desc = + INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations ); + +static struct interface_operation nfs_pm_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ), +}; + +static struct interface_descriptor nfs_pm_desc = + INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations ); + +static struct interface_operation nfs_mount_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ), +}; + +static struct interface_descriptor nfs_mount_desc = + INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations ); + +static struct interface_operation nfs_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ), +}; + +static struct interface_descriptor nfs_desc = + INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations, + xfer ); + +/***************************************************************************** + * + * URI opener + * + */ + +static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) { + int rc; + + if ( ! uri || ! uri->host || ! uri->path ) + return -EINVAL; + + if ( ! ( nfs->path = strdup ( uri->path ) ) ) + return -ENOMEM; + + if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { + rc = -ENOMEM; + goto err_hostname; + } + + nfs->filename = basename ( nfs->path ); + nfs->mountpoint = dirname ( nfs->path ); + + if ( nfs->filename[0] == '\0' ) + goto err_filename; + + DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, filename=%s)\n", + nfs, nfs->mountpoint, nfs->filename ); + + return 0; + +err_filename: + rc = -EINVAL; + free ( nfs->hostname ); +err_hostname: + free ( nfs->path ); + return rc; +} + +/** + * Initiate a NFS connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int nfs_open ( struct interface *xfer, struct uri *uri ) { + int rc; + struct nfs_request *nfs; + + nfs = zalloc ( sizeof ( *nfs ) ); + if ( ! nfs ) + return -ENOMEM; + + rc = nfs_parse_uri( nfs, uri ); + if ( rc != 0 ) + goto err_uri; + + rc = oncrpc_init_cred_sys ( &nfs->auth_sys ); + if ( rc != 0 ) + goto err_cred; + + ref_init ( &nfs->refcnt, nfs_free ); + intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt ); + intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt ); + intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt ); + intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt ); + + portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential ); + mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential ); + nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential ); + + rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname ); + if ( rc != 0 ) + goto err_connect; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &nfs->xfer, xfer ); + ref_put ( &nfs->refcnt ); + + return 0; + +err_connect: + free ( nfs->auth_sys.hostname ); +err_cred: +err_uri: + free ( nfs ); + return rc; +} + +/** NFS URI opener */ +struct uri_opener nfs_uri_opener __uri_opener = { + .scheme = "nfs", + .open = nfs_open, +}; diff --git a/src/net/oncrpc/oncrpc_iob.c b/src/net/oncrpc/oncrpc_iob.c new file mode 100644 index 00000000..be51805e --- /dev/null +++ b/src/net/oncrpc/oncrpc_iob.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol + * + */ + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ) { + size_t i; + size_t s = 0; + + struct oncrpc_field f; + + if ( ! io_buf ) + return 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + f = fields[i]; + switch ( f.type ) { + case oncrpc_int32: + s += oncrpc_iob_add_int ( io_buf, f.value.int32 ); + break; + + case oncrpc_int64: + s += oncrpc_iob_add_int64 ( io_buf, f.value.int64 ); + break; + + case oncrpc_str: + s += oncrpc_iob_add_string ( io_buf, f.value.str ); + break; + + case oncrpc_array: + s += oncrpc_iob_add_array ( io_buf, + f.value.array.length, + f.value.array.ptr ); + break; + + case oncrpc_intarray: + s += oncrpc_iob_add_intarray ( io_buf, + f.value.intarray.length, + f.value.intarray.ptr ); + break; + + case oncrpc_cred: + s += oncrpc_iob_add_cred ( io_buf, f.value.cred); + break; + + default: + return s; + } + } + + return s; +} + +/** + * Add an array of bytes to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + * + * In the ONC RPC protocol, every data is four byte paded, we add padding when + * necessary by using oncrpc_align() + */ +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ) { + size_t padding = oncrpc_align ( length ) - length; + + oncrpc_iob_add_int ( io_buf, length ); + memcpy ( iob_put ( io_buf, length ), data, length ); + memset ( iob_put ( io_buf, padding ), 0, padding ); + + return length + padding + sizeof ( uint32_t ); +} + +/** + * Add an int array to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v length Length od the array + * @v val Int array + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ) { + size_t i; + + oncrpc_iob_add_int ( io_buf, length ); + + for ( i = 0; i < length; ++i ) + oncrpc_iob_add_int ( io_buf, array[i] ); + + return ( ( length + 1 ) * sizeof ( uint32_t ) ); +} + +/** + * Add credential information to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Credential information + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ) { + struct oncrpc_cred_sys *syscred; + size_t s; + + struct oncrpc_field credfields[] = { + ONCRPC_FIELD ( int32, cred->flavor ), + ONCRPC_FIELD ( int32, cred->length ), + ONCRPC_FIELD_END, + }; + + if ( ! io_buf || ! cred ) + return 0; + + s = oncrpc_iob_add_fields ( io_buf, credfields); + + switch ( cred->flavor ) { + case ONCRPC_AUTH_NONE: + break; + + case ONCRPC_AUTH_SYS: + syscred = container_of ( cred, struct oncrpc_cred_sys, + credential ); + + struct oncrpc_field syscredfields[] = { + ONCRPC_FIELD ( int32, syscred->stamp ), + ONCRPC_FIELD ( str, syscred->hostname ), + ONCRPC_FIELD ( int32, syscred->uid ), + ONCRPC_FIELD ( int32, syscred->gid ), + ONCRPC_SUBFIELD ( intarray, syscred->aux_gid_len, + syscred->aux_gid ), + ONCRPC_FIELD_END, + }; + + s += oncrpc_iob_add_fields ( io_buf, syscredfields ); + break; + } + + return s; +} + +/** + * Get credential information from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Struct where the information will be saved + * @ret size Size of the data read + */ +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ) { + if ( cred == NULL ) + return * ( uint32_t * ) io_buf->data; + + cred->flavor = oncrpc_iob_get_int ( io_buf ); + cred->length = oncrpc_iob_get_int ( io_buf ); + + iob_pull ( io_buf, cred->length ); + + return ( 2 * sizeof ( uint32_t ) + cred->length ); +} diff --git a/src/net/oncrpc/portmap.c b/src/net/oncrpc/portmap.c new file mode 100644 index 00000000..df62221d --- /dev/null +++ b/src/net/oncrpc/portmap.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PORTMAPPER protocol. + * + */ + +/** PORTMAP GETPORT procedure. */ +#define PORTMAP_GETPORT 3 + +/** + * Send a GETPORT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v prog ONC RPC program number + * @v vers ONC RPC rogram version number + * @v proto Protocol (TCP or UDP) + * @ret rc Return status code + */ +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( int32, prog ), + ONCRPC_FIELD ( int32, vers ), + ONCRPC_FIELD ( int32, proto ), + ONCRPC_FIELD ( int32, 0 ), /* The port field is only meaningful + in GETPORT reply */ + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, PORTMAP_GETPORT, fields ); +} + +/** + * Parse a GETPORT reply + * + * @v getport_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ) { + if ( ! getport_reply || ! reply ) + return -EINVAL; + + getport_reply->port = oncrpc_iob_get_int ( reply->data ); + if ( getport_reply == 0 || getport_reply->port >= 65536 ) + return -EINVAL; + + return 0; +} diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c new file mode 100644 index 00000000..819d3179 --- /dev/null +++ b/src/net/tcp/oncrpc.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol + * + */ + +/** Set most significant bit to 1. */ +#define SET_LAST_FRAME( x ) ( (x) | 1 << 31 ) +#define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) ) + +#define ONCRPC_CALL 0 +#define ONCRPC_REPLY 1 + +/** AUTH NONE authentication flavor */ +struct oncrpc_cred oncrpc_auth_none = { + .flavor = ONCRPC_AUTH_NONE, + .length = 0 +}; + +struct setting uid_setting __setting ( SETTING_AUTH ) = { + .name = "uid", + .description = "User ID", + .tag = DHCP_EB_UID, + .type = &setting_type_uint32 +}; + +struct setting gid_setting __setting ( SETTING_AUTH ) = { + .name = "gid", + .description = "Group ID", + .tag = DHCP_EB_GID, + .type = &setting_type_uint32 +}; + +/** + * Initialize an ONC RPC AUTH SYS credential structure + * + * @v auth_sys The structure to initialize + * + * The hostname field is filled with the value of the hostname setting, if the + * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used + * instead. + */ +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) { + if ( ! auth_sys ) + return -EINVAL; + + fetch_string_setting_copy ( NULL, &hostname_setting, + &auth_sys->hostname ); + if ( ! auth_sys->hostname ) + if ( ! ( auth_sys->hostname = strdup ( PRODUCT_SHORT_NAME ) ) ) + return -ENOMEM; + + auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->gid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->aux_gid_len = 0; + auth_sys->stamp = 0; + + auth_sys->credential.flavor = ONCRPC_AUTH_SYS; + auth_sys->credential.length = 16 + + oncrpc_strlen ( auth_sys->hostname ); + + return 0; +} + +/** + * Prepare an ONC RPC session structure to be used by the ONC RPC layer + * + * @v session ONC RPC session + * @v credential Credential structure pointer + * @v verifier Verifier structure pointer + * @v prog_name ONC RPC program number + * @v prog_vers ONC RPC program version number + */ +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ) { + if ( ! session ) + return; + + session->rpc_id = rand(); + session->credential = credential; + session->verifier = verifier; + session->prog_name = prog_name; + session->prog_vers = prog_vers; +} + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ) { + int rc; + size_t frame_size; + struct io_buffer *io_buf; + + if ( ! session ) + return -EINVAL; + + struct oncrpc_field header[] = { + ONCRPC_FIELD ( int32, 0 ), + ONCRPC_FIELD ( int32, ++session->rpc_id ), + ONCRPC_FIELD ( int32, ONCRPC_CALL ), + ONCRPC_FIELD ( int32, ONCRPC_VERS ), + ONCRPC_FIELD ( int32, session->prog_name ), + ONCRPC_FIELD ( int32, session->prog_vers ), + ONCRPC_FIELD ( int32, proc_name ), + ONCRPC_FIELD ( cred, session->credential ), + ONCRPC_FIELD ( cred, session->verifier ), + ONCRPC_FIELD_END, + }; + + frame_size = oncrpc_compute_size ( header ); + frame_size += oncrpc_compute_size ( fields ); + + io_buf = alloc_iob ( frame_size ); + if ( ! io_buf ) + return -ENOBUFS; + + header[0].value.int32 = SET_LAST_FRAME ( frame_size - + sizeof ( uint32_t ) ); + + oncrpc_iob_add_fields ( io_buf, header ); + oncrpc_iob_add_fields ( io_buf, fields ); + + rc = xfer_deliver_iob ( intf, io_buf ); + if ( rc != 0 ) + free_iob ( io_buf ); + + return rc; +} + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { + + size_t i; + size_t size = 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + switch ( fields[i].type ) { + case oncrpc_int32: + size += sizeof ( uint32_t ); + break; + + case oncrpc_int64: + size += sizeof ( uint64_t ); + break; + + case oncrpc_str: + size += oncrpc_strlen ( fields[i].value.str ); + break; + + case oncrpc_array: + size += oncrpc_align ( fields[i].value.array.length ); + size += sizeof ( uint32_t ); + break; + + case oncrpc_intarray: + size += sizeof ( uint32_t ) * + fields[i].value.intarray.length; + size += sizeof ( uint32_t ); + break; + + case oncrpc_cred: + size += fields[i].value.cred->length; + size += 2 * sizeof ( uint32_t ); + break; + + default: + return size; + } + } + + return size; +} + +/** + * Parse an I/O buffer to extract a ONC RPC REPLY + * @v session ONC RPC session + * @v reply Reply structure where data will be saved + * @v io_buf I/O buffer + */ +int oncrpc_get_reply ( struct oncrpc_session *session __unused, + struct oncrpc_reply *reply, struct io_buffer *io_buf ) { + if ( ! reply || ! io_buf ) + return -EINVAL; + + reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) ); + reply->rpc_id = oncrpc_iob_get_int ( io_buf ); + + /* iPXE has no support for handling ONC RPC call */ + if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY ) + return -ENOSYS; + + reply->reply_state = oncrpc_iob_get_int ( io_buf ); + + if ( reply->reply_state == 0 ) + { + /* verifier.flavor */ + oncrpc_iob_get_int ( io_buf ); + /* verifier.length */ + iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf )); + + /* We don't use the verifier in iPXE, let it be an empty + verifier. */ + reply->verifier = &oncrpc_auth_none; + } + + reply->accept_state = oncrpc_iob_get_int ( io_buf ); + reply->data = io_buf; + + return 0; +} From e52c24492af3465657fef882e15da9bc80e3512a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2013 18:06:41 +0200 Subject: [PATCH 33/65] [script] Avoid trying to read final character of a zero-length string Detected using Valgrind. Signed-off-by: Michael Brown --- src/image/script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/image/script.c b/src/image/script.c index 8ef05854..ceccd901 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -92,11 +92,11 @@ static int process_script ( struct image *image, script_offset += ( frag_len + 1 ); /* Strip trailing CR, if present */ - if ( line[ len - 1 ] == '\r' ) + if ( len && ( line[ len - 1 ] == '\r' ) ) len--; /* Handle backslash continuations */ - if ( line[ len - 1 ] == '\\' ) { + if ( len && ( line[ len - 1 ] == '\\' ) ) { len--; rc = -EINVAL; continue; From 9978e2cb3725c716169ade674c18376b1a401219 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Jul 2013 00:23:09 +0200 Subject: [PATCH 34/65] [legal] Add missing FILE_LICENCE declarations Signed-off-by: Michael Brown --- src/include/sys/time.h | 2 ++ src/include/time.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/include/sys/time.h b/src/include/sys/time.h index 6c9b7421..2647d358 100644 --- a/src/include/sys/time.h +++ b/src/include/sys/time.h @@ -6,6 +6,8 @@ * Date and time */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include /** Seconds since the Epoch diff --git a/src/include/time.h b/src/include/time.h index bc73af4c..452a544b 100644 --- a/src/include/time.h +++ b/src/include/time.h @@ -6,6 +6,8 @@ * Date and time */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include #include From ca319873bf43d5d955cc3d8770b5b2415798907d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 16 Jul 2013 00:08:50 +0200 Subject: [PATCH 35/65] [build] Fix %.licence build target Our use of --gc-sections causes the linker to discard the symbols defined by FILE_LICENCE(), meaning that the resulting licence determination is incomplete. We must use the KEEP() directive in the linker script to force the linker to not discard the licence symbols. Using KEEP(*(COMMON)) would be undesirable, since there are some symbols in COMMON which we may wish to discard. Fix by placing symbols defined by PROVIDE_SYMBOL() (which is used by FILE_LICENCE()) into a special ".provided" section, which we then mark with KEEP(). All such symbols are zero-length, so there is no cost in terms of the final binary size. Since the symbols are no longer in COMMON, the linker will reject symbols with the same name coming from multiple objects. We therefore append the object name to the licence symbol, to ensure that it is unique. Reported-by: Marin Hannache Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 11 ++++++++--- src/arch/i386/scripts/i386-kir.lds | 2 ++ src/arch/i386/scripts/i386.lds | 2 ++ src/arch/i386/scripts/linux.lds | 2 ++ src/arch/x86/scripts/efi.lds | 2 ++ src/arch/x86_64/scripts/linux.lds | 2 ++ src/include/compiler.h | 23 +++++++++++++---------- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 80b70f5c..8603bfd6 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1011,15 +1011,20 @@ $(BIN)/%.nodeps : $(BIN)/%.tmp # Get licensing verdict for the specified target # define licensable_deps_list - $(filter-out config/local/%.h,$(call deps_list,$(1))) + $(filter-out config/local/%.h,\ + $(filter-out $(BIN)/.%.list,\ + $(call deps_list,$(1)))) endef define unlicensed_deps_list $(shell grep -L FILE_LICENCE $(call licensable_deps_list,$(1))) endef define licence_list - $(patsubst __licence_%,%,\ - $(filter __licence_%,$(shell $(NM) $(1) | cut -d" " -f3))) + $(sort $(foreach LICENCE,\ + $(filter __licence__%,$(shell $(NM) $(1) | cut -d" " -f3)),\ + $(word 2,$(subst __, ,$(LICENCE))))) endef +$(BIN)/%.licence_list : $(BIN)/%.tmp + $(Q)$(ECHO) $(call licence_list,$<) $(BIN)/%.licence : $(BIN)/%.tmp $(QM)$(ECHO) " [LICENCE] $@" $(Q)$(if $(strip $(call unlicensed_deps_list,$<)),\ diff --git a/src/arch/i386/scripts/i386-kir.lds b/src/arch/i386/scripts/i386-kir.lds index 338d6ee8..66bf804e 100644 --- a/src/arch/i386/scripts/i386-kir.lds +++ b/src/arch/i386/scripts/i386-kir.lds @@ -98,6 +98,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata16_progbits = .; } .bss16 : AT ( _data16_load_offset + __bss16 ) { diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index fb763656..98f95cb2 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -109,6 +109,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _mtextdata = .; } .bss.textdata (NOLOAD) : AT ( _end_lma ) { *(.bss) diff --git a/src/arch/i386/scripts/linux.lds b/src/arch/i386/scripts/linux.lds index 64c9b97f..9f2eeaf3 100644 --- a/src/arch/i386/scripts/linux.lds +++ b/src/arch/i386/scripts/linux.lds @@ -53,6 +53,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/arch/x86/scripts/efi.lds b/src/arch/x86/scripts/efi.lds index 1a16c29b..f1049f24 100644 --- a/src/arch/x86/scripts/efi.lds +++ b/src/arch/x86/scripts/efi.lds @@ -55,6 +55,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/arch/x86_64/scripts/linux.lds b/src/arch/x86_64/scripts/linux.lds index 85e548ff..47db2174 100644 --- a/src/arch/x86_64/scripts/linux.lds +++ b/src/arch/x86_64/scripts/linux.lds @@ -53,6 +53,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/include/compiler.h b/src/include/compiler.h index e5559245..132793b5 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -60,12 +60,15 @@ /** Provide a symbol within this object file */ #ifdef ASSEMBLY #define PROVIDE_SYMBOL( _sym ) \ + .section ".provided", "a", @nobits ; \ + .hidden _sym ; \ .globl _sym ; \ - .comm _sym, 0 + _sym: ; \ + .previous #else /* ASSEMBLY */ #define PROVIDE_SYMBOL( _sym ) \ - extern char _sym[]; \ - char _sym[0] + char _sym[0] \ + __attribute__ (( section ( ".provided" ) )) #endif /* ASSEMBLY */ /** Require a symbol within this object file @@ -652,7 +655,7 @@ int __debug_disable; * be in the public domain. */ #define FILE_LICENCE_PUBLIC_DOMAIN \ - PROVIDE_SYMBOL ( __licence_public_domain ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__public_domain__ ) ) /** Declare a file as being under version 2 (or later) of the GNU GPL * @@ -661,7 +664,7 @@ int __debug_disable; * (at your option) any later version". */ #define FILE_LICENCE_GPL2_OR_LATER \ - PROVIDE_SYMBOL ( __licence_gpl2_or_later ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_or_later__ ) ) /** Declare a file as being under version 2 of the GNU GPL * @@ -670,7 +673,7 @@ int __debug_disable; * "or, at your option, any later version" clause. */ #define FILE_LICENCE_GPL2_ONLY \ - PROVIDE_SYMBOL ( __licence_gpl2_only ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_only__ ) ) /** Declare a file as being under any version of the GNU GPL * @@ -682,7 +685,7 @@ int __debug_disable; * version ever published by the Free Software Foundation". */ #define FILE_LICENCE_GPL_ANY \ - PROVIDE_SYMBOL ( __licence_gpl_any ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl_any__ ) ) /** Declare a file as being under the three-clause BSD licence * @@ -707,7 +710,7 @@ int __debug_disable; * functionally equivalent to the standard three-clause BSD licence. */ #define FILE_LICENCE_BSD3 \ - PROVIDE_SYMBOL ( __licence_bsd3 ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__bsd3__ ) ) /** Declare a file as being under the two-clause BSD licence * @@ -728,7 +731,7 @@ int __debug_disable; * functionally equivalent to the standard two-clause BSD licence. */ #define FILE_LICENCE_BSD2 \ - PROVIDE_SYMBOL ( __licence_bsd2 ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__bsd2__ ) ) /** Declare a file as being under the one-clause MIT-style licence * @@ -738,7 +741,7 @@ int __debug_disable; * permission notice appear in all copies. */ #define FILE_LICENCE_MIT \ - PROVIDE_SYMBOL ( __licence_mit ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__mit__ ) ) /** Declare a particular licence as applying to a file */ #define FILE_LICENCE( _licence ) FILE_LICENCE_ ## _licence From 063645118ce1f73e143bb17a74bd678d31d7563e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 13:35:19 +0100 Subject: [PATCH 36/65] [settings] Clarify usage of the term "named setting" There are currently two conflicting usages of the term "named setting" within iPXE: one refers to predefined settings (such as show up in the "config" UI), the other refers to settings identified by a name (such as "net0.dhcp/ip"). Split these usages into the term "predefined setting" and "named setting" to avoid ambiguity. Signed-off-by: Michael Brown --- src/arch/i386/interface/vmware/guestinfo.c | 7 +++---- src/core/settings.c | 20 ++++++++++---------- src/interface/smbios/smbios_settings.c | 4 ++-- src/net/netdev_settings.c | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/i386/interface/vmware/guestinfo.c index da013ca2..8ce363aa 100644 --- a/src/arch/i386/interface/vmware/guestinfo.c +++ b/src/arch/i386/interface/vmware/guestinfo.c @@ -58,7 +58,7 @@ static int guestinfo_fetch_type ( struct settings *settings, strlen ( parent_name ) + 1 /* "." */ + strlen ( setting->name ) + 1 /* "." */ + ( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ]; - struct setting *named_setting; + struct setting *predefined; char *info; int info_len; int check_len; @@ -82,9 +82,8 @@ static int guestinfo_fetch_type ( struct settings *settings, /* Determine default type if necessary */ if ( ! type ) { - named_setting = find_setting ( setting->name ); - type = ( named_setting ? - named_setting->type : &setting_type_string ); + predefined = find_setting ( setting->name ); + type = ( predefined ? predefined->type : &setting_type_string ); } assert ( type != NULL ); diff --git a/src/core/settings.c b/src/core/settings.c index 8cdabe09..6d0c854f 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -262,7 +262,7 @@ static void autovivified_settings_free ( struct refcnt *refcnt ) { } /** - * Find child named settings block + * Find child settings block * * @v parent Parent settings block * @v name Name within this parent @@ -289,7 +289,7 @@ struct settings * find_child_settings ( struct settings *parent, } /** - * Find or create child named settings block + * Find or create child settings block * * @v parent Parent settings block * @v name Name within this parent @@ -383,7 +383,7 @@ parse_settings_name ( const char *name, } /** - * Find named settings block + * Find settings block * * @v name Name * @ret settings Settings block, or NULL @@ -1113,10 +1113,10 @@ int storef_setting ( struct settings *settings, struct setting *setting, */ /** - * Find named setting + * Find predefined setting * * @v name Name - * @ret setting Named setting, or NULL + * @ret setting Setting, or NULL */ struct setting * find_setting ( const char *name ) { struct setting *setting; @@ -1192,7 +1192,7 @@ parse_setting_name ( const char *name, char *settings_name; char *setting_name; char *type_name; - struct setting *named_setting; + struct setting *predefined; /* Set defaults */ *settings = &settings_root; @@ -1226,10 +1226,10 @@ parse_setting_name ( const char *name, setting->tag = parse_setting_tag ( setting_name ); setting->scope = (*settings)->default_scope; setting->name = setting_name; - for_each_table_entry ( named_setting, SETTINGS ) { - /* Matches a defined named setting; use that setting */ - if ( setting_cmp ( named_setting, setting ) == 0 ) { - memcpy ( setting, named_setting, sizeof ( *setting ) ); + for_each_table_entry ( predefined, SETTINGS ) { + /* Matches a predefined setting; use that setting */ + if ( setting_cmp ( predefined, setting ) == 0 ) { + memcpy ( setting, predefined, sizeof ( *setting ) ); break; } } diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index d2975df4..ecd3f1d9 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -197,8 +197,8 @@ struct setting uuid_setting __setting ( SETTING_HOST ) = { .scope = &smbios_settings_scope, }; -/** Other SMBIOS named settings */ -struct setting smbios_named_settings[] __setting ( SETTING_HOST_EXTRA ) = { +/** Other SMBIOS predefined settings */ +struct setting smbios_predefined_settings[] __setting ( SETTING_HOST_EXTRA ) = { { .name = "manufacturer", .description = "Manufacturer", diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 72152762..52e8007d 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -35,7 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Network device named settings */ +/** Network device predefined settings */ struct setting mac_setting __setting ( SETTING_NETDEV ) = { .name = "mac", .description = "MAC address", From 3880ebeb182208f03db32464b954dba1b8b8338b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 17 Jul 2013 14:00:38 +0100 Subject: [PATCH 37/65] [settings] Avoid potentially large stack allocations Avoid potentially large stack allocations in fetchf_setting() and storef_setting(). Signed-off-by: Michael Brown --- src/core/settings.c | 73 ++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 6d0c854f..891e7b3a 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1046,28 +1046,32 @@ int setting_cmp ( struct setting *a, struct setting *b ) { */ int fetchf_setting ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { + void *raw; int raw_len; - int check_len; - int rc; + int ret; /* Fetch raw value */ - raw_len = fetch_setting_len ( settings, setting ); + raw_len = fetch_setting_copy ( settings, setting, &raw ); if ( raw_len < 0 ) { - rc = raw_len; - return rc; - } else { - uint8_t raw[raw_len]; - - /* Fetch raw value */ - check_len = fetch_setting ( settings, setting, raw, - sizeof ( raw ) ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == raw_len ); - - /* Format value */ - return setting->type->format ( raw, sizeof ( raw ), buf, len ); + ret = raw_len; + goto err_fetch_copy; } + + /* Return error if setting does not exist */ + if ( ! raw ) { + ret = -ENOENT; + goto err_exists; + } + + /* Format setting */ + if ( ( ret = setting->type->format ( raw, raw_len, buf, len ) ) < 0 ) + goto err_format; + + err_format: + free ( raw ); + err_exists: + err_fetch_copy: + return ret; } /** @@ -1080,6 +1084,7 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, */ int storef_setting ( struct settings *settings, struct setting *setting, const char *value ) { + void *raw; int raw_len; int check_len; int rc; @@ -1088,21 +1093,33 @@ int storef_setting ( struct settings *settings, struct setting *setting, if ( ( ! value ) || ( ! value[0] ) ) return delete_setting ( settings, setting ); - /* Parse formatted value */ + /* Get raw value length */ raw_len = setting->type->parse ( value, NULL, 0 ); if ( raw_len < 0 ) { rc = raw_len; - return rc; - } else { - uint8_t raw[raw_len]; - - /* Parse formatted value */ - check_len = setting->type->parse ( value, raw, sizeof ( raw ) ); - assert ( check_len == raw_len ); - - /* Store raw value */ - return store_setting ( settings, setting, raw, sizeof ( raw ) ); + goto err_parse_len; } + + /* Allocate buffer for raw value */ + raw = malloc ( raw_len ); + if ( ! raw ) { + rc = -ENOMEM; + goto err_alloc_raw; + } + + /* Parse formatted value */ + check_len = setting->type->parse ( value, raw, raw_len ); + assert ( check_len == raw_len ); + + /* Store raw value */ + if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) + goto err_store; + + err_store: + free ( raw ); + err_alloc_raw: + err_parse_len: + return rc; } /****************************************************************************** From 78178608e978f42c12e12dfd32072e3e9cb85bf9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 15:48:29 +0100 Subject: [PATCH 38/65] [settings] Remove temporary name buffer parameter from parse_setting_name() Signed-off-by: Michael Brown --- src/core/settings.c | 62 +++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 891e7b3a..6f57891b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1189,27 +1189,27 @@ static struct setting_type * find_setting_type ( const char *name ) { * @v settings Settings block to fill in * @v setting Setting to fill in * @v default_type Default type to use, if none specified - * @v tmp_name Buffer for copy of setting name * @ret rc Return status code * * Interprets a name of the form * "[settings_name/]tag_name[:type_name]" and fills in the appropriate * fields. * - * The @c tmp_name buffer must be large enough to hold a copy of the - * setting name. + * Note that on success, this function will have modified the original + * setting @c name. */ static int -parse_setting_name ( const char *name, - struct settings * ( * get_child ) ( struct settings *, - const char * ), +parse_setting_name ( char *name, + struct settings * ( * get_child ) + ( struct settings *settings, + const char *name ), struct settings **settings, struct setting *setting, - struct setting_type *default_type, - char *tmp_name ) { + struct setting_type *default_type ) { char *settings_name; char *setting_name; char *type_name; struct setting *predefined; + int rc; /* Set defaults */ *settings = &settings_root; @@ -1218,12 +1218,11 @@ parse_setting_name ( const char *name, setting->type = default_type; /* Split name into "[settings_name/]setting_name[:type_name]" */ - strcpy ( tmp_name, name ); - if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { + if ( ( setting_name = strchr ( name, '/' ) ) != NULL ) { *(setting_name++) = 0; - settings_name = tmp_name; + settings_name = name; } else { - setting_name = tmp_name; + setting_name = name; settings_name = NULL; } if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) @@ -1235,7 +1234,8 @@ parse_setting_name ( const char *name, if ( *settings == NULL ) { DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", settings_name, name ); - return -ENODEV; + rc = -ENODEV; + goto err; } } @@ -1257,11 +1257,20 @@ parse_setting_name ( const char *name, if ( setting->type == NULL ) { DBG ( "Invalid setting type \"%s\" in \"%s\"\n", type_name, name ); - return -ENOTSUP; + rc = -ENOTSUP; + goto err; } } return 0; + + err: + /* Restore original name */ + if ( settings_name ) + *( setting_name - 1 ) = '/'; + if ( type_name ) + *( type_name - 1 ) = ':'; + return rc; } /** @@ -1299,10 +1308,13 @@ int store_named_setting ( const char *name, struct setting_type *default_type, char tmp_name[ strlen ( name ) + 1 ]; int rc; + /* Create modifiable copy of setting name */ + strcpy ( tmp_name, name ); + /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, autovivify_child_settings, - &settings, &setting, default_type, - tmp_name ) ) != 0 ) + if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, + &settings, &setting, + default_type ) ) != 0 ) return rc; /* Store setting */ @@ -1327,10 +1339,13 @@ int storef_named_setting ( const char *name, struct setting_type *default_type, char tmp_name[ strlen ( name ) + 1 ]; int rc; + /* Create modifiable copy of setting name */ + strcpy ( tmp_name, name ); + /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, autovivify_child_settings, - &settings, &setting, default_type, - tmp_name ) ) != 0 ) + if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, + &settings, &setting, + default_type ) ) != 0 ) return rc; /* Store setting */ @@ -1360,9 +1375,12 @@ int fetchf_named_setting ( const char *name, int len; int rc; + /* Create modifiable copy of setting name */ + strcpy ( tmp_name, name ); + /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, find_child_settings, &settings, - &setting, NULL, tmp_name ) ) != 0 ) + if ( ( rc = parse_setting_name ( tmp_name, find_child_settings, + &settings, &setting, NULL ) ) != 0 ) return rc; /* Fetch setting */ From 77c70ac0cdced55a602a15fd8481f2e11e88569c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2013 14:08:13 +0100 Subject: [PATCH 39/65] [settings] Remove default_type parameter from parse_setting_name() Signed-off-by: Michael Brown --- src/core/settings.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 6f57891b..c85ea61d 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1063,6 +1063,10 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, goto err_exists; } + /* Sanity check */ + assert ( setting->type != NULL ); + assert ( setting->type->format != NULL ); + /* Format setting */ if ( ( ret = setting->type->format ( raw, raw_len, buf, len ) ) < 0 ) goto err_format; @@ -1093,6 +1097,10 @@ int storef_setting ( struct settings *settings, struct setting *setting, if ( ( ! value ) || ( ! value[0] ) ) return delete_setting ( settings, setting ); + /* Sanity check */ + assert ( setting->type != NULL ); + assert ( setting->type->parse != NULL ); + /* Get raw value length */ raw_len = setting->type->parse ( value, NULL, 0 ); if ( raw_len < 0 ) { @@ -1188,7 +1196,6 @@ static struct setting_type * find_setting_type ( const char *name ) { * @v get_child Function to find or create child settings block * @v settings Settings block to fill in * @v setting Setting to fill in - * @v default_type Default type to use, if none specified * @ret rc Return status code * * Interprets a name of the form @@ -1203,8 +1210,7 @@ parse_setting_name ( char *name, struct settings * ( * get_child ) ( struct settings *settings, const char *name ), - struct settings **settings, struct setting *setting, - struct setting_type *default_type ) { + struct settings **settings, struct setting *setting ) { char *settings_name; char *setting_name; char *type_name; @@ -1215,7 +1221,6 @@ parse_setting_name ( char *name, *settings = &settings_root; memset ( setting, 0, sizeof ( *setting ) ); setting->name = ""; - setting->type = default_type; /* Split name into "[settings_name/]setting_name[:type_name]" */ if ( ( setting_name = strchr ( name, '/' ) ) != NULL ) { @@ -1313,10 +1318,13 @@ int store_named_setting ( const char *name, struct setting_type *default_type, /* Parse setting name */ if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, - &settings, &setting, - default_type ) ) != 0 ) + &settings, &setting ) ) != 0 ) return rc; + /* Apply default type if necessary */ + if ( ! setting.type ) + setting.type = default_type; + /* Store setting */ if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) return rc; @@ -1344,10 +1352,13 @@ int storef_named_setting ( const char *name, struct setting_type *default_type, /* Parse setting name */ if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, - &settings, &setting, - default_type ) ) != 0 ) + &settings, &setting ) ) != 0 ) return rc; + /* Apply default type if necessary */ + if ( ! setting.type ) + setting.type = default_type; + /* Store setting */ if ( ( rc = storef_setting ( settings, &setting, value ) ) != 0 ) return rc; @@ -1380,7 +1391,7 @@ int fetchf_named_setting ( const char *name, /* Parse setting name */ if ( ( rc = parse_setting_name ( tmp_name, find_child_settings, - &settings, &setting, NULL ) ) != 0 ) + &settings, &setting ) ) != 0 ) return rc; /* Fetch setting */ From 5ffcae69c03afeab1c7b17a725376b45c91d04d9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 14:44:09 +0100 Subject: [PATCH 40/65] [settings] Expose parse_setting_name() Signed-off-by: Michael Brown --- src/core/settings.c | 16 +++++----------- src/include/ipxe/settings.h | 9 ++++++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index c85ea61d..a0a09d4e 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -295,8 +295,8 @@ struct settings * find_child_settings ( struct settings *parent, * @v name Name within this parent * @ret settings Settings block, or NULL */ -static struct settings * autovivify_child_settings ( struct settings *parent, - const char *name ) { +struct settings * autovivify_child_settings ( struct settings *parent, + const char *name ) { struct { struct autovivified_settings autovivified; char name[ strlen ( name ) + 1 /* NUL */ ]; @@ -356,9 +356,7 @@ const char * settings_name ( struct settings *settings ) { * @ret settings Settings block, or NULL */ static struct settings * -parse_settings_name ( const char *name, - struct settings * ( * get_child ) ( struct settings *, - const char * ) ) { +parse_settings_name ( const char *name, get_child_settings_t get_child ) { struct settings *settings = &settings_root; char name_copy[ strlen ( name ) + 1 ]; char *subname; @@ -1205,12 +1203,8 @@ static struct setting_type * find_setting_type ( const char *name ) { * Note that on success, this function will have modified the original * setting @c name. */ -static int -parse_setting_name ( char *name, - struct settings * ( * get_child ) - ( struct settings *settings, - const char *name ), - struct settings **settings, struct setting *setting ) { +int parse_setting_name ( char *name, get_child_settings_t get_child, + struct settings **settings, struct setting *setting ) { char *settings_name; char *setting_name; char *type_name; diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 6e75251c..81ee90f3 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -241,6 +241,9 @@ struct generic_settings { struct list_head list; }; +/** A child settings block locator function */ +typedef struct settings * ( *get_child_settings_t ) ( struct settings *settings, + const char *name ); extern struct settings_operations generic_settings_operations; extern int generic_settings_store ( struct settings *settings, struct setting *setting, @@ -295,10 +298,14 @@ extern int setting_cmp ( struct setting *a, struct setting *b ); extern struct settings * find_child_settings ( struct settings *parent, const char *name ); +extern struct settings * autovivify_child_settings ( struct settings *parent, + const char *name ); extern const char * settings_name ( struct settings *settings ); extern struct settings * find_settings ( const char *name ); extern struct setting * find_setting ( const char *name ); - +extern int parse_setting_name ( char *name, get_child_settings_t get_child, + struct settings **settings, + struct setting *setting ); extern int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ); extern int fetchf_setting ( struct settings *settings, struct setting *setting, From 72fb55e437474f1322ae6c748ab0df75e5eb84b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2013 14:53:38 +0100 Subject: [PATCH 41/65] [settings] Change "not-found" semantics of fetch_setting_copy() fetch_settings_copy() currently returns success and a NULL data pointer to indicate a non-existent setting. This is intended to allow the caller to differentiate between a non-existent setting and an error in allocating memory for the copy of the setting. The underlying settings blocks' fetch() methods provide no way to perform an existence check separate from an attempt to fetch the setting. A "non-existent setting" therefore means simply a setting for which an error was encountered when attempting to fetch from every settings block within the subtree. Since any underlying error within a settings block (e.g. a GuestRPC failure when attempting to retrieve a VMware GuestInfo setting) will produce the effect of a "non-existent setting", it seems somewhat meaningless to give special treatment to memory allocation errors within fetch_setting_copy(). Remove the special treatment and simplify the semantics of fetch_setting_copy() by directly passing through any underlying error (including non-existence) encountered while fetching the setting. Signed-off-by: Michael Brown --- src/core/settings.c | 16 ++-------------- src/crypto/clientcert.c | 21 ++++----------------- src/crypto/rootcert.c | 18 ++---------------- 3 files changed, 8 insertions(+), 47 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index a0a09d4e..0be1a2dd 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -722,11 +722,6 @@ int fetch_setting_len ( struct settings *settings, struct setting *setting ) { * * The caller is responsible for eventually freeing the allocated * buffer. - * - * To allow the caller to distinguish between a non-existent setting - * and an error in allocating memory for the copy, this function will - * return success (and a NULL buffer pointer) for a non-existent - * setting. */ int fetch_setting_copy ( struct settings *settings, struct setting *setting, void **data ) { @@ -736,10 +731,10 @@ int fetch_setting_copy ( struct settings *settings, struct setting *setting, /* Avoid returning uninitialised data on error */ *data = NULL; - /* Fetch setting length, and return success if non-existent */ + /* Check existence, and fetch setting length */ len = fetch_setting_len ( settings, setting ); if ( len < 0 ) - return 0; + return len; /* Allocate buffer */ *data = malloc ( len ); @@ -1055,12 +1050,6 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, goto err_fetch_copy; } - /* Return error if setting does not exist */ - if ( ! raw ) { - ret = -ENOENT; - goto err_exists; - } - /* Sanity check */ assert ( setting->type != NULL ); assert ( setting->type->format != NULL ); @@ -1071,7 +1060,6 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, err_format: free ( raw ); - err_exists: err_fetch_copy: return ret; } diff --git a/src/crypto/clientcert.c b/src/crypto/clientcert.c index 5ce1f6c1..6f6bf113 100644 --- a/src/crypto/clientcert.c +++ b/src/crypto/clientcert.c @@ -116,7 +116,6 @@ static int clientcert_apply_settings ( void ) { static void *cert = NULL; static void *key = NULL; int len; - int rc; /* Allow client certificate to be overridden only if * not explicitly specified at build time. @@ -129,14 +128,8 @@ static int clientcert_apply_settings ( void ) { /* Fetch new client certificate, if any */ free ( cert ); - len = fetch_setting_copy ( NULL, &cert_setting, &cert ); - if ( len < 0 ) { - rc = len; - DBGC ( &client_certificate, "CLIENTCERT cannot fetch " - "client certificate: %s\n", strerror ( rc ) ); - return rc; - } - if ( cert ) { + if ( ( len = fetch_setting_copy ( NULL, &cert_setting, + &cert ) ) >= 0 ) { client_certificate.data = cert; client_certificate.len = len; } @@ -147,14 +140,8 @@ static int clientcert_apply_settings ( void ) { /* Fetch new client private key, if any */ free ( key ); - len = fetch_setting_copy ( NULL, &privkey_setting, &key ); - if ( len < 0 ) { - rc = len; - DBGC ( &client_certificate, "CLIENTCERT cannot fetch " - "client private key: %s\n", strerror ( rc ) ); - return rc; - } - if ( key ) { + if ( ( len = fetch_setting_copy ( NULL, &privkey_setting, + &key ) ) >= 0 ) { client_private_key.data = key; client_private_key.len = len; } diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 30ca170f..2aa31334 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -91,7 +91,6 @@ struct x509_root root_certificates = { static void rootcert_init ( void ) { void *external = NULL; int len; - int rc; /* Allow trusted root certificates to be overridden only if * not explicitly specified at build time. @@ -101,21 +100,8 @@ static void rootcert_init ( void ) { /* Fetch copy of "trust" setting, if it exists. This * memory will never be freed. */ - len = fetch_setting_copy ( NULL, &trust_setting, &external ); - if ( len < 0 ) { - rc = len; - DBGC ( &root_certificates, "ROOTCERT cannot fetch " - "trusted root certificate fingerprints: %s\n", - strerror ( rc ) ); - /* No way to prevent startup; fail safe by - * trusting no certificates. - */ - root_certificates.count = 0; - return; - } - - /* Use certificates from "trust" setting, if present */ - if ( external ) { + if ( ( len = fetch_setting_copy ( NULL, &trust_setting, + &external ) ) >= 0 ) { root_certificates.fingerprints = external; root_certificates.count = ( len / FINGERPRINT_LEN ); } From a5be7c4f2902502c6eea5edf6bcfbfe90eb5537d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 14:46:20 +0100 Subject: [PATCH 42/65] [settings] Add fetchf_setting_copy() Signed-off-by: Michael Brown --- src/core/settings.c | 41 +++++++++++++++++++++++++++++++++++-- src/include/ipxe/settings.h | 2 ++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 0be1a2dd..d3d15155 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -726,7 +726,7 @@ int fetch_setting_len ( struct settings *settings, struct setting *setting ) { int fetch_setting_copy ( struct settings *settings, struct setting *setting, void **data ) { int len; - int check_len = 0; + int check_len; /* Avoid returning uninitialised data on error */ *data = NULL; @@ -1028,7 +1028,7 @@ int setting_cmp ( struct setting *a, struct setting *b ) { */ /** - * Fetch and format value of setting + * Fetch formatted value of setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch @@ -1064,6 +1064,43 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, return ret; } +/** + * Fetch copy of formatted value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v type Settings type + * @v value Buffer to allocate and fill with formatted value + * @ret len Length of formatted value, or negative error + * + * The caller is responsible for eventually freeing the allocated + * buffer. + */ +int fetchf_setting_copy ( struct settings *settings, struct setting *setting, + char **value ) { + int len; + int check_len; + + /* Avoid returning uninitialised data on error */ + *value = NULL; + + /* Check existence, and fetch formatted value length */ + len = fetchf_setting ( settings, setting, NULL, 0 ); + if ( len < 0 ) + return len; + + /* Allocate buffer */ + *value = zalloc ( len + 1 /* NUL */ ); + if ( ! *value ) + return -ENOMEM; + + /* Fetch formatted value */ + check_len = fetchf_setting ( settings, setting, *value, + ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + return len; +} + /** * Store formatted value of setting * diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 81ee90f3..8f919bbe 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -310,6 +310,8 @@ extern int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ); extern int fetchf_setting ( struct settings *settings, struct setting *setting, char *buf, size_t len ); +extern int fetchf_setting_copy ( struct settings *settings, + struct setting *setting, char **value ); extern int storef_setting ( struct settings *settings, struct setting *setting, const char *value ); From 129a70631a1b138f74bf0e2a7c6bdd1b564f8a10 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 14:47:42 +0100 Subject: [PATCH 43/65] [settings] Eliminate call to fetchf_named_setting() in expand_settings() Use parse_setting_name() and fetchf_setting_copy() in expand_settings(), to eliminate the call to fetchf_named_setting(). This change also eliminates the potentially large stack-allocated buffer in expand_settings(). Signed-off-by: Michael Brown --- src/core/settings.c | 47 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index d3d15155..d5e02460 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2014,15 +2014,18 @@ struct setting_type setting_type_busdevfn __setting_type = { * eventually free() it. */ char * expand_settings ( const char *string ) { + struct settings *settings; + struct setting setting; char *expstr; char *start; char *end; char *head; char *name; char *tail; - int setting_len; - int new_len; + char *value; char *tmp; + int new_len; + int rc; /* Obtain temporary modifiable copy of string */ expstr = strdup ( string ); @@ -2052,27 +2055,27 @@ char * expand_settings ( const char *string ) { *end = '\0'; tail = ( end + 1 ); - /* Determine setting length */ - setting_len = fetchf_named_setting ( name, NULL, 0, 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, NULL, 0, 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; + /* Expand setting */ + if ( ( rc = parse_setting_name ( name, find_child_settings, + &settings, + &setting ) ) != 0 ) { + /* Treat invalid setting names as empty */ + value = NULL; + } else { + /* Fetch and format setting value. Ignore + * errors; treat non-existent settings as empty. + */ + fetchf_setting_copy ( settings, &setting, &value ); } + + /* Construct expanded string and discard old string */ + tmp = expstr; + new_len = asprintf ( &expstr, "%s%s%s", + head, ( value ? value : "" ), tail ); + free ( value ); + free ( tmp ); + if ( new_len < 0 ) + return NULL; } return expstr; From 44fd30904467e9c99a42b0e8fe2456e7d43a8e0e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 14:49:06 +0100 Subject: [PATCH 44/65] [settings] Eliminate call to store_named_setting() in nslookup.c Signed-off-by: Michael Brown --- src/usr/nslookup.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/usr/nslookup.c b/src/usr/nslookup.c index c931ec5a..cb6d8d2b 100644 --- a/src/usr/nslookup.c +++ b/src/usr/nslookup.c @@ -46,7 +46,7 @@ struct nslookup { struct interface resolver; /** Setting name */ - const char *setting_name; + char *setting_name; }; /** @@ -71,7 +71,9 @@ static void nslookup_close ( struct nslookup *nslookup, int rc ) { static void nslookup_resolv_done ( struct nslookup *nslookup, struct sockaddr *sa ) { struct sockaddr_in *sin; - struct setting_type *type; + struct setting_type *default_type; + struct settings *settings; + struct setting setting; void *data; size_t len; int rc; @@ -82,16 +84,25 @@ static void nslookup_resolv_done ( struct nslookup *nslookup, sin = ( ( struct sockaddr_in * ) sa ); data = &sin->sin_addr; len = sizeof ( sin->sin_addr ); - type = &setting_type_ipv4; + default_type = &setting_type_ipv4; break; default: rc = -ENOTSUP; goto err; } - /* Save in specified setting */ - if ( ( rc = store_named_setting ( nslookup->setting_name, type, - data, len ) ) != 0 ) + /* Parse specified setting name */ + if ( ( rc = parse_setting_name ( nslookup->setting_name, + autovivify_child_settings, &settings, + &setting ) ) != 0 ) + goto err; + + /* Apply default type if necessary */ + if ( ! setting.type ) + setting.type = default_type; + + /* Store in specified setting */ + if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) goto err; err: From 8ea5822afd5530245c391e9058f2c878a3cd0597 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 15:19:59 +0100 Subject: [PATCH 45/65] [settings] Remove now-unused store_named_setting() Signed-off-by: Michael Brown --- src/core/settings.c | 35 ----------------------------------- src/include/ipxe/settings.h | 13 ------------- 2 files changed, 48 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index d5e02460..ab44dea9 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1316,41 +1316,6 @@ int setting_name ( struct settings *settings, struct setting *setting, setting->name, setting->type->name ); } -/** - * Store value of named setting - * - * @v name Name of setting - * @v default_type Default type to use, if none specified - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int store_named_setting ( const char *name, struct setting_type *default_type, - const void *data, size_t len ) { - struct settings *settings; - struct setting setting; - char tmp_name[ strlen ( name ) + 1 ]; - int rc; - - /* Create modifiable copy of setting name */ - strcpy ( tmp_name, name ); - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, - &settings, &setting ) ) != 0 ) - return rc; - - /* Apply default type if necessary */ - if ( ! setting.type ) - setting.type = default_type; - - /* Store setting */ - if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) - return rc; - - return 0; -} - /** * Parse and store value of named setting * diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 8f919bbe..2faf4d79 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -315,9 +315,6 @@ extern int fetchf_setting_copy ( struct settings *settings, extern int storef_setting ( struct settings *settings, struct setting *setting, const char *value ); -extern int store_named_setting ( const char *name, - struct setting_type *default_type, - const void *data, size_t len ); extern int storef_named_setting ( const char *name, struct setting_type *default_type, const char *value ); @@ -402,16 +399,6 @@ static inline int delete_setting ( struct settings *settings, return store_setting ( settings, setting, NULL, 0 ); } -/** - * Delete named setting - * - * @v name Name of setting - * @ret rc Return status code - */ -static inline int delete_named_setting ( const char *name ) { - return store_named_setting ( name, NULL, NULL, 0 ); -} - /** * Check existence of setting * From b87020a0908669446796b83d57eaa2c093419621 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Jul 2013 16:13:25 +0100 Subject: [PATCH 46/65] [parseopt] Allow parsed option to be modified Parsing a setting name requires the ability to modify the text being parsed. Signed-off-by: Michael Brown --- src/core/parseopt.c | 14 +++++++------- src/hci/commands/fcmgmt_cmd.c | 7 +++---- src/hci/commands/image_cmd.c | 2 +- src/hci/commands/image_trust_cmd.c | 2 +- src/hci/commands/menu_cmd.c | 8 ++++---- src/include/ipxe/parseopt.h | 18 +++++++++--------- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 659d20ee..1ae5d9b2 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -59,7 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret value String value * @ret rc Return status code */ -int parse_string ( const char *text, const char **value ) { +int parse_string ( char *text, char **value ) { /* Sanity check */ assert ( text != NULL ); @@ -77,7 +77,7 @@ int parse_string ( const char *text, const char **value ) { * @ret value Integer value * @ret rc Return status code */ -int parse_integer ( const char *text, unsigned int *value ) { +int parse_integer ( char *text, unsigned int *value ) { char *endp; /* Sanity check */ @@ -100,7 +100,7 @@ int parse_integer ( const char *text, unsigned int *value ) { * @ret netdev Network device * @ret rc Return status code */ -int parse_netdev ( const char *text, struct net_device **netdev ) { +int parse_netdev ( char *text, struct net_device **netdev ) { /* Sanity check */ assert ( text != NULL ); @@ -122,7 +122,7 @@ int parse_netdev ( const char *text, struct net_device **netdev ) { * @ret menu Menu * @ret rc Return status code */ -int parse_menu ( const char *text, struct menu **menu ) { +int parse_menu ( char *text, struct menu **menu ) { /* Find menu */ *menu = find_menu ( text ); @@ -145,7 +145,7 @@ int parse_menu ( const char *text, struct menu **menu ) { * @ret flag Flag to set * @ret rc Return status code */ -int parse_flag ( const char *text __unused, int *flag ) { +int parse_flag ( char *text __unused, int *flag ) { /* Set flag */ *flag = 1; @@ -160,7 +160,7 @@ int parse_flag ( const char *text __unused, int *flag ) { * @ret key Key * @ret rc Return status code */ -int parse_key ( const char *text, unsigned int *key ) { +int parse_key ( char *text, unsigned int *key ) { /* Interpret single characters as being a literal key character */ if ( text[0] && ! text[1] ) { @@ -198,7 +198,7 @@ int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, char shortopts[ cmd->num_options * 3 /* possible "::" */ + 1 /* "h" */ + 1 /* NUL */ ]; unsigned int shortopt_idx = 0; - int ( * parse ) ( const char *text, void *value ); + int ( * parse ) ( char *text, void *value ); void *value; unsigned int i; unsigned int j; diff --git a/src/hci/commands/fcmgmt_cmd.c b/src/hci/commands/fcmgmt_cmd.c index b7e38040..99f76113 100644 --- a/src/hci/commands/fcmgmt_cmd.c +++ b/src/hci/commands/fcmgmt_cmd.c @@ -43,7 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret port Fibre Channel port * @ret rc Return status code */ -static int parse_fc_port ( const char *text, struct fc_port **port ) { +static int parse_fc_port ( char *text, struct fc_port **port ) { /* Sanity check */ assert ( text != NULL ); @@ -65,7 +65,7 @@ static int parse_fc_port ( const char *text, struct fc_port **port ) { * @ret port_id Fibre Channel port ID * @ret rc Return status code */ -static int parse_fc_port_id ( const char *text, struct fc_port_id *port_id ) { +static int parse_fc_port_id ( char *text, struct fc_port_id *port_id ) { int rc; /* Sanity check */ @@ -87,8 +87,7 @@ static int parse_fc_port_id ( const char *text, struct fc_port_id *port_id ) { * @ret handler Fibre Channel ELS handler * @ret rc Return status code */ -static int parse_fc_els_handler ( const char *text, - struct fc_els_handler **handler ) { +static int parse_fc_els_handler ( char *text, struct fc_els_handler **handler ){ for_each_table_entry ( (*handler), FC_ELS_HANDLERS ) { if ( strcasecmp ( (*handler)->name, text ) == 0 ) diff --git a/src/hci/commands/image_cmd.c b/src/hci/commands/image_cmd.c index 6f51a6ba..17e22dcb 100644 --- a/src/hci/commands/image_cmd.c +++ b/src/hci/commands/image_cmd.c @@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** "img{single}" options */ struct imgsingle_options { /** Image name */ - const char *name; + char *name; /** Replace image */ int replace; /** Free image after execution */ diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index e7a2bf12..ad238bff 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -84,7 +84,7 @@ static int imgtrust_exec ( int argc, char **argv ) { /** "imgverify" options */ struct imgverify_options { /** Required signer common name */ - const char *signer; + char *signer; /** Keep signature after verification */ int keep; }; diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/menu_cmd.c index 10966db2..844ad703 100644 --- a/src/hci/commands/menu_cmd.c +++ b/src/hci/commands/menu_cmd.c @@ -41,7 +41,7 @@ FEATURE ( FEATURE_MISC, "Menu", DHCP_EB_FEATURE_MENU, 1 ); /** "menu" options */ struct menu_options { /** Name */ - const char *name; + char *name; /** Delete */ int delete; }; @@ -107,7 +107,7 @@ static int menu_exec ( int argc, char **argv ) { /** "item" options */ struct item_options { /** Menu name */ - const char *menu; + char *menu; /** Shortcut key */ unsigned int key; /** Use as default */ @@ -192,11 +192,11 @@ static int item_exec ( int argc, char **argv ) { /** "choose" options */ struct choose_options { /** Menu name */ - const char *menu; + char *menu; /** Timeout */ unsigned int timeout; /** Default selection */ - const char *select; + char *select; /** Keep menu */ int keep; }; diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index b492a51e..1e1fe6b7 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -31,7 +31,7 @@ struct option_descriptor { * @v value Option value to fill in * @ret rc Return status code */ - int ( * parse ) ( const char *text, void *value ); + int ( * parse ) ( char *text, void *value ); }; /** @@ -43,9 +43,9 @@ struct option_descriptor { * @ret _parse Generic option parser */ #define OPTION_PARSER( _struct, _field, _parse ) \ - ( ( int ( * ) ( const char *text, void *value ) ) \ + ( ( int ( * ) ( char *text, void *value ) ) \ ( ( ( ( typeof ( _parse ) * ) NULL ) == \ - ( ( int ( * ) ( const char *text, \ + ( ( int ( * ) ( char *text, \ typeof ( ( ( _struct * ) NULL )->_field ) * ) ) \ NULL ) ) ? _parse : _parse ) ) @@ -114,12 +114,12 @@ struct command_descriptor { .usage = _usage, \ } -extern int parse_string ( const char *text, const char **value ); -extern int parse_integer ( const char *text, unsigned int *value ); -extern int parse_netdev ( const char *text, struct net_device **netdev ); -extern int parse_menu ( const char *text, struct menu **menu ); -extern int parse_flag ( const char *text __unused, int *flag ); -extern int parse_key ( const char *text, unsigned int *key ); +extern int parse_string ( char *text, char **value ); +extern int parse_integer ( char *text, unsigned int *value ); +extern int parse_netdev ( char *text, struct net_device **netdev ); +extern int parse_menu ( char *text, struct menu **menu ); +extern int parse_flag ( char *text __unused, int *flag ); +extern int parse_key ( char *text, unsigned int *key ); extern void print_usage ( struct command_descriptor *cmd, char **argv ); extern int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, void *opts ); From 46433f9b5eb917e6ad90ffaa169febdfcf78270f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 15:30:00 +0100 Subject: [PATCH 47/65] [parseopt] Move parse_settings() to parseopt.c Signed-off-by: Michael Brown --- src/core/parseopt.c | 23 +++++++++++++++++++++++ src/hci/commands/config_cmd.c | 22 ---------------------- src/include/ipxe/parseopt.h | 3 ++- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 1ae5d9b2..15cca085 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -172,6 +173,28 @@ int parse_key ( char *text, unsigned int *key ) { return parse_integer ( text, key ); } +/** + * Parse settings block name + * + * @v text Text + * @ret value Integer value + * @ret rc Return status code + */ +int parse_settings ( char *text, struct settings **value ) { + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse scope name */ + *value = find_settings ( text ); + if ( ! *value ) { + printf ( "\"%s\": no such scope\n", text ); + return -EINVAL; + } + + return 0; +} + /** * Print command usage message * diff --git a/src/hci/commands/config_cmd.c b/src/hci/commands/config_cmd.c index f1fb567c..b81c866f 100644 --- a/src/hci/commands/config_cmd.c +++ b/src/hci/commands/config_cmd.c @@ -44,28 +44,6 @@ static struct option_descriptor config_opts[] = {}; static struct command_descriptor config_cmd = COMMAND_DESC ( struct config_options, config_opts, 0, 1, "[]" ); -/** - * Parse settings scope name - * - * @v text Text - * @ret value Integer value - * @ret rc Return status code - */ -static int parse_settings ( const char *text, struct settings **value ) { - - /* Sanity check */ - assert ( text != NULL ); - - /* Parse scope name */ - *value = find_settings ( text ); - if ( ! *value ) { - printf ( "\"%s\": no such scope\n", text ); - return -EINVAL; - } - - return 0; -} - /** * "config" command * diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index 1e1fe6b7..8aeed974 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct net_device; struct menu; +struct settings; /** A command-line option descriptor */ struct option_descriptor { @@ -120,7 +121,7 @@ extern int parse_netdev ( char *text, struct net_device **netdev ); extern int parse_menu ( char *text, struct menu **menu ); extern int parse_flag ( char *text __unused, int *flag ); extern int parse_key ( char *text, unsigned int *key ); -extern void print_usage ( struct command_descriptor *cmd, char **argv ); +extern int parse_settings ( char *text, struct settings **settings ); extern int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, void *opts ); extern int parse_options ( int argc, char **argv, From 1625a8c05fe72b7c53cff049c646e913d6dab362 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 16:11:39 +0100 Subject: [PATCH 48/65] [parseopt] Add parse_setting() Signed-off-by: Michael Brown --- src/core/parseopt.c | 55 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/parseopt.h | 16 ++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 15cca085..65ad4ec5 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -195,6 +195,61 @@ int parse_settings ( char *text, struct settings **value ) { return 0; } +/** + * Parse setting name + * + * @v text Text + * @v setting Named setting to fill in + * @v get_child Function to find or create child settings block + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_setting ( char *text, struct named_setting *setting, + get_child_settings_t get_child ) { + int rc; + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse setting name */ + if ( ( rc = parse_setting_name ( text, get_child, &setting->settings, + &setting->setting ) ) != 0 ) { + printf ( "\"%s\": invalid setting\n", text ); + return rc; + } + + return 0; +} + +/** + * Parse existing setting name + * + * @v text Text + * @v setting Named setting to fill in + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_existing_setting ( char *text, struct named_setting *setting ) { + + return parse_setting ( text, setting, find_child_settings ); +} + +/** + * Parse and autovivify setting name + * + * @v text Text + * @v setting Named setting to fill in + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_autovivified_setting ( char *text, struct named_setting *setting ) { + + return parse_setting ( text, setting, autovivify_child_settings ); +} + /** * Print command usage message * diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index 8aeed974..2e38d231 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -11,10 +11,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include struct net_device; struct menu; -struct settings; /** A command-line option descriptor */ struct option_descriptor { @@ -115,6 +115,14 @@ struct command_descriptor { .usage = _usage, \ } +/** A parsed named setting */ +struct named_setting { + /** Settings block */ + struct settings *settings; + /** Setting */ + struct setting setting; +}; + extern int parse_string ( char *text, char **value ); extern int parse_integer ( char *text, unsigned int *value ); extern int parse_netdev ( char *text, struct net_device **netdev ); @@ -122,6 +130,12 @@ extern int parse_menu ( char *text, struct menu **menu ); extern int parse_flag ( char *text __unused, int *flag ); extern int parse_key ( char *text, unsigned int *key ); extern int parse_settings ( char *text, struct settings **settings ); +extern int parse_setting ( char *text, struct named_setting *setting, + get_child_settings_t get_child ); +extern int parse_existing_setting ( char *text, struct named_setting *setting ); +extern int parse_autovivified_setting ( char *text, + struct named_setting *setting ); +extern void print_usage ( struct command_descriptor *cmd, char **argv ); extern int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, void *opts ); extern int parse_options ( int argc, char **argv, From 7010b1049181b7da26748dfe31187ffd7c012339 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 18 Jul 2013 15:49:32 +0100 Subject: [PATCH 49/65] [settings] Eliminate call to storef_named_setting() in choose_exec() Signed-off-by: Michael Brown --- src/hci/commands/menu_cmd.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/menu_cmd.c index 844ad703..0ad53dbd 100644 --- a/src/hci/commands/menu_cmd.c +++ b/src/hci/commands/menu_cmd.c @@ -228,9 +228,9 @@ static struct command_descriptor choose_cmd = */ static int choose_exec ( int argc, char **argv ) { struct choose_options opts; + struct named_setting setting; struct menu *menu; struct menu_item *item; - const char *setting; int rc; /* Parse options */ @@ -238,7 +238,9 @@ static int choose_exec ( int argc, char **argv ) { goto err_parse_options; /* Parse setting name */ - setting = argv[optind]; + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; /* Identify menu */ if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) @@ -248,11 +250,15 @@ static int choose_exec ( int argc, char **argv ) { if ( ( rc = show_menu ( menu, opts.timeout, opts.select, &item ) ) != 0) goto err_show_menu; + /* Apply default type if necessary */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_string; + /* Store setting */ - if ( ( rc = storef_named_setting ( setting, &setting_type_string, - item->label ) ) != 0 ) { + if ( ( rc = storef_setting ( setting.settings, &setting.setting, + item->label ) ) != 0 ) { printf ( "Could not store \"%s\": %s\n", - setting, strerror ( rc ) ); + setting.setting.name, strerror ( rc ) ); goto err_store; } @@ -265,6 +271,7 @@ static int choose_exec ( int argc, char **argv ) { if ( ! opts.keep ) destroy_menu ( menu ); err_parse_menu: + err_parse_setting: err_parse_options: return rc; } From 652abb67069fa15802e3a30d09b476e9514d9110 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2013 14:07:18 +0100 Subject: [PATCH 50/65] [settings] Eliminate calls to {fetch,store}f_named_setting() in NVO commands A deliberate side effect of this commit is that the "read" command will now preserve the type of the setting, if the setting name contains no type information. For example: iPXE> set foo:ipv4 192.168.0.1 iPXE> read foo 192.168.0.100 iPXE> show foo foo:ipv4 = 192.168.0.100 rather than the arguably unexpected behaviour of resetting the type to "string". Signed-off-by: Michael Brown --- src/hci/commands/nvo_cmd.c | 98 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index f255bbf5..dd54a84e 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -55,31 +55,44 @@ static struct command_descriptor show_cmd = */ static int show_exec ( int argc, char **argv ) { struct show_options opts; - const char *name; + struct named_setting setting; + struct settings *origin; char name_buf[32]; - char value_buf[256]; + char *value; int rc; /* Parse options */ if ( ( rc = parse_options ( argc, argv, &show_cmd, &opts ) ) != 0 ) - return rc; + goto err_parse_options; /* Parse setting name */ - name = argv[optind]; + if ( ( rc = parse_existing_setting ( argv[optind], &setting ) ) != 0 ) + goto err_parse_setting; - /* Fetch setting */ - if ( ( rc = fetchf_named_setting ( name, name_buf, sizeof ( name_buf ), - value_buf, - sizeof ( value_buf ) ) ) < 0 ) { + /* Fetch formatted setting value */ + if ( ( rc = fetchf_setting_copy ( setting.settings, &setting.setting, + &value ) ) < 0 ) { printf ( "Could not find \"%s\": %s\n", - name, strerror ( rc ) ); - return rc; + setting.setting.name, strerror ( rc ) ); + goto err_fetchf; } - /* Print setting value */ - printf ( "%s = %s\n", name_buf, value_buf ); + /* Fetch origin and format fully-qualified name */ + origin = fetch_setting_origin ( setting.settings, &setting.setting ); + assert ( origin != NULL ); + setting_name ( origin, &setting.setting, name_buf, sizeof ( name_buf )); - return 0; + /* Print setting value */ + printf ( "%s = %s\n", name_buf, value ); + + /* Success */ + rc = 0; + + free ( value ); + err_fetchf: + err_parse_setting: + err_parse_options: + return rc; } /** "set", "clear", and "read" options */ @@ -109,10 +122,10 @@ static struct command_descriptor clear_read_cmd = */ static int set_core_exec ( int argc, char **argv, struct command_descriptor *cmd, - int ( * get_value ) ( const char *name, + int ( * get_value ) ( struct named_setting *setting, char **args, char **value ) ) { struct set_core_options opts; - const char *name; + struct named_setting setting; char *value; int rc; @@ -121,26 +134,30 @@ static int set_core_exec ( int argc, char **argv, goto err_parse_options; /* Parse setting name */ - name = argv[optind]; + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; /* Parse setting value */ - if ( ( rc = get_value ( name, &argv[ optind + 1 ], &value ) ) != 0 ) + if ( ( rc = get_value ( &setting, &argv[ optind + 1 ], &value ) ) != 0 ) goto err_get_value; - /* Determine total length of command line */ - if ( ( rc = storef_named_setting ( name, &setting_type_string, - value ) ) != 0 ) { - printf ( "Could not %s \"%s\": %s\n", - argv[0], name, strerror ( rc ) ); + /* Apply default type if necessary */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_string; + + /* Store setting */ + if ( ( rc = storef_setting ( setting.settings, &setting.setting, + value ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); goto err_store; } - free ( value ); - return 0; - err_store: free ( value ); err_get_value: + err_parse_setting: err_parse_options: return rc; } @@ -148,12 +165,13 @@ static int set_core_exec ( int argc, char **argv, /** * Get setting value for "set" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int set_value ( const char *name __unused, char **args, char **value ) { +static int set_value ( struct named_setting *setting __unused, + char **args, char **value ) { *value = concat_args ( args ); if ( ! *value ) @@ -176,13 +194,13 @@ static int set_exec ( int argc, char **argv ) { /** * Get setting value for "clear" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int clear_value ( const char *name __unused, char **args __unused, - char **value ) { +static int clear_value ( struct named_setting *setting __unused, + char **args __unused, char **value ) { *value = NULL; return 0; @@ -202,29 +220,27 @@ static int clear_exec ( int argc, char **argv ) { /** * Get setting value for "read" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int read_value ( const char *name, char **args __unused, char **value ) { +static int read_value ( struct named_setting *setting, char **args __unused, + char **value ) { char *existing; int rc; - /* Read existing value */ - if ( ( rc = fetchf_named_setting_copy ( name, &existing ) ) < 0 ) - goto err_existing; + /* Read existing value, treating errors as equivalent to an + * empty initial setting. + */ + fetchf_setting_copy ( setting->settings, &setting->setting, &existing ); /* Read new value */ if ( ( rc = readline_history ( NULL, existing, NULL, value ) ) != 0 ) - goto err_new; + goto err_readline; - /* Success */ - rc = 0; - - err_new: + err_readline: free ( existing ); - err_existing: return rc; } From bd6c3a1886faaab0028e099db9a64c16dea93f33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Jul 2013 14:36:00 +0100 Subject: [PATCH 51/65] [settings] Remove now-unused fetchf_named_setting() and storef_named_setting() Signed-off-by: Michael Brown --- src/core/settings.c | 114 ------------------------------------ src/include/ipxe/settings.h | 7 --- 2 files changed, 121 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index ab44dea9..76d7f6a6 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1316,120 +1316,6 @@ int setting_name ( struct settings *settings, struct setting *setting, setting->name, setting->type->name ); } -/** - * Parse and store value of named setting - * - * @v name Name of setting - * @v default_type Default type to use, if none specified - * @v value Formatted setting data, or NULL - * @ret rc Return status code - */ -int storef_named_setting ( const char *name, struct setting_type *default_type, - const char *value ) { - struct settings *settings; - struct setting setting; - char tmp_name[ strlen ( name ) + 1 ]; - int rc; - - /* Create modifiable copy of setting name */ - strcpy ( tmp_name, name ); - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( tmp_name, autovivify_child_settings, - &settings, &setting ) ) != 0 ) - return rc; - - /* Apply default type if necessary */ - if ( ! setting.type ) - setting.type = default_type; - - /* Store setting */ - if ( ( rc = storef_setting ( settings, &setting, value ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Fetch and format value of named setting - * - * @v name Name of setting - * @v name_buf Buffer to contain canonicalised name - * @v name_len Length of canonicalised name buffer - * @v value_buf Buffer to contain formatted value - * @v value_len Length of formatted value buffer - * @ret len Length of formatted value, or negative error - */ -int fetchf_named_setting ( const char *name, - char *name_buf, size_t name_len, - char *value_buf, size_t value_len ) { - struct settings *settings; - struct setting setting; - struct settings *origin; - char tmp_name[ strlen ( name ) + 1 ]; - int len; - int rc; - - /* Create modifiable copy of setting name */ - strcpy ( tmp_name, name ); - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( tmp_name, find_child_settings, - &settings, &setting ) ) != 0 ) - return rc; - - /* Fetch setting */ - if ( ( len = fetchf_setting ( settings, &setting, value_buf, - value_len ) ) < 0 ) - return len; - - /* Construct setting name */ - origin = fetch_setting_origin ( settings, &setting ); - assert ( origin != NULL ); - setting_name ( origin, &setting, name_buf, name_len ); - - return len; -} - -/** - * Fetch and format copy of value of named setting - * - * @v name Name of setting - * @v data Buffer to allocate and fill with formatted value - * @ret len Length of formatted value, or negative error - * - * The caller is responsible for eventually freeing the allocated - * buffer. - * - * To allow the caller to distinguish between a non-existent setting - * and an error in allocating memory for the copy, this function will - * return success (and a NULL buffer pointer) for a non-existent - * setting. - */ -int fetchf_named_setting_copy ( const char *name, char **data ) { - int len; - int check_len; - - /* Avoid returning uninitialised data on error */ - *data = NULL; - - /* Fetch formatted value length, and return success if non-existent */ - len = fetchf_named_setting ( name, NULL, 0, NULL, 0 ); - if ( len < 0 ) - return 0; - - /* Allocate buffer */ - *data = malloc ( len + 1 /* NUL */ ); - if ( ! *data ) - return -ENOMEM; - - /* Fetch formatted value */ - check_len = fetchf_named_setting ( name, NULL, 0, *data, - ( len + 1 /* NUL */ ) ); - assert ( check_len == len ); - return len; -} - /****************************************************************************** * * Setting types diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 2faf4d79..c7287ebb 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -315,13 +315,6 @@ extern int fetchf_setting_copy ( struct settings *settings, extern int storef_setting ( struct settings *settings, struct setting *setting, const char *value ); -extern int storef_named_setting ( const char *name, - struct setting_type *default_type, - const char *value ); -extern int fetchf_named_setting ( const char *name, char *name_buf, - size_t name_len, char *value_buf, - size_t value_len ); -extern int fetchf_named_setting_copy ( const char *name, char **data ); extern char * expand_settings ( const char *string ); extern struct setting_type setting_type_string __setting_type; From 7fc18ea8ab845da3e252abb7d78060182b0342a3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 23 Jul 2013 14:46:45 +0100 Subject: [PATCH 52/65] [script] Allow initial whitespace on lines containing labels Initial whitespace is already accepted on lines containing commands, since it gets ignored by the system() call. Minimise surprise and allow for neater indentation of scripts by also allowing whitespace on lines containing labels. Signed-off-by: Michael Brown --- src/image/script.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/image/script.c b/src/image/script.c index ceccd901..d30f2613 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -136,6 +136,26 @@ static int terminate_on_exit_or_failure ( int rc ) { ( rc != 0 ) ); } +/** + * Find label within script line + * + * @v line Line of script + * @ret label Start of label name, or NULL if not found + */ +static const char * find_label ( const char *line ) { + + /* Skip any leading whitespace */ + while ( isspace ( *line ) ) + line++; + + /* If first non-whitespace character is a ':', then we have a label */ + if ( *line == ':' ) { + return ( line + 1 ); + } else { + return NULL; + } +} + /** * Execute script line * @@ -146,7 +166,7 @@ static int script_exec_line ( const char *line ) { int rc; /* Skip label lines */ - if ( line[0] == ':' ) + if ( find_label ( line ) != NULL ) return 0; /* Execute command */ @@ -252,14 +272,19 @@ static const char *goto_label; */ static int goto_find_label ( const char *line ) { size_t len = strlen ( goto_label ); + const char *label; - if ( line[0] != ':' ) + /* Find label */ + label = find_label ( line ); + if ( ! label ) return -ENOENT; - if ( strncmp ( goto_label, &line[1], len ) != 0 ) + /* Check if label matches */ + if ( strncmp ( goto_label, label, len ) != 0 ) return -ENOENT; - if ( line[ 1 + len ] && ! isspace ( line[ 1 + len ] ) ) + /* Check label is terminated by a NUL or whitespace */ + if ( label[len] && ! isspace ( label[len] ) ) return -ENOENT; return 0; From 31f5211035cf0434a1b5043f707ecd2316d4a18c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 23 Jul 2013 16:49:12 +0100 Subject: [PATCH 53/65] [script] Allow commands following a script label Allow commands to be placed on the same line as a label. This allows for improved legibility of loop constructions by incorporating the loop check condition into the same line as the loop label. For example, to iterate over network devices using the forthcoming "inc" command: set idx:int16 0 :loop isset ${net${idx}/mac} || goto loop_done echo net${idx} is a ${net${idx}/chip} with MAC ${net${idx}/mac} inc idx && goto loop :loop_done Signed-off-by: Michael Brown --- src/image/script.c | 96 +++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/image/script.c b/src/image/script.c index d30f2613..881d51f3 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -55,19 +55,27 @@ static size_t script_offset; * @ret rc Return status code */ static int process_script ( struct image *image, - int ( * process_line ) ( const char *line ), + int ( * process_line ) ( struct image *image, + size_t offset, + const char *label, + const char *command ), int ( * terminate ) ( int rc ) ) { size_t len = 0; char *line = NULL; + size_t line_offset; + char *label; + char *command; off_t eol; size_t frag_len; char *tmp; int rc; + /* Initialise script and line offsets */ script_offset = 0; + line_offset = 0; do { - + /* Find length of next line, excluding any terminating '\n' */ eol = memchr_user ( image->data, script_offset, '\n', ( image->len - script_offset ) ); @@ -104,10 +112,23 @@ static int process_script ( struct image *image, /* Terminate line */ line[len] = '\0'; - DBG ( "$ %s\n", line ); + + /* Split line into (optional) label and command */ + command = line; + while ( isspace ( *command ) ) + command++; + if ( *command == ':' ) { + label = ++command; + while ( *command && ! isspace ( *command ) ) + command++; + if ( *command ) + *(command++) = '\0'; + } else { + label = NULL; + } /* Process line */ - rc = process_line ( line ); + rc = process_line ( image, line_offset, label, command ); if ( terminate ( rc ) ) goto err_process; @@ -116,6 +137,9 @@ static int process_script ( struct image *image, line = NULL; len = 0; + /* Update line offset */ + line_offset = script_offset; + } while ( script_offset < image->len ); err_process: @@ -136,41 +160,24 @@ static int terminate_on_exit_or_failure ( int rc ) { ( rc != 0 ) ); } -/** - * Find label within script line - * - * @v line Line of script - * @ret label Start of label name, or NULL if not found - */ -static const char * find_label ( const char *line ) { - - /* Skip any leading whitespace */ - while ( isspace ( *line ) ) - line++; - - /* If first non-whitespace character is a ':', then we have a label */ - if ( *line == ':' ) { - return ( line + 1 ); - } else { - return NULL; - } -} - /** * Execute script line * - * @v line Line of script + * @v image Script + * @v offset Offset within script + * @v label Label, or NULL + * @v command Command * @ret rc Return status code */ -static int script_exec_line ( const char *line ) { +static int script_exec_line ( struct image *image, size_t offset, + const char *label __unused, + const char *command ) { int rc; - /* Skip label lines */ - if ( find_label ( line ) != NULL ) - return 0; + DBGC ( image, "[%04zx] $ %s\n", offset, command ); /* Execute command */ - if ( ( rc = system ( line ) ) != 0 ) + if ( ( rc = system ( command ) ) != 0 ) return rc; return 0; @@ -224,7 +231,7 @@ static int script_probe ( struct image *image ) { /* Sanity check */ if ( image->len < sizeof ( test ) ) { - DBG ( "Too short to be a script\n" ); + DBGC ( image, "Too short to be a script\n" ); return -ENOEXEC; } @@ -233,7 +240,7 @@ static int script_probe ( struct image *image ) { if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) || ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) && isspace ( test[ sizeof ( test ) - 1 ] ) ) ) { - DBG ( "Invalid magic signature\n" ); + DBGC ( image, "Invalid magic signature\n" ); return -ENOEXEC; } @@ -267,25 +274,26 @@ static const char *goto_label; /** * Check for presence of label * - * @v line Script line + * @v image Script + * @v offset Offset within script + * @v label Label + * @v command Command * @ret rc Return status code */ -static int goto_find_label ( const char *line ) { - size_t len = strlen ( goto_label ); - const char *label; +static int goto_find_label ( struct image *image, size_t offset, + const char *label, const char *command __unused ) { - /* Find label */ - label = find_label ( line ); + /* Check label exists */ if ( ! label ) return -ENOENT; - /* Check if label matches */ - if ( strncmp ( goto_label, label, len ) != 0 ) + /* Check label matches */ + if ( strcmp ( goto_label, label ) != 0 ) return -ENOENT; - /* Check label is terminated by a NUL or whitespace */ - if ( label[len] && ! isspace ( label[len] ) ) - return -ENOENT; + /* Update script offset */ + script_offset = offset; + DBGC ( image, "[%04zx] Gone to :%s\n", offset, label ); return 0; } @@ -331,6 +339,8 @@ static int goto_exec ( int argc, char **argv ) { if ( ( rc = process_script ( current_image, goto_find_label, terminate_on_label_found ) ) != 0 ) { script_offset = saved_offset; + DBGC ( current_image, "[%04zx] No such label :%s\n", + script_offset, goto_label ); return rc; } From c70d4cb1b39eae7b026d34575431fe8a54c65998 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 1 Aug 2013 14:39:58 +0100 Subject: [PATCH 54/65] [settings] Introduce the generalised concept of a numeric setting Signed-off-by: Michael Brown --- src/arch/i386/interface/vmware/guestinfo.c | 2 +- src/core/settings.c | 601 ++++++++++++++------- src/include/ipxe/settings.h | 45 +- src/tests/settings_test.c | 91 +++- 4 files changed, 549 insertions(+), 190 deletions(-) diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/i386/interface/vmware/guestinfo.c index 8ce363aa..7fa41b86 100644 --- a/src/arch/i386/interface/vmware/guestinfo.c +++ b/src/arch/i386/interface/vmware/guestinfo.c @@ -114,7 +114,7 @@ static int guestinfo_fetch_type ( struct settings *settings, settings, &command[9] /* Skip "info-get " */, info ); /* Parse GuestInfo value according to type */ - ret = type->parse ( info, data, len ); + ret = setting_parse ( type, info, data, len ); if ( ret < 0 ) { DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: " "%s\n", settings, info, type->name, strerror ( ret ) ); diff --git a/src/core/settings.c b/src/core/settings.c index 76d7f6a6..889e1078 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -850,15 +850,14 @@ int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, /** * Extract numeric value of setting * + * @v is_signed Treat value as a signed integer * @v raw Raw setting data * @v len Length of raw setting data - * @ret signed_value Value, when interpreted as a signed integer - * @ret unsigned_value Value, when interpreted as an unsigned integer + * @ret value Numeric value * @ret len Length of setting, or negative error */ -static int numeric_setting_value ( const void *raw, size_t len, - signed long *signed_value, - unsigned long *unsigned_value ) { +static int numeric_setting_value ( int is_signed, const void *raw, size_t len, + unsigned long *value ) { const uint8_t *unsigned_bytes = raw; const int8_t *signed_bytes = raw; int is_negative; @@ -871,17 +870,40 @@ static int numeric_setting_value ( const void *raw, size_t len, /* Convert to host-ordered longs */ is_negative = ( len && ( signed_bytes[0] < 0 ) ); - *signed_value = ( is_negative ? -1L : 0 ); - *unsigned_value = 0; + *value = ( ( is_signed && is_negative ) ? -1L : 0 ); for ( i = 0 ; i < len ; i++ ) { byte = unsigned_bytes[i]; - *signed_value = ( ( *signed_value << 8 ) | byte ); - *unsigned_value = ( ( *unsigned_value << 8 ) | byte ); + *value = ( ( *value << 8 ) | byte ); } return len; } +/** + * Fetch value of numeric setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_numeric_setting ( struct settings *settings, struct setting *setting, + unsigned long *value, int is_signed ) { + unsigned long tmp; + int len; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch raw (network-ordered, variable-length) setting */ + len = fetch_setting ( settings, setting, &tmp, sizeof ( tmp ) ); + if ( len < 0 ) + return len; + + /* Extract numeric value */ + return numeric_setting_value ( is_signed, &tmp, len, value ); +} + /** * Fetch value of signed integer setting * @@ -892,20 +914,9 @@ static int numeric_setting_value ( const void *raw, size_t len, */ int fetch_int_setting ( struct settings *settings, struct setting *setting, long *value ) { - unsigned long dummy; - long tmp; - int len; - /* Avoid returning uninitialised data on error */ - *value = 0; - - /* Fetch raw (network-ordered, variable-length) setting */ - len = fetch_setting ( settings, setting, &tmp, sizeof ( tmp ) ); - if ( len < 0 ) - return len; - - /* Extract numeric value */ - return numeric_setting_value ( &tmp, len, value, &dummy ); + return fetch_numeric_setting ( settings, setting, + ( ( unsigned long * ) value ), 1 ); } /** @@ -918,20 +929,8 @@ int fetch_int_setting ( struct settings *settings, struct setting *setting, */ int fetch_uint_setting ( struct settings *settings, struct setting *setting, unsigned long *value ) { - signed long dummy; - long tmp; - int len; - /* Avoid returning uninitialised data on error */ - *value = 0; - - /* Fetch raw (network-ordered, variable-length) setting */ - len = fetch_setting ( settings, setting, &tmp, sizeof ( tmp ) ); - if ( len < 0 ) - return len; - - /* Extract numeric value */ - return numeric_setting_value ( &tmp, len, &dummy, value ); + return fetch_numeric_setting ( settings, setting, value, 0 ); } /** @@ -942,9 +941,9 @@ int fetch_uint_setting ( struct settings *settings, struct setting *setting, * @ret value Setting value, or zero */ long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ - long value; + unsigned long value; - fetch_int_setting ( settings, setting, &value ); + fetch_numeric_setting ( settings, setting, &value, 1 ); return value; } @@ -959,7 +958,7 @@ unsigned long fetch_uintz_setting ( struct settings *settings, struct setting *setting ) { unsigned long value; - fetch_uint_setting ( settings, setting, &value ); + fetch_numeric_setting ( settings, setting, &value, 0 ); return value; } @@ -1027,12 +1026,88 @@ int setting_cmp ( struct setting *a, struct setting *b ) { ****************************************************************************** */ +/** + * Format setting value as a string + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +int setting_format ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->format ) + return -ENOTSUP; + + return type->format ( type, raw, raw_len, buf, len ); +} + +/** + * Parse formatted string to setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +int setting_parse ( struct setting_type *type, const char *value, + void *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->parse ) + return -ENOTSUP; + + return type->parse ( type, value, buf, len ); +} + +/** + * Convert setting value to number + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @ret value Numeric value + * @ret rc Return status code + */ +int setting_numerate ( struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ) { + + /* Sanity check */ + if ( ! type->numerate ) + return -ENOTSUP; + + return type->numerate ( type, raw, raw_len, value ); +} + +/** + * Convert number to setting value + * + * @v type Setting type + * @v value Numeric value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +int setting_denumerate ( struct setting_type *type, unsigned long value, + void *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->denumerate ) + return -ENOTSUP; + + return type->denumerate ( type, value, buf, len ); +} + /** * Fetch formatted value of setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch - * @v type Settings type * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error @@ -1052,10 +1127,10 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, /* Sanity check */ assert ( setting->type != NULL ); - assert ( setting->type->format != NULL ); /* Format setting */ - if ( ( ret = setting->type->format ( raw, raw_len, buf, len ) ) < 0 ) + if ( ( ret = setting_format ( setting->type, raw, raw_len, buf, + len ) ) < 0 ) goto err_format; err_format: @@ -1069,7 +1144,6 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch - * @v type Settings type * @v value Buffer to allocate and fill with formatted value * @ret len Length of formatted value, or negative error * @@ -1122,13 +1196,12 @@ int storef_setting ( struct settings *settings, struct setting *setting, /* Sanity check */ assert ( setting->type != NULL ); - assert ( setting->type->parse != NULL ); /* Get raw value length */ - raw_len = setting->type->parse ( value, NULL, 0 ); + raw_len = setting_parse ( setting->type, value, NULL, 0 ); if ( raw_len < 0 ) { rc = raw_len; - goto err_parse_len; + goto err_raw_len; } /* Allocate buffer for raw value */ @@ -1139,7 +1212,7 @@ int storef_setting ( struct settings *settings, struct setting *setting, } /* Parse formatted value */ - check_len = setting->type->parse ( value, raw, raw_len ); + check_len = setting_parse ( setting->type, value, raw, raw_len ); assert ( check_len == raw_len ); /* Store raw value */ @@ -1149,7 +1222,89 @@ int storef_setting ( struct settings *settings, struct setting *setting, err_store: free ( raw ); err_alloc_raw: - err_parse_len: + err_raw_len: + return rc; +} + +/** + * Fetch numeric value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Numeric value to fill in + * @ret rc Return status code + */ +int fetchn_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ) { + void *raw; + int raw_len; + int rc; + + /* Fetch raw value */ + raw_len = fetch_setting_copy ( settings, setting, &raw ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_fetch_copy; + } + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Numerate setting */ + if ( ( rc = setting_numerate ( setting->type, raw, raw_len, + value ) ) < 0 ) + goto err_numerate; + + err_numerate: + free ( raw ); + err_fetch_copy: + return rc; +} + +/** + * Store numeric value of setting + * + * @v settings Settings block + * @v setting Setting + * @v value Numeric value + * @ret rc Return status code + */ +int storen_setting ( struct settings *settings, struct setting *setting, + unsigned long value ) { + void *raw; + int raw_len; + int check_len; + int rc; + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Get raw value length */ + raw_len = setting_denumerate ( setting->type, value, NULL, 0 ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_raw_len; + } + + /* Allocate buffer for raw value */ + raw = malloc ( raw_len ); + if ( ! raw ) { + rc = -ENOMEM; + goto err_alloc_raw; + } + + /* Denumerate value */ + check_len = setting_denumerate ( setting->type, value, raw, raw_len ); + assert ( check_len == raw_len ); + + /* Store raw value */ + if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) + goto err_store; + + err_store: + free ( raw ); + err_alloc_raw: + err_raw_len: return rc; } @@ -1326,12 +1481,14 @@ int setting_name ( struct settings *settings, struct setting *setting, /** * Parse string setting value * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @ret len Length of raw value, or negative error */ -static int parse_string_setting ( const char *value, void *buf, size_t len ) { +static int parse_string_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { size_t raw_len = strlen ( value ); /* Exclude terminating NUL */ /* Copy string to buffer */ @@ -1345,13 +1502,15 @@ static int parse_string_setting ( const char *value, void *buf, size_t len ) { /** * Format string setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_string_setting ( const void *raw, size_t raw_len, char *buf, +static int format_string_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { /* Copy string to buffer, and terminate */ @@ -1373,13 +1532,14 @@ struct setting_type setting_type_string __setting_type = { /** * Parse URI-encoded string setting value * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @ret len Length of raw value, or negative error */ -static int parse_uristring_setting ( const char *value, void *buf, - size_t len ) { +static int parse_uristring_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ){ char tmp[ len + 1 /* NUL */ ]; size_t raw_len; @@ -1397,13 +1557,15 @@ static int parse_uristring_setting ( const char *value, void *buf, /** * Format URI-encoded string setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_uristring_setting ( const void *raw, size_t raw_len, +static int format_uristring_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { char tmp[ raw_len + 1 /* NUL */ ]; @@ -1425,12 +1587,14 @@ struct setting_type setting_type_uristring __setting_type = { /** * Parse IPv4 address setting value * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @ret len Length of raw value, or negative error */ -static int parse_ipv4_setting ( const char *value, void *buf, size_t len ) { +static int parse_ipv4_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { struct in_addr ipv4; /* Parse IPv4 address */ @@ -1448,13 +1612,15 @@ static int parse_ipv4_setting ( const char *value, void *buf, size_t len ) { /** * Format IPv4 address setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_ipv4_setting ( const void *raw, size_t raw_len, char *buf, +static int format_ipv4_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { const struct in_addr *ipv4 = raw; @@ -1471,28 +1637,100 @@ struct setting_type setting_type_ipv4 __setting_type = { }; /** - * Parse integer setting value + * Integer setting type indices * - * @v value Formatted setting value + * These indexes are defined such that (1<name - setting_type_int_name[0] ) / + sizeof ( setting_type_int_name[0] ) ); +} + +/** + * Get integer setting type width + * + * @v type Setting type + * @ret index Integer setting type width + */ +static unsigned int setting_type_int_width ( struct setting_type *type ) { + + return ( 1 << setting_type_int_index ( type ) ); +} + +/** + * Get integer setting type signedness + * + * @v type Setting type + * @ret is_signed Integer setting type is signed + */ +static int setting_type_int_is_signed ( struct setting_type *type ) { + return ( ( type->name - setting_type_int_name[0] ) & 1 ); +} + +/** + * Convert number to setting value + * + * @v type Setting type + * @v value Numeric value * @v buf Buffer to contain raw value * @v len Length of buffer - * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_int_setting ( const char *value, void *buf, size_t len, - unsigned int size ) { +static int denumerate_int_setting ( struct setting_type *type, + unsigned long value, void *buf, + size_t len ) { + unsigned int size = setting_type_int_width ( type ); union { uint32_t num; uint8_t bytes[4]; } u; - char *endp; - /* Parse value */ - u.num = htonl ( strtoul ( value, &endp, 0 ) ); - if ( *endp ) - return -EINVAL; - - /* Copy to buffer */ + u.num = htonl ( value ); if ( len > size ) len = size; memcpy ( buf, &u.bytes[ sizeof ( u ) - size ], len ); @@ -1501,64 +1739,69 @@ static int parse_int_setting ( const char *value, void *buf, size_t len, } /** - * Parse 8-bit integer setting value + * Convert setting value to number * - * @v value Formatted setting value - * @v buf Buffer to contain raw value - * @v len Length of buffer - * @v size Integer size, in bytes - * @ret len Length of raw value, or negative error + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v value Numeric value to fill in + * @ret rc Return status code */ -static int parse_int8_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint8_t ) ); +static int numerate_int_setting ( struct setting_type *type, + const void *raw, size_t raw_len, + unsigned long *value ) { + int is_signed = setting_type_int_is_signed ( type ); + int check_len; + + /* Extract numeric value */ + check_len = numeric_setting_value ( is_signed, raw, raw_len, value ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == ( int ) raw_len ); + + return 0; } /** - * Parse 16-bit integer setting value + * Parse integer setting value * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer - * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_int16_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint16_t ) ); -} +static int parse_int_setting ( struct setting_type *type, const char *value, + void *buf, size_t len ) { + char *endp; + unsigned long num_value; -/** - * Parse 32-bit integer setting value - * - * @v value Formatted setting value - * @v buf Buffer to contain raw value - * @v len Length of buffer - * @v size Integer size, in bytes - * @ret len Length of raw value, or negative error - */ -static int parse_int32_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint32_t ) ); + /* Parse value */ + num_value = strtoul ( value, &endp, 0 ); + if ( *endp ) + return -EINVAL; + + return type->denumerate ( type, num_value, buf, len ); } /** * Format signed integer setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_int_setting ( const void *raw, size_t raw_len, char *buf, - size_t len ) { - signed long value; - unsigned long dummy; - int check_len; +static int format_int_setting ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { + unsigned long value; + int ret; /* Extract numeric value */ - check_len = numeric_setting_value ( raw, raw_len, &value, &dummy ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == ( int ) raw_len ); + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; /* Format value */ return snprintf ( buf, len, "%ld", value ); @@ -1567,82 +1810,90 @@ static int format_int_setting ( const void *raw, size_t raw_len, char *buf, /** * Format unsigned integer setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_uint_setting ( const void *raw, size_t raw_len, char *buf, - size_t len ) { - signed long dummy; +static int format_uint_setting ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { unsigned long value; - int check_len; + int ret; /* Extract numeric value */ - check_len = numeric_setting_value ( raw, raw_len, &dummy, &value ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == ( int ) raw_len ); + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; /* Format value */ return snprintf ( buf, len, "%#lx", value ); } +/** + * Define a signed integer setting type + * + * @v index Integer setting type index + * @ret type Setting type + */ +#define SETTING_TYPE_INT( index ) { \ + .name = SETTING_TYPE_INT_NAME ( index ), \ + .parse = parse_int_setting, \ + .format = format_int_setting, \ + .denumerate = denumerate_int_setting, \ + .numerate = numerate_int_setting, \ +} + +/** + * Define an unsigned integer setting type + * + * @v index Integer setting type index + * @ret type Setting type + */ +#define SETTING_TYPE_UINT( index ) { \ + .name = SETTING_TYPE_UINT_NAME ( index ), \ + .parse = parse_int_setting, \ + .format = format_uint_setting, \ + .denumerate = denumerate_int_setting, \ + .numerate = numerate_int_setting, \ +} + /** A signed 8-bit integer setting type */ -struct setting_type setting_type_int8 __setting_type = { - .name = "int8", - .parse = parse_int8_setting, - .format = format_int_setting, -}; +struct setting_type setting_type_int8 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT8 ); /** A signed 16-bit integer setting type */ -struct setting_type setting_type_int16 __setting_type = { - .name = "int16", - .parse = parse_int16_setting, - .format = format_int_setting, -}; +struct setting_type setting_type_int16 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT16 ); /** A signed 32-bit integer setting type */ -struct setting_type setting_type_int32 __setting_type = { - .name = "int32", - .parse = parse_int32_setting, - .format = format_int_setting, -}; +struct setting_type setting_type_int32 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT32 ); /** An unsigned 8-bit integer setting type */ -struct setting_type setting_type_uint8 __setting_type = { - .name = "uint8", - .parse = parse_int8_setting, - .format = format_uint_setting, -}; +struct setting_type setting_type_uint8 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT8 ); /** An unsigned 16-bit integer setting type */ -struct setting_type setting_type_uint16 __setting_type = { - .name = "uint16", - .parse = parse_int16_setting, - .format = format_uint_setting, -}; +struct setting_type setting_type_uint16 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT16 ); /** An unsigned 32-bit integer setting type */ -struct setting_type setting_type_uint32 __setting_type = { - .name = "uint32", - .parse = parse_int32_setting, - .format = format_uint_setting, -}; +struct setting_type setting_type_uint32 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT32 ); /** * Format hex string setting value * + * @v delimiter Byte delimiter * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer - * @v delimiter Byte delimiter * @ret len Length of formatted value, or negative error */ -static int format_hex_setting ( const void *raw, size_t raw_len, char *buf, - size_t len, const char *delimiter ) { +static int format_hex_setting ( const char *delimiter, const void *raw, + size_t raw_len, char *buf, size_t len ) { const uint8_t *bytes = raw; int used = 0; unsigned int i; @@ -1660,40 +1911,46 @@ static int format_hex_setting ( const void *raw, size_t raw_len, char *buf, /** * Parse hex string setting value (using colon delimiter) * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_hex_setting ( const char *value, void *buf, size_t len ) { +static int parse_hex_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { return hex_decode ( value, ':', buf, len ); } /** * Format hex string setting value (using colon delimiter) * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_hex_colon_setting ( const void *raw, size_t raw_len, +static int format_hex_colon_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( raw, raw_len, buf, len, ":" ); + return format_hex_setting ( ":", raw, raw_len, buf, len ); } /** * Parse hex string setting value (using hyphen delimiter) * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_hex_hyphen_setting ( const char *value, void *buf, +static int parse_hex_hyphen_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { return hex_decode ( value, '-', buf, len ); } @@ -1701,43 +1958,48 @@ static int parse_hex_hyphen_setting ( const char *value, void *buf, /** * Format hex string setting value (using hyphen delimiter) * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_hex_hyphen_setting ( const void *raw, size_t raw_len, +static int format_hex_hyphen_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( raw, raw_len, buf, len, "-" ); + return format_hex_setting ( "-", raw, raw_len, buf, len ); } /** * Parse hex string setting value (using no delimiter) * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_hex_raw_setting ( const char *value, void *buf, - size_t len ) { +static int parse_hex_raw_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { return hex_decode ( value, 0, buf, len ); } /** * Format hex string setting value (using no delimiter) * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_hex_raw_setting ( const void *raw, size_t raw_len, +static int format_hex_raw_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( raw, raw_len, buf, len, "" ); + return format_hex_setting ( "", raw, raw_len, buf, len ); } /** A hex-string setting (colon-delimited) */ @@ -1761,29 +2023,18 @@ struct setting_type setting_type_hexraw __setting_type = { .format = format_hex_raw_setting, }; -/** - * Parse UUID setting value - * - * @v value Formatted setting value - * @v buf Buffer to contain raw value - * @v len Length of buffer - * @ret len Length of raw value, or negative error - */ -static int parse_uuid_setting ( const char *value __unused, - void *buf __unused, size_t len __unused ) { - return -ENOTSUP; -} - /** * Format UUID setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_uuid_setting ( const void *raw, size_t raw_len, char *buf, +static int format_uuid_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { const union uuid *uuid = raw; @@ -1798,40 +2049,27 @@ static int format_uuid_setting ( const void *raw, size_t raw_len, char *buf, /** UUID setting type */ struct setting_type setting_type_uuid __setting_type = { .name = "uuid", - .parse = parse_uuid_setting, .format = format_uuid_setting, }; -/** - * Parse PCI bus:dev.fn setting value - * - * @v value Formatted setting value - * @v buf Buffer to contain raw value - * @v len Length of buffer - * @ret len Length of raw value, or negative error - */ -static int parse_busdevfn_setting ( const char *value __unused, - void *buf __unused, size_t len __unused ) { - return -ENOTSUP; -} - /** * Format PCI bus:dev.fn setting value * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_busdevfn_setting ( const void *raw, size_t raw_len, char *buf, +static int format_busdevfn_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { - signed long dummy; unsigned long busdevfn; int check_len; /* Extract numeric value */ - check_len = numeric_setting_value ( raw, raw_len, &dummy, &busdevfn ); + check_len = numeric_setting_value ( 0, raw, raw_len, &busdevfn ); if ( check_len < 0 ) return check_len; assert ( check_len == ( int ) raw_len ); @@ -1844,7 +2082,6 @@ static int format_busdevfn_setting ( const void *raw, size_t raw_len, char *buf, /** PCI bus:dev.fn setting type */ struct setting_type setting_type_busdevfn __setting_type = { .name = "busdevfn", - .parse = parse_busdevfn_setting, .format = format_busdevfn_setting, }; diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index c7287ebb..d1666e1d 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -185,24 +185,47 @@ struct setting_type { * This is the name exposed to the user (e.g. "string"). */ const char *name; - /** Parse formatted setting value + /** Parse formatted string to setting value * + * @v type Setting type * @v value Formatted setting value * @v buf Buffer to contain raw value * @v len Length of buffer * @ret len Length of raw value, or negative error */ - int ( * parse ) ( const char *value, void *buf, size_t len ); - /** Format setting value + int ( * parse ) ( struct setting_type *type, const char *value, + void *buf, size_t len ); + /** Format setting value as a string * + * @v type Setting type * @v raw Raw setting value * @v raw_len Length of raw setting value * @v buf Buffer to contain formatted value * @v len Length of buffer * @ret len Length of formatted value, or negative error */ - int ( * format ) ( const void *raw, size_t raw_len, char *buf, - size_t len ); + int ( * format ) ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ); + /** Convert number to setting value + * + * @v type Setting type + * @v value Numeric value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ + int ( * denumerate ) ( struct setting_type *type, unsigned long value, + void *buf, size_t len ); + /** Convert setting value to number + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v value Numeric value to fill in + * @ret rc Return status code + */ + int ( * numerate ) ( struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ); }; /** Configuration setting type table */ @@ -308,6 +331,14 @@ extern int parse_setting_name ( char *name, get_child_settings_t get_child, struct setting *setting ); extern int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ); +extern int setting_format ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ); +extern int setting_parse ( struct setting_type *type, const char *value, + void *buf, size_t len ); +extern int setting_numerate ( struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ); +extern int setting_denumerate ( struct setting_type *type, unsigned long value, + void *buf, size_t len ); extern int fetchf_setting ( struct settings *settings, struct setting *setting, char *buf, size_t len ); extern int fetchf_setting_copy ( struct settings *settings, @@ -315,6 +346,10 @@ extern int fetchf_setting_copy ( struct settings *settings, extern int storef_setting ( struct settings *settings, struct setting *setting, const char *value ); +extern int fetchn_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ); +extern int storen_setting ( struct settings *settings, struct setting *setting, + unsigned long value ); extern char * expand_settings ( const char *string ); extern struct setting_type setting_type_string __setting_type; diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 42957c7d..670d549b 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -82,12 +82,63 @@ FILE_LICENCE ( GPL2_OR_LATER ); len = fetchf_setting ( settings, setting, actual, \ sizeof ( actual ) ); \ DBGC ( settings, "Fetched %s \"%s\" from:\n", \ - (setting)->type->name, formatted ); \ + (setting)->type->name, actual ); \ DBGC_HDA ( settings, 0, raw, sizeof ( raw ) ); \ ok ( len == ( int ) ( sizeof ( actual ) - 1 ) ); \ ok ( strcmp ( actual, formatted ) == 0 ); \ } while ( 0 ) +/** + * Report a numeric-store test result + * + * @v settings Settings block + * @v setting Setting + * @v numeric Numeric value + * @v raw_array Expected raw value + */ +#define storen_ok( settings, setting, numeric, raw_array ) do { \ + const uint8_t expected[] = raw_array; \ + uint8_t actual[ sizeof ( expected ) ]; \ + int len; \ + \ + ok ( storen_setting ( settings, setting, numeric ) == 0 ); \ + len = fetch_setting ( settings, setting, actual, \ + sizeof ( actual ) ); \ + if ( len >= 0 ) { \ + DBGC ( settings, "Stored %s %#lx, got:\n", \ + (setting)->type->name, \ + ( unsigned long ) numeric ); \ + DBGC_HDA ( settings, 0, actual, len ); \ + } else { \ + DBGC ( settings, "Stored %s %#lx, got error %s\n", \ + (setting)->type->name, \ + ( unsigned long ) numeric, strerror ( len ) ); \ + } \ + ok ( len == ( int ) sizeof ( actual ) ); \ + ok ( memcmp ( actual, expected, sizeof ( actual ) ) == 0 ); \ + } while ( 0 ) + +/** + * Report a numeric-fetch test result + * + * @v settings Settings block + * @v setting Setting + * @v raw_array Raw array + * @v numeric Expected numeric value + */ +#define fetchn_ok( settings, setting, raw_array, numeric ) do { \ + const uint8_t raw[] = raw_array; \ + unsigned long actual; \ + \ + ok ( store_setting ( settings, setting, raw, \ + sizeof ( raw ) ) == 0 ); \ + ok ( fetchn_setting ( settings, setting, &actual ) == 0 ); \ + DBGC ( settings, "Fetched %s %#lx from:\n", \ + (setting)->type->name, actual ); \ + DBGC_HDA ( settings, 0, raw, sizeof ( raw ) ); \ + ok ( actual == ( unsigned long ) numeric ); \ + } while ( 0 ) + /** Test generic settings block */ struct generic_settings test_generic_settings = { .settings = { @@ -216,7 +267,7 @@ static void settings_test_exec ( void ) { fetchf_ok ( &test_settings, &test_ipv4_setting, RAW ( 212, 13, 204, 60 ), "212.13.204.60" ); - /* Integer setting types */ + /* Integer setting types (as formatted strings) */ storef_ok ( &test_settings, &test_int8_setting, "54", RAW ( 54 ) ); storef_ok ( &test_settings, &test_int8_setting, @@ -256,6 +307,42 @@ static void settings_test_exec ( void ) { fetchf_ok ( &test_settings, &test_uint32_setting, RAW ( 0xf2, 0x37, 0xb2, 0x18 ), "0xf237b218" ); + /* Integer setting types (as numeric values) */ + storen_ok ( &test_settings, &test_int8_setting, + 72, RAW ( 72 ) ); + storen_ok ( &test_settings, &test_int8_setting, + 0xabcd, RAW ( 0xcd ) ); + fetchn_ok ( &test_settings, &test_int8_setting, + RAW ( 0xfe ), -2 ); + storen_ok ( &test_settings, &test_uint8_setting, + 84, RAW ( 84 ) ); + fetchn_ok ( &test_settings, &test_uint8_setting, + RAW ( 0xfe ), 0xfe ); + storen_ok ( &test_settings, &test_int16_setting, + 0x87bd, RAW ( 0x87, 0xbd ) ); + fetchn_ok ( &test_settings, &test_int16_setting, + RAW ( 0x3d, 0x14 ), 0x3d14 ); + fetchn_ok ( &test_settings, &test_int16_setting, + RAW ( 0x80 ), -128 ); + storen_ok ( &test_settings, &test_uint16_setting, + 1, RAW ( 0x00, 0x01 ) ); + fetchn_ok ( &test_settings, &test_uint16_setting, + RAW ( 0xbd, 0x87 ), 0xbd87 ); + fetchn_ok ( &test_settings, &test_uint16_setting, + RAW ( 0x80 ), 0x0080 ); + storen_ok ( &test_settings, &test_int32_setting, + 0x0812bfd2, RAW ( 0x08, 0x12, 0xbf, 0xd2 ) ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0x43, 0x87, 0x91, 0xb4 ), 0x438791b4 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0xff, 0xff, 0xfe ), -2 ); + storen_ok ( &test_settings, &test_uint32_setting, + 0xb5927ab8, RAW ( 0xb5, 0x92, 0x7a, 0xb8 ) ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0x98, 0xab, 0x41, 0x81 ), 0x98ab4181 ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0xff, 0xff, 0xfe ), 0x00fffffe ); + /* "hex" setting type */ storef_ok ( &test_settings, &test_hex_setting, "08:12:f5:22:90:1b:4b:47:a8:30:cb:4d:67:4c:d6:76", From 2b869786c57623715618a07c2bcaba83eb76d8c4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 1 Aug 2013 14:42:28 +0100 Subject: [PATCH 55/65] [cmdline] Add "inc" command The "inc" command allows the numeric value of a setting to be incremented, allowing for the construction of simple loops within an iPXE script. Signed-off-by: Michael Brown --- src/hci/commands/nvo_cmd.c | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index dd54a84e..3fd684d0 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,73 @@ static int read_exec ( int argc, char **argv ) { return set_core_exec ( argc, argv, &clear_read_cmd, read_value ); } +/** "inc" options */ +struct inc_options {}; + +/** "inc" option list */ +static struct option_descriptor inc_opts[] = {}; + +/** "inc" command descriptor */ +static struct command_descriptor inc_cmd = + COMMAND_DESC ( struct inc_options, inc_opts, 1, 2, + " []" ); + +/** + * "inc" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int inc_exec ( int argc, char **argv ) { + struct inc_options opts; + struct named_setting setting; + unsigned int increment = 1; + unsigned long value; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &inc_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_existing_setting ( argv[optind], &setting ) ) != 0 ) + goto err_parse_setting; + + /* Parse increment (if present) */ + if ( ( ( optind + 1 ) < argc ) && + ( ( rc = parse_integer ( argv[ optind + 1 ], &increment ) ) != 0)) + goto err_parse_increment; + + /* Fetch existing setting value, if any, allowing for the fact + * that numeric settings are big-endian and variable-length. + */ + if ( ( rc = fetchn_setting ( setting.settings, &setting.setting, + &value ) ) != 0 ) { + /* Treat as a non-existent :int32 setting with a zero value */ + value = 0; + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_int32; + } + + /* Increment value */ + value += increment; + + /* Store updated setting value */ + if ( ( rc = storen_setting ( setting.settings, &setting.setting, + value ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + err_store: + err_parse_increment: + err_parse_setting: + err_parse_options: + return rc; +} + /** Non-volatile option commands */ struct command nvo_commands[] __command = { { @@ -273,4 +341,8 @@ struct command nvo_commands[] __command = { .name = "read", .exec = read_exec, }, + { + .name = "inc", + .exec = inc_exec, + }, }; From 6d910559b3855e3b09f7475a42c67b5bcf39327f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 1 Aug 2013 16:52:28 +0100 Subject: [PATCH 56/65] [pci] Add pci_find_next() to iterate over existent PCI devices Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 65 ++++++++++++++++++++++++++++-------------- src/include/ipxe/pci.h | 2 ++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 7bd353d8..4a8d00b5 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -171,8 +171,20 @@ void adjust_pci_device ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_read_config ( struct pci_device *pci ) { + uint16_t busdevfn; + uint8_t hdrtype; uint32_t tmp; + /* Ignore all but the first function on non-multifunction devices */ + if ( PCI_FUNC ( pci->busdevfn ) != 0 ) { + busdevfn = pci->busdevfn; + pci->busdevfn = PCI_FIRST_FUNC ( pci->busdevfn ); + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); + pci->busdevfn = busdevfn; + if ( ! ( hdrtype & 0x80 ) ) + return -ENODEV; + } + /* Check for physical device presence */ pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp ); if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) ) @@ -203,6 +215,32 @@ int pci_read_config ( struct pci_device *pci ) { return 0; } +/** + * Find next device on PCI bus + * + * @v pci PCI device to fill in + * @v busdevfn Starting bus:dev.fn address + * @ret busdevfn Bus:dev.fn address of next PCI device, or negative error + */ +int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { + static unsigned int end; + int rc; + + /* Determine number of PCI buses */ + if ( ! end ) + end = PCI_BUSDEVFN ( pci_num_bus(), 0, 0 ); + + /* Find next PCI device, if any */ + for ( ; busdevfn < end ; busdevfn++ ) { + memset ( pci, 0, sizeof ( *pci ) ); + pci_init ( pci, busdevfn ); + if ( ( rc = pci_read_config ( pci ) ) == 0 ) + return busdevfn; + } + + return -ENODEV; +} + /** * Find driver for PCI device * @@ -276,14 +314,10 @@ void pci_remove ( struct pci_device *pci ) { */ static int pcibus_probe ( struct root_device *rootdev ) { struct pci_device *pci = NULL; - unsigned int num_bus; - unsigned int busdevfn; - uint8_t hdrtype = 0; + int busdevfn = 0; int rc; - num_bus = pci_num_bus(); - for ( busdevfn = 0 ; busdevfn < PCI_BUSDEVFN ( num_bus, 0, 0 ) ; - busdevfn++ ) { + for ( busdevfn = 0 ; 1 ; busdevfn++ ) { /* Allocate struct pci_device */ if ( ! pci ) @@ -292,22 +326,11 @@ static int pcibus_probe ( struct root_device *rootdev ) { rc = -ENOMEM; goto err; } - memset ( pci, 0, sizeof ( *pci ) ); - pci_init ( pci, busdevfn ); - - /* Skip all but the first function on - * non-multifunction cards - */ - if ( PCI_FUNC ( busdevfn ) == 0 ) { - pci_read_config_byte ( pci, PCI_HEADER_TYPE, - &hdrtype ); - } else if ( ! ( hdrtype & 0x80 ) ) { - continue; - } - /* Read device configuration */ - if ( ( rc = pci_read_config ( pci ) ) != 0 ) - continue; + /* Find next PCI device, if any */ + busdevfn = pci_find_next ( pci, busdevfn ); + if ( busdevfn < 0 ) + break; /* Look for a driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index a6ed484f..692771eb 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -351,6 +351,7 @@ struct pci_driver { #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) #define PCI_BUSDEVFN( bus, slot, func ) \ ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) +#define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) #define PCI_BASE_CLASS( class ) ( (class) >> 16 ) #define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff ) @@ -385,6 +386,7 @@ extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ); extern int pci_read_config ( struct pci_device *pci ); +extern int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ); extern int pci_find_driver ( struct pci_device *pci ); extern int pci_probe ( struct pci_device *pci ); extern void pci_remove ( struct pci_device *pci ); From b76885165e64a836ac1e2ea987bdb1b724b7b2b3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 1 Aug 2013 16:53:17 +0100 Subject: [PATCH 57/65] [cmdline] Add "pciscan" command to allow iteration over PCI devices Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/general.h | 1 + src/hci/commands/pci_cmd.c | 114 +++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/hci/commands/pci_cmd.c diff --git a/src/config/config.c b/src/config/config.c index b17b335b..437f6133 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -260,6 +260,9 @@ REQUIRE_OBJECT ( sync_cmd ); #ifdef NSLOOKUP_CMD REQUIRE_OBJECT ( nslookup_cmd ); #endif +#ifdef PCI_CMD +REQUIRE_OBJECT ( pci_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/general.h b/src/config/general.h index 3fd7408d..645f1341 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -129,6 +129,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define REBOOT_CMD /* Reboot command */ //#define POWEROFF_CMD /* Power off command */ //#define IMAGE_TRUST_CMD /* Image trust management commands */ +//#define PCI_CMD /* PCI commands */ /* * ROM-specific options diff --git a/src/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c new file mode 100644 index 00000000..f2d69569 --- /dev/null +++ b/src/hci/commands/pci_cmd.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * PCI commands + * + */ + +/** "pciscan" options */ +struct pciscan_options {}; + +/** "pciscan" option list */ +static struct option_descriptor pciscan_opts[] = {}; + +/** "pciscan" command descriptor */ +static struct command_descriptor pciscan_cmd = + COMMAND_DESC ( struct pciscan_options, pciscan_opts, 1, 1, + "" ); + +/** + * "pciscan" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int pciscan_exec ( int argc, char **argv ) { + struct pciscan_options opts; + struct named_setting setting; + struct pci_device pci; + unsigned long prev; + int next; + int len; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &pciscan_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; + + /* Determine starting bus:dev.fn address */ + if ( ( len = fetch_uint_setting ( setting.settings, &setting.setting, + &prev ) ) < 0 ) { + /* Setting not yet defined: start searching from 00:00.0 */ + prev = 0; + } else { + /* Setting is defined: start searching from next location */ + prev++; + } + + /* Find next existent PCI device */ + if ( ( next = pci_find_next ( &pci, prev ) ) < 0 ) { + rc = next; + goto err_find_next; + } + + /* Apply default type if necessary. Use ":uint16" rather than + * ":busdevfn" to allow for easy inclusion within a + * "${pci/${location}.x.y}" constructed setting. + */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_uint16; + + /* Store setting */ + if ( ( rc = storen_setting ( setting.settings, &setting.setting, + next ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + err_store: + err_find_next: + err_parse_setting: + err_parse_options: + return rc; +} + +/** PCI commands */ +struct command pci_commands[] __command = { + { + .name = "pciscan", + .exec = pciscan_exec, + }, +}; From e972057381753a744e82e6b23d19082f9f7e46b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Aug 2013 15:52:31 +0100 Subject: [PATCH 58/65] [udp] Move high-frequency debug messages to DBGLVL_EXTRA This makes it possible to leave UDP debugging enabled in order to see interesting UDP events, without flooding the console with at least one message per packet. Signed-off-by: Michael Brown --- src/net/udp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/net/udp.c b/src/net/udp.c index 20badb7f..bae5f4a7 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -220,9 +220,9 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, udphdr->chksum = tcpip_chksum ( udphdr, len ); /* Dump debugging information */ - DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp, - ntohs ( udphdr->src ), ntohs ( udphdr->dest ), - ntohs ( udphdr->len ) ); + DBGC2 ( udp, "UDP %p TX %d->%d len %d\n", udp, + ntohs ( udphdr->src ), ntohs ( udphdr->dest ), + ntohs ( udphdr->len ) ); /* Send it to the next layer for processing */ if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev, @@ -317,8 +317,8 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, iob_pull ( iobuf, sizeof ( *udphdr ) ); /* Dump debugging information */ - DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp, - ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); + DBGC2 ( udp, "UDP %p RX %d<-%d len %zd\n", udp, + ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); /* Ignore if no matching connection found */ if ( ! udp ) { From 03506828654a67c49471580a17fc91169b1ce96e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Aug 2013 15:55:23 +0100 Subject: [PATCH 59/65] [ipv6] Rename sin_{family,port} to sin6_{family,port} in struct sockaddr_in6 Signed-off-by: Michael Brown --- src/include/ipxe/in.h | 4 ++-- src/net/icmpv6.c | 2 +- src/net/ipv6.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 20f1ce26..1208ae38 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -82,9 +82,9 @@ struct sockaddr_in6 { * * Always set to @c AF_INET6 for IPv6 addresses */ - sa_family_t sin_family; + sa_family_t sin6_family; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ - uint16_t sin_port; + uint16_t sin6_port; uint32_t sin6_flowinfo; /* Flow number */ struct in6_addr sin6_addr; /* 128-bit destination address */ uint32_t sin6_scope_id; /* Scope ID */ diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index 1a5aad3b..262ffc3f 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -47,7 +47,7 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) ); /* Solicited multicast address */ - st_dest.sin6.sin_family = AF_INET6; + st_dest.sin6.sin6_family = AF_INET6; st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02; st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 57bf94d8..d76e59cb 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -332,10 +332,10 @@ static int ipv6_rx ( struct io_buffer *iobuf, /* Construct socket address */ memset ( &src, 0, sizeof ( src ) ); - src.sin6.sin_family = AF_INET6; + src.sin6.sin6_family = AF_INET6; src.sin6.sin6_addr = ip6hdr->src; memset ( &dest, 0, sizeof ( dest ) ); - dest.sin6.sin_family = AF_INET6; + dest.sin6.sin6_family = AF_INET6; dest.sin6.sin6_addr = ip6hdr->dest; /* Strip header */ From 252d28f098bfd12df59fe147d7e8354be61a6da8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Aug 2013 15:56:54 +0100 Subject: [PATCH 60/65] [tcpip] Allow binding to unspecified privileged ports (below 1024) Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/include/ipxe/in.h | 30 ++++++++++++++++++----- src/include/ipxe/tcpip.h | 18 +++++++++++++- src/net/tcp.c | 53 +++++++++++----------------------------- src/net/tcpip.c | 44 +++++++++++++++++++++++++++++++++ src/net/udp.c | 53 ++++++++++++++-------------------------- 5 files changed, 117 insertions(+), 81 deletions(-) diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 1208ae38..eee9159f 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -59,19 +59,22 @@ struct sockaddr_in { * Always set to @c AF_INET for IPv4 addresses */ sa_family_t sin_family; + /** Flags (part of struct @c sockaddr_tcpip) */ + uint16_t sin_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin_port; /** IPv4 address */ struct in_addr sin_addr; /** Padding * - * This ensures that a struct @c sockaddr_tcpip is large - * enough to hold a socket address for any TCP/IP address - * family. + * This ensures that a struct @c sockaddr_in is large enough + * to hold a socket address for any TCP/IP address family. */ - char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t ) - - sizeof ( uint16_t ) - - sizeof ( struct in_addr ) ]; + char pad[ sizeof ( struct sockaddr ) - + ( sizeof ( sa_family_t ) /* sin_family */ + + sizeof ( uint16_t ) /* sin_flags */ + + sizeof ( uint16_t ) /* sin_port */ + + sizeof ( struct in_addr ) /* sin_addr */ ) ]; } __attribute__ (( may_alias )); /** @@ -83,11 +86,26 @@ struct sockaddr_in6 { * Always set to @c AF_INET6 for IPv6 addresses */ sa_family_t sin6_family; + /** Flags (part of struct @c sockaddr_tcpip) */ + uint16_t sin6_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin6_port; uint32_t sin6_flowinfo; /* Flow number */ struct in6_addr sin6_addr; /* 128-bit destination address */ uint32_t sin6_scope_id; /* Scope ID */ + /** Padding + * + * This ensures that a struct @c sockaddr_in6 is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ sizeof ( struct sockaddr ) - + ( sizeof ( sa_family_t ) /* sin6_family */ + + sizeof ( uint16_t ) /* sin6_flags */ + + sizeof ( uint16_t ) /* sin6_port */ + + sizeof ( uint32_t ) /* sin6_flowinfo */ + + sizeof ( struct in6_addr ) /* sin6_addr */ + + sizeof ( uint32_t ) /* sin6_scope_id */ ) ]; } __attribute__ (( may_alias )); extern int inet_aton ( const char *cp, struct in_addr *inp ); diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h index 34d7f88f..0cc688a9 100644 --- a/src/include/ipxe/tcpip.h +++ b/src/include/ipxe/tcpip.h @@ -24,6 +24,16 @@ struct net_device; */ #define TCPIP_EMPTY_CSUM 0xffff +/** TCP/IP address flags */ +enum tcpip_st_flags { + /** Bind to a privileged port (less than 1024) + * + * This value is chosen as 1024 to optimise the calculations + * in tcpip_bind(). + */ + TCPIP_BIND_PRIVILEGED = 0x0400, +}; + /** * TCP/IP socket address * @@ -33,6 +43,8 @@ struct net_device; struct sockaddr_tcpip { /** Socket address family (part of struct @c sockaddr) */ sa_family_t st_family; + /** Flags */ + uint16_t st_flags; /** TCP/IP port */ uint16_t st_port; /** Padding @@ -42,7 +54,9 @@ struct sockaddr_tcpip { * family. */ char pad[ sizeof ( struct sockaddr ) - - ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ]; + ( sizeof ( sa_family_t ) /* st_family */ + + sizeof ( uint16_t ) /* st_flags */ + + sizeof ( uint16_t ) /* st_port */ ) ]; } __attribute__ (( may_alias )); /** @@ -125,6 +139,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ); extern uint16_t tcpip_chksum ( const void *data, size_t len ); +extern int tcpip_bind ( struct sockaddr_tcpip *st_local, + int ( * available ) ( int port ) ); /* Use generic_tcpip_continue_chksum() if no architecture-specific * version is available diff --git a/src/net/tcp.c b/src/net/tcp.c index b97107fc..0e18c831 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -157,6 +157,7 @@ static LIST_HEAD ( tcp_conns ); static struct interface_descriptor tcp_xfer_desc; static void tcp_expired ( struct retry_timer *timer, int over ); static void tcp_wait_expired ( struct retry_timer *timer, int over ); +static struct tcp_connection * tcp_demux ( unsigned int local_port ); static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, uint32_t win ); @@ -226,46 +227,14 @@ tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) { */ /** - * Bind TCP connection to local port + * Check if local TCP port is available * - * @v tcp TCP connection * @v port Local port number - * @ret rc Return status code - * - * If the port is 0, the connection is assigned an available port - * between 1024 and 65535. + * @ret port Local port number, or negative error */ -static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) { - struct tcp_connection *existing; - uint16_t try_port; - unsigned int i; +static int tcp_port_available ( int port ) { - /* If no port is specified, find an available port */ - if ( ! port ) { - try_port = random(); - for ( i = 0 ; i < 65536 ; i++ ) { - try_port++; - if ( try_port < 1024 ) - continue; - if ( tcp_bind ( tcp, try_port ) == 0 ) - return 0; - } - DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp ); - return -EADDRINUSE; - } - - /* Attempt bind to local port */ - list_for_each_entry ( existing, &tcp_conns, list ) { - if ( existing->local_port == port ) { - DBGC ( tcp, "TCP %p could not bind: port %d in use\n", - tcp, port ); - return -EADDRINUSE; - } - } - tcp->local_port = port; - - DBGC ( tcp, "TCP %p bound to port %d\n", tcp, port ); - return 0; + return ( tcp_demux ( port ) ? -EADDRINUSE : port ); } /** @@ -281,7 +250,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; struct tcp_connection *tcp; - unsigned int bind_port; + int port; int rc; /* Allocate and initialise structure */ @@ -303,9 +272,15 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); /* Bind to local port */ - bind_port = ( st_local ? ntohs ( st_local->st_port ) : 0 ); - if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 ) + port = tcpip_bind ( st_local, tcp_port_available ); + if ( port < 0 ) { + rc = port; + DBGC ( tcp, "TCP %p could not bind: %s\n", + tcp, strerror ( rc ) ); goto err; + } + tcp->local_port = port; + DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port ); /* Start timer to initiate SYN */ start_timer_nodelay ( &tcp->timer ); diff --git a/src/net/tcpip.c b/src/net/tcpip.c index 8e187f7e..721a4e48 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -133,3 +134,46 @@ uint16_t generic_tcpip_continue_chksum ( uint16_t partial, uint16_t tcpip_chksum ( const void *data, size_t len ) { return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len ); } + +/** + * Bind to local TCP/IP port + * + * @v st_local Local TCP/IP socket address, or NULL + * @v available Function to check port availability + * @ret port Local port number, or negative error + */ +int tcpip_bind ( struct sockaddr_tcpip *st_local, + int ( * available ) ( int port ) ) { + uint16_t flags = 0; + uint16_t try_port = 0; + uint16_t min_port; + uint16_t max_port; + unsigned int offset; + unsigned int i; + + /* Extract parameters from local socket address */ + if ( st_local ) { + flags = st_local->st_flags; + try_port = ntohs ( st_local->st_port ); + } + + /* If an explicit port is specified, check its availability */ + if ( try_port ) + return available ( try_port ); + + /* Otherwise, find an available port in the range [1,1023] or + * [1025,65535] as appropriate. + */ + min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); + max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 ); + offset = random(); + for ( i = 0 ; i <= max_port ; i++ ) { + try_port = ( ( i + offset ) & max_port ); + if ( try_port < min_port ) + continue; + if ( available ( try_port ) < 0 ) + continue; + return try_port; + } + return -EADDRINUSE; +} diff --git a/src/net/udp.c b/src/net/udp.c index bae5f4a7..edc7488a 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -48,45 +48,19 @@ static struct interface_descriptor udp_xfer_desc; struct tcpip_protocol udp_protocol __tcpip_protocol; /** - * Bind UDP connection to local port + * Check if local UDP port is available * - * @v udp UDP connection - * @ret rc Return status code - * - * Opens the UDP connection and binds to the specified local port. If - * no local port is specified, the first available port will be used. + * @v port Local port number + * @ret port Local port number, or negative error */ -static int udp_bind ( struct udp_connection *udp ) { - struct udp_connection *existing; - static uint16_t try_port = 1023; +static int udp_port_available ( int port ) { + struct udp_connection *udp; - /* If no port specified, find the first available port */ - if ( ! udp->local.st_port ) { - while ( try_port ) { - try_port++; - if ( try_port < 1024 ) - continue; - udp->local.st_port = htons ( try_port ); - if ( udp_bind ( udp ) == 0 ) - return 0; - } - return -EADDRINUSE; - } - - /* Attempt bind to local port */ - list_for_each_entry ( existing, &udp_conns, list ) { - if ( existing->local.st_port == udp->local.st_port ) { - DBGC ( udp, "UDP %p could not bind: port %d in use\n", - udp, ntohs ( udp->local.st_port ) ); + list_for_each_entry ( udp, &udp_conns, list ) { + if ( udp->local.st_port == htons ( port ) ) return -EADDRINUSE; - } } - - /* Add to UDP connection list */ - DBGC ( udp, "UDP %p bound to port %d\n", - udp, ntohs ( udp->local.st_port ) ); - - return 0; + return port; } /** @@ -104,6 +78,7 @@ static int udp_open_common ( struct interface *xfer, struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; struct udp_connection *udp; + int port; int rc; /* Allocate and initialise structure */ @@ -120,8 +95,16 @@ static int udp_open_common ( struct interface *xfer, /* Bind to local port */ if ( ! promisc ) { - if ( ( rc = udp_bind ( udp ) ) != 0 ) + port = tcpip_bind ( st_local, udp_port_available ); + if ( port < 0 ) { + rc = port; + DBGC ( udp, "UDP %p could not bind: %s\n", + udp, strerror ( rc ) ); goto err; + } + udp->local.st_port = htons ( port ); + DBGC ( udp, "UDP %p bound to port %d\n", + udp, ntohs ( udp->local.st_port ) ); } /* Attach parent interface, transfer reference to connection From 53c01d6444d9e7d5b420c65e9486e990960aba45 Mon Sep 17 00:00:00 2001 From: Marin Hannache Date: Thu, 18 Jul 2013 14:35:30 +0200 Subject: [PATCH 61/65] [nfs] Fix an issue with the selection of a local port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Robin Smidsrød Signed-off-by: Marin Hannache Signed-off-by: Michael Brown --- src/net/oncrpc/nfs_open.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/oncrpc/nfs_open.c b/src/net/oncrpc/nfs_open.c index ff2b7404..349957ff 100644 --- a/src/net/oncrpc/nfs_open.c +++ b/src/net/oncrpc/nfs_open.c @@ -160,12 +160,12 @@ static int nfs_connect ( struct interface *intf, uint16_t port, return -EINVAL; memset ( &peer, 0, sizeof ( peer ) ); - memset ( &peer, 0, sizeof ( local ) ); + memset ( &local, 0, sizeof ( local ) ); peer.st_port = htons ( port ); /* Use a local port < 1024 to avoid using the 'insecure' option in * /etc/exports file. */ - local.st_port = htons ( 1 + ( rand() % 1023 ) ); + local.st_flags = TCPIP_BIND_PRIVILEGED; return xfer_open_named_socket ( intf, SOCK_STREAM, ( struct sockaddr * ) &peer, hostname, From 55201e2d0e60003edfd7e2c7c4c592136b000f44 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Aug 2013 19:16:30 +0100 Subject: [PATCH 62/65] [settings] Expose CPUID instruction via settings mechanism Allow CPUID values to be read using the syntax ${cpuid/.} For example, ${cpuid/2.0x80000001} will give the value of %ecx after calling CPUID with %eax=0x80000001. Values for are encoded as %eax=0, %ebx=1, %ecx=2, %edx=3. The numeric encoding is more sophisticated than described above, allowing for settings such as the CPU model (obtained by calling CPUID with %eax=0x80000002-0x80000004 inclusive and concatenating the values returned in %eax:%ebx:%ecx:%edx). See the source code for details. The "cpuvendor" and "cpumodel" settings provide easy access to these more complex CPUID settings. This functionality is intended to complement the "cpuid" command, which allows for testing individual CPUID feature bits. Signed-off-by: Michael Brown --- src/arch/x86/core/cpuid.c | 20 +- src/arch/x86/core/cpuid_settings.c | 272 ++++++++++++++++++++++++++++ src/arch/x86/include/bits/errfile.h | 1 + src/arch/x86/include/ipxe/cpuid.h | 25 +++ src/config/config.c | 3 + src/config/settings.h | 1 + 6 files changed, 303 insertions(+), 19 deletions(-) create mode 100644 src/arch/x86/core/cpuid_settings.c diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index 96f79409..5908f441 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -33,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * * @ret is_supported CPUID instruction is supported */ -static int cpuid_is_supported ( void ) { +int cpuid_is_supported ( void ) { unsigned long original; unsigned long inverted; @@ -52,24 +52,6 @@ static int cpuid_is_supported ( void ) { return ( ( original ^ inverted ) & CPUID_FLAG ); } -/** - * Issue CPUID instruction - * - * @v operation CPUID operation - * @v eax Output via %eax - * @v ebx Output via %ebx - * @v ecx Output via %ecx - * @v edx Output via %edx - */ -static inline __attribute__ (( always_inline )) void -cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, - uint32_t *edx ) { - - __asm__ ( "cpuid" - : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) - : "0" ( operation ) ); -} - /** * Get Intel-defined x86 CPU features * diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c new file mode 100644 index 00000000..e2ec9bde --- /dev/null +++ b/src/arch/x86/core/cpuid_settings.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * x86 CPUID settings + * + * CPUID settings are numerically encoded as: + * + * Bit 31 Extended function + * Bits 30-28 Unused + * Bits 27-24 Number of consecutive functions to call, minus one + * Bit 23 Return result as little-endian (used for strings) + * Bits 22-18 Unused + * Bits 17-16 Number of registers in register array, minus one + * Bits 15-8 Array of register indices. First entry in array is in + * bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx. + * Bits 7-0 Starting function number (excluding "extended" bit) + * + * This encoding scheme is designed to allow the common case of + * extracting a single register from a single function to be encoded + * using "cpuid/.", e.g. "cpuid/2.0x80000001" to + * retrieve the value of %ecx from calling CPUID with %eax=0x80000001. + */ + +/** CPUID setting tag register indices */ +enum cpuid_registers { + CPUID_EAX = 0, + CPUID_EBX = 1, + CPUID_ECX = 2, + CPUID_EDX = 3, +}; + +/** + * Construct CPUID setting tag + * + * @v function Starting function number + * @v num_functions Number of consecutive functions + * @v little_endian Return result as little-endian + * @v num_registers Number of registers in register array + * @v register1 First register in register array (or zero, if empty) + * @v register2 Second register in register array (or zero, if empty) + * @v register3 Third register in register array (or zero, if empty) + * @v register4 Fourth register in register array (or zero, if empty) + * @ret tag Setting tag + */ +#define CPUID_TAG( function, num_functions, little_endian, num_registers, \ + register1, register2, register3, register4 ) \ + ( (function) | ( ( (num_functions) - 1 ) << 24 ) | \ + ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \ + ( (register1) << 8 ) | ( (register2) << 10 ) | \ + ( (register3) << 12 ) | ( (register4) << 14 ) ) + +/** + * Extract endianness from CPUID setting tag + * + * @v tag Setting tag + * @ret little_endian Result should be returned as little-endian + */ +#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL ) + +/** + * Extract starting function number from CPUID setting tag + * + * @v tag Setting tag + * @ret function Starting function number + */ +#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL ) + +/** + * Extract number of consecutive functions from CPUID setting tag + * + * @v tag Setting tag + * @ret num_functions Number of consecutive functions + */ +#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 ) + +/** + * Extract register array from CPUID setting tag + * + * @v tag Setting tag + * @ret registers Register array + */ +#define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff ) + +/** + * Extract number of registers from CPUID setting tag + * + * @v tag Setting tag + * @ret num_registers Number of registers within register array + */ +#define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 ) + +/** CPUID settings scope */ +static struct settings_scope cpuid_settings_scope; + +/** + * Check applicability of CPUID setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int cpuid_settings_applies ( struct settings *settings __unused, + struct setting *setting ) { + + return ( setting->scope == &cpuid_settings_scope ); +} + +/** + * Fetch value of CPUID setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int cpuid_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + uint32_t function; + uint32_t max_function; + uint32_t num_functions; + uint32_t registers; + uint32_t num_registers; + uint32_t buf[4]; + uint32_t output; + uint32_t discard_b; + uint32_t discard_c; + uint32_t discard_d; + size_t frag_len; + size_t result_len = 0; + + /* Fail unless CPUID is supported */ + if ( ! cpuid_is_supported() ) { + DBGC ( settings, "CPUID not supported\n" ); + return -ENOTSUP; + } + + /* Find highest supported function number within this set */ + function = CPUID_FUNCTION ( setting->tag ); + cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b, + &discard_c, &discard_d ); + + /* Fail if maximum function number is meaningless (e.g. if we + * are attempting to call an extended function on a CPU which + * does not support them). + */ + if ( ( max_function & CPUID_AMD_CHECK_MASK ) != + ( function & CPUID_AMD_CHECK_MASK ) ) { + DBGC ( settings, "CPUID invalid maximum function\n" ); + return -ENOTSUP; + } + + /* Call each function in turn */ + num_functions = CPUID_NUM_FUNCTIONS ( setting->tag ); + for ( ; num_functions-- ; function++ ) { + + /* Fail if this function is not supported */ + if ( function > max_function ) { + DBGC ( settings, "CPUID function %#08x not supported\n", + function ); + return -ENOTSUP; + } + + /* Issue CPUID */ + cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX], + &buf[CPUID_ECX], &buf[CPUID_EDX] ); + DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n", + function, buf[0], buf[1], buf[2], buf[3] ); + + /* Copy results to buffer */ + registers = CPUID_REGISTERS ( setting->tag ); + num_registers = CPUID_NUM_REGISTERS ( setting->tag ); + for ( ; num_registers-- ; registers >>= 2 ) { + output = buf[ registers & 0x3 ]; + if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) ) + output = cpu_to_be32 ( output ); + frag_len = sizeof ( output ); + if ( frag_len > len ) + frag_len = len; + memcpy ( data, &output, frag_len ); + data += frag_len; + len -= frag_len; + result_len += sizeof ( output ); + } + } + + /* Set type if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return result_len; +} + +/** CPUID settings operations */ +static struct settings_operations cpuid_settings_operations = { + .applies = cpuid_settings_applies, + .fetch = cpuid_settings_fetch, +}; + +/** CPUID settings */ +static struct settings cpuid_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ), + .children = LIST_HEAD_INIT ( cpuid_settings.children ), + .op = &cpuid_settings_operations, + .default_scope = &cpuid_settings_scope, +}; + +/** Initialise CPUID settings */ +static void cpuid_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &cpuid_settings, NULL, + "cpuid" ) ) != 0 ) { + DBG ( "CPUID could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** CPUID settings initialiser */ +struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = cpuid_settings_init, +}; + +/** CPUID predefined settings */ +struct setting cpuid_predefined_settings[] __setting ( SETTING_HOST_EXTRA ) = { + { + .name = "cpuvendor", + .description = "CPU vendor", + .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3, + CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ), + .type = &setting_type_string, + .scope = &cpuid_settings_scope, + }, + { + .name = "cpumodel", + .description = "CPU model", + .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4, + CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ), + .type = &setting_type_string, + .scope = &cpuid_settings_scope, + }, +}; diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index bfd22385..d9cf5c45 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) +#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) /** @} */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index 9705137c..2f78dfca 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -30,6 +30,9 @@ struct x86_features { /** CPUID support flag */ #define CPUID_FLAG 0x00200000UL +/** CPUID extended function */ +#define CPUID_EXTENDED 0x80000000UL + /** Get vendor ID and largest standard function */ #define CPUID_VENDOR_ID 0x00000000UL @@ -48,6 +51,28 @@ struct x86_features { /** Get extended features */ #define CPUID_AMD_FEATURES 0x80000001UL +/** Get CPU model */ +#define CPUID_MODEL 0x80000002UL + +/** + * Issue CPUID instruction + * + * @v operation CPUID operation + * @v eax Output via %eax + * @v ebx Output via %ebx + * @v ecx Output via %ecx + * @v edx Output via %edx + */ +static inline __attribute__ (( always_inline )) void +cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx ) { + + __asm__ ( "cpuid" + : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) + : "0" ( operation ) ); +} + +extern int cpuid_is_supported ( void ); extern void x86_features ( struct x86_features *features ); #endif /* _IPXE_CPUID_H */ diff --git a/src/config/config.c b/src/config/config.c index 437f6133..06c5713f 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -313,6 +313,9 @@ REQUIRE_OBJECT ( pci_settings ); #ifdef VMWARE_SETTINGS REQUIRE_OBJECT ( guestinfo ); #endif +#ifdef CPUID_SETTINGS +REQUIRE_OBJECT ( cpuid_settings ); +#endif /* * Drag in selected keyboard map diff --git a/src/config/settings.h b/src/config/settings.h index 9c5c2d20..97131e88 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define PCI_SETTINGS /* PCI device settings */ +//#define CPUID_SETTINGS /* CPUID settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ #include From d105627928b90c2031894e1771098848cc74a7c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 12 Aug 2013 16:34:10 +0100 Subject: [PATCH 63/65] [test] Add self-tests for snprintf() Signed-off-by: Michael Brown --- src/tests/tests.c | 1 + src/tests/vsprintf_test.c | 102 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/tests/vsprintf_test.c diff --git a/src/tests/tests.c b/src/tests/tests.c index 7c99a527..f965e6e3 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Drag in all applicable self-tests */ REQUIRE_OBJECT ( memcpy_test ); REQUIRE_OBJECT ( string_test ); +REQUIRE_OBJECT ( vsprintf_test ); REQUIRE_OBJECT ( list_test ); REQUIRE_OBJECT ( byteswap_test ); REQUIRE_OBJECT ( base64_test ); diff --git a/src/tests/vsprintf_test.c b/src/tests/vsprintf_test.c new file mode 100644 index 00000000..11512ec8 --- /dev/null +++ b/src/tests/vsprintf_test.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * vsprintf() self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include + +/** + * Report an snprintf() test result + * + */ +#define snprintf_ok( len, result, format, ... ) do { \ + char actual[ (len) ]; \ + const char expected[] = result; \ + size_t actual_len; \ + \ + actual_len = snprintf ( actual, sizeof ( actual ), \ + format, ##__VA_ARGS__ ); \ + ok ( actual_len >= strlen ( result ) ); \ + ok ( strcmp ( actual, expected ) == 0 ); \ + if ( strcmp ( actual, expected ) != 0 ) { \ + DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", \ + expected, actual ); \ + } \ + } while ( 0 ) + +/** + * Perform vsprintf() self-tests + * + */ +static void vsprintf_test_exec ( void ) { + + /* Constant string */ + snprintf_ok ( 16, "Testing", "Testing" ); + + /* Constant string, truncated to fit */ + snprintf_ok ( 5, "Test", "Testing" ); + + /* Basic format specifiers */ + snprintf_ok ( 16, "%", "%%" ); + snprintf_ok ( 16, "ABC", "%c%c%c", 'A', 'B', 'C' ); + snprintf_ok ( 16, "abc", "%lc%lc%lc", L'a', L'b', L'c' ); + snprintf_ok ( 16, "Hello world", "%s %s", "Hello", "world" ); + snprintf_ok ( 16, "Goodbye world", "%ls %s", L"Goodbye", "world" ); + snprintf_ok ( 16, "0x1234abcd", "%p", ( ( void * ) 0x1234abcd ) ); + snprintf_ok ( 16, "0xa723", "%#x", 0xa723 ); + snprintf_ok ( 16, "a723", "%x", 0xa723 ); + snprintf_ok ( 16, "0x0000a723", "%#08x", 0xa723 ); + snprintf_ok ( 16, "00A723", "%06X", 0xa723 ); + snprintf_ok ( 16, "9876abcd", "%lx", 0x9876abcdUL ); + snprintf_ok ( 16, "1234 5678", "%04llx %04llx", 0x1234ULL, 0x5678ULL ); + snprintf_ok ( 16, "123", "%d", 123 ); + snprintf_ok ( 16, "456", "%i", 456 ); + snprintf_ok ( 16, " 99", "%3d", 99 ); + snprintf_ok ( 16, "099", "%03d", 99 ); + snprintf_ok ( 16, "-72", "%d", -72 ); + snprintf_ok ( 16, " -72", "%4d", -72 ); + snprintf_ok ( 16, "-072", "%04d", -72 ); + snprintf_ok ( 16, "4", "%zd", sizeof ( uint32_t ) ); + snprintf_ok ( 16, "123456789", "%d", 123456789 ); + + /* Realistic combinations */ + snprintf_ok ( 64, "DBG 0x1234 thingy at 0x0003f0c0+0x5c\n", + "DBG %p %s at %#08lx+%#zx\n", ( ( void * ) 0x1234 ), + "thingy", 0x3f0c0UL, ( ( size_t ) 0x5c ) ); + snprintf_ok ( 64, "PCI 00:1f.3", "PCI %02x:%02x.%x", 0x00, 0x1f, 0x03 ); + snprintf_ok ( 64, "Region [1000000,3f000000)", "Region [%llx,%llx)", + 0x1000000ULL, 0x3f000000ULL ); +} + +/** vsprintf() self-test */ +struct self_test vsprintf_test __self_test = { + .name = "vsprintf", + .exec = vsprintf_test_exec, +}; From 55daa953fb82412e2e4dced4a39c6f7a3ff39b46 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 12 Aug 2013 18:23:25 +0100 Subject: [PATCH 64/65] [settings] Allow numeric_setting_value() to handle long setting values Allow numeric_setting_value() to handle e.g. the byte sequence 00:00:00:00:12:34:56:78 by returning -ERANGE only if the value actually overflows the return type. Signed-off-by: Michael Brown --- src/core/settings.c | 8 ++++---- src/tests/settings_test.c | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 889e1078..87de0d1b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -862,18 +862,18 @@ static int numeric_setting_value ( int is_signed, const void *raw, size_t len, const int8_t *signed_bytes = raw; int is_negative; unsigned int i; + uint8_t pad; uint8_t byte; - /* Range check */ - if ( len > sizeof ( long ) ) - return -ERANGE; - /* Convert to host-ordered longs */ is_negative = ( len && ( signed_bytes[0] < 0 ) ); *value = ( ( is_signed && is_negative ) ? -1L : 0 ); + pad = *value; for ( i = 0 ; i < len ; i++ ) { byte = unsigned_bytes[i]; *value = ( ( *value << 8 ) | byte ); + if ( ( ( i + sizeof ( *value ) ) < len ) && ( byte != pad ) ) + return -ERANGE; } return len; diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 670d549b..d1d923a4 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -342,6 +342,12 @@ static void settings_test_exec ( void ) { RAW ( 0x98, 0xab, 0x41, 0x81 ), 0x98ab4181 ); fetchn_ok ( &test_settings, &test_uint32_setting, RAW ( 0xff, 0xff, 0xfe ), 0x00fffffe ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0, 0, 0, 0x12, 0x34, 0x56, 0x78 ), 0x12345678 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0, 0, 0, 0x12, 0x34, 0x56, 0x78 ), 0x12345678 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0xff, 0xff, 0x87, 0x65, 0x43, 0x21 ), -0x789abcdf ); /* "hex" setting type */ storef_ok ( &test_settings, &test_hex_setting, From c692a690dafe4c6bdb406c4c5e0ae66bfcb2f6cb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 12 Aug 2013 13:42:12 +0100 Subject: [PATCH 65/65] [settings] Expose memory map via settings mechanism Allow memory map entries to be read using the syntax ${memmap/..} where is the index of the memory region, is a bitmask where bit 0 represents the start address and bit 1 represents the length (allowing the end address to be encoded by having both bits 0 and 1 set), and is the number of bits by which to shift the result. This allows for several values of interest to be encoded. For example: ${memmap/.1.0:hexraw} # 64-bit start address of ${memmap/.2.0:hexraw} # 64-bit length of , in bytes ${memmap/.3.0:hexraw} # 64-bit end address of ${memmap/.2.10:int32} # Length of , in kB ${memmap/.2.20:int32} # Length of , in MB The numeric encoding is slightly more sophisticated than described here, allowing a single encoding to cover multiple regions. (See the source code for details.) The primary use case for this feature is to provide the total system memory size (in MB) via the "memsize" predefined setting. Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/settings.h | 1 + src/core/memmap_settings.c | 242 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 247 insertions(+) create mode 100644 src/core/memmap_settings.c diff --git a/src/config/config.c b/src/config/config.c index 06c5713f..c8cb5399 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -316,6 +316,9 @@ REQUIRE_OBJECT ( guestinfo ); #ifdef CPUID_SETTINGS REQUIRE_OBJECT ( cpuid_settings ); #endif +#ifdef MEMMAP_SETTINGS +REQUIRE_OBJECT ( memmap_settings ); +#endif /* * Drag in selected keyboard map diff --git a/src/config/settings.h b/src/config/settings.h index 97131e88..b06f2901 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define PCI_SETTINGS /* PCI device settings */ //#define CPUID_SETTINGS /* CPUID settings */ +//#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ #include diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c new file mode 100644 index 00000000..f06b8750 --- /dev/null +++ b/src/core/memmap_settings.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Memory map settings + * + * Memory map settings are numerically encoded as: + * + * Bits 31-24 Number of regions, minus one + * Bits 23-16 Starting region + * Bits 15-11 Unused + * Bit 10 Ignore non-existent regions (rather than generating an error) + * Bit 9 Include length + * Bit 8 Include start address + * Bits 7-6 Unused + * Bits 5-0 Scale factor (i.e. right shift count) + */ + +/** + * Construct memory map setting tag + * + * @v start Starting region + * @v count Number of regions + * @v include_start Include start address + * @v include_length Include length + * @v ignore Ignore non-existent regions + * @v scale Scale factor + * @ret tag Setting tag + */ +#define MEMMAP_TAG( start, count, include_start, include_length, \ + ignore, scale ) \ + ( ( (start) << 16 ) | ( ( (count) - 1 ) << 24 ) | \ + ( (ignore) << 10 ) | ( (include_length) << 9 ) | \ + ( (include_start) << 8 ) | (scale) ) + +/** + * Extract number of regions from setting tag + * + * @v tag Setting tag + * @ret count Number of regions + */ +#define MEMMAP_COUNT( tag ) ( ( ( (tag) >> 24 ) & 0xff ) + 1 ) + +/** + * Extract starting region from setting tag + * + * @v tag Setting tag + * @ret start Starting region + */ +#define MEMMAP_START( tag ) ( ( (tag) >> 16 ) & 0xff ) + +/** + * Extract ignore flag from setting tag + * + * @v tag Setting tag + * @ret ignore Ignore non-existent regions + */ +#define MEMMAP_IGNORE_NONEXISTENT( tag ) ( (tag) & 0x00000400UL ) + +/** + * Extract length inclusion flag from setting tag + * + * @v tag Setting tag + * @ret include_length Include length + */ +#define MEMMAP_INCLUDE_LENGTH( tag ) ( (tag) & 0x00000200UL ) + +/** + * Extract start address inclusion flag from setting tag + * + * @v tag Setting tag + * @ret include_start Include start address + */ +#define MEMMAP_INCLUDE_START( tag ) ( (tag) & 0x00000100UL ) + +/** + * Extract scale factor from setting tag + * + * @v tag Setting tag + * @v scale Scale factor + */ +#define MEMMAP_SCALE( tag ) ( (tag) & 0x3f ) + +/** Memory map settings scope */ +static struct settings_scope memmap_settings_scope; + +/** + * Check applicability of memory map setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int memmap_settings_applies ( struct settings *settings __unused, + struct setting *setting ) { + + return ( setting->scope == &memmap_settings_scope ); +} + +/** + * Fetch value of memory map setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int memmap_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct memory_map memmap; + struct memory_region *region; + uint64_t result = 0; + unsigned int i; + unsigned int count; + + DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n", + MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ), + ( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ), + ( ( MEMMAP_INCLUDE_START ( setting->tag ) && + MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) ? "+" : "" ), + ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ? "length" : "" ), + ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ? " ignore" : "" ), + MEMMAP_SCALE ( setting->tag ) ); + + /* Fetch memory map */ + get_memmap ( &memmap ); + + /* Extract results from memory map */ + count = MEMMAP_COUNT ( setting->tag ); + for ( i = MEMMAP_START ( setting->tag ) ; count-- ; i++ ) { + + /* Check that region exists */ + if ( i >= memmap.count ) { + if ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ) { + continue; + } else { + DBGC ( settings, "MEMMAP region %d does not " + "exist\n", i ); + return -ENOENT; + } + } + + /* Extract results from this region */ + region = &memmap.regions[i]; + if ( MEMMAP_INCLUDE_START ( setting->tag ) ) { + result += region->start; + DBGC ( settings, "MEMMAP %d start %08llx\n", + i, region->start ); + } + if ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) { + result += ( region->end - region->start ); + DBGC ( settings, "MEMMAP %d length %08llx\n", + i, ( region->end - region->start ) ); + } + } + + /* Scale result */ + result >>= MEMMAP_SCALE ( setting->tag ); + + /* Return result */ + result = cpu_to_be64 ( result ); + if ( len > sizeof ( result ) ) + len = sizeof ( result ); + memcpy ( data, &result, len ); + + /* Set type if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return sizeof ( result ); +} + +/** Memory map settings operations */ +static struct settings_operations memmap_settings_operations = { + .applies = memmap_settings_applies, + .fetch = memmap_settings_fetch, +}; + +/** Memory map settings */ +static struct settings memmap_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( memmap_settings.siblings ), + .children = LIST_HEAD_INIT ( memmap_settings.children ), + .op = &memmap_settings_operations, + .default_scope = &memmap_settings_scope, +}; + +/** Initialise memory map settings */ +static void memmap_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &memmap_settings, NULL, + "memmap" ) ) != 0 ) { + DBG ( "MEMMAP could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** Memory map settings initialiser */ +struct init_fn memmap_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = memmap_settings_init, +}; + +/** Memory map predefined settings */ +struct setting memmap_predefined_settings[] __setting ( SETTING_MISC ) = { + { + .name = "memsize", + .description = "Memory size (in MB)", + .tag = MEMMAP_TAG ( 0, 0x100, 0, 1, 1, 20 ), + .type = &setting_type_int32, + .scope = &memmap_settings_scope, + }, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 0fd3faca..4053923b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -285,6 +285,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_linux_pci ( ERRFILE_OTHER | 0x003c0000 ) #define ERRFILE_pci_settings ( ERRFILE_OTHER | 0x003d0000 ) #define ERRFILE_efi_reboot ( ERRFILE_OTHER | 0x003e0000 ) +#define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 ) /** @} */