From 64b4452bca04af433f1c98ab782c0e93cd5c88c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 19 Feb 2019 18:47:12 +0000 Subject: [efi] Blacklist the Dell Ip4ConfigDxe driver On a Dell OptiPlex 7010, calling DisconnectController() on the LOM device handle will lock up the system. Debugging shows that execution is trapped in an infinite loop that is somehow trying to reconnect drivers (without going via ConnectController()). The problem can be reproduced in the UEFI shell with no iPXE code present, by using the "disconnect" command. Experimentation shows that the only fix is to unload (rather than just disconnect) the "Ip4ConfigDxe" driver. Add the concept of a blacklist of UEFI drivers that will be automatically unloaded when iPXE runs as an application, and add the Dell Ip4ConfigDxe driver to this blacklist. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_blacklist.h | 13 ++ src/include/ipxe/errfile.h | 1 + src/interface/efi/efi_blacklist.c | 237 +++++++++++++++++++++++++++++++++++ src/interface/efi/efiprefix.c | 6 + 4 files changed, 257 insertions(+) create mode 100644 src/include/ipxe/efi/efi_blacklist.h create mode 100644 src/interface/efi/efi_blacklist.c (limited to 'src') diff --git a/src/include/ipxe/efi/efi_blacklist.h b/src/include/ipxe/efi/efi_blacklist.h new file mode 100644 index 000000000..c5a5a61dc --- /dev/null +++ b/src/include/ipxe/efi/efi_blacklist.h @@ -0,0 +1,13 @@ +#ifndef _IPXE_EFI_BLACKLIST_H +#define _IPXE_EFI_BLACKLIST_H + +/** @file + * + * EFI driver blacklist + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern void efi_unload_blacklist ( void ); + +#endif /* _IPXE_EFI_BLACKLIST_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 596491a1f..ce67fc66d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -375,6 +375,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) #define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) #define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 ) +#define ERRFILE_efi_blacklist ( ERRFILE_OTHER | 0x00520000 ) /** @} */ diff --git a/src/interface/efi/efi_blacklist.c b/src/interface/efi/efi_blacklist.c new file mode 100644 index 000000000..292b28e8c --- /dev/null +++ b/src/interface/efi/efi_blacklist.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2019 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_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI driver blacklist + * + */ + +/** A blacklisted driver */ +struct efi_blacklist { + /** Name */ + const char *name; + /** + * Check if driver is blacklisted + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @ret blacklisted Driver is the blacklisted driver + */ + int ( * blacklist ) ( EFI_DRIVER_BINDING_PROTOCOL *binding, + EFI_LOADED_IMAGE_PROTOCOL *loaded, + EFI_COMPONENT_NAME_PROTOCOL *wtf ); +}; + +/** + * Blacklist Dell Ip4ConfigDxe driver + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @ret blacklisted Driver is the blacklisted driver + */ +static int +efi_blacklist_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf ) { + static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; + static const char dell[] = "Dell Inc."; + char manufacturer[ sizeof ( dell ) ]; + CHAR16 *name; + + /* Check driver name */ + if ( ! wtf ) + return 0; + if ( wtf->GetDriverName ( wtf, "eng", &name ) != 0 ) + return 0; + if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) + return 0; + + /* Check manufacturer */ + fetch_string_setting ( NULL, &manufacturer_setting, manufacturer, + sizeof ( manufacturer ) ); + if ( strcmp ( manufacturer, dell ) != 0 ) + return 0; + + return 1; +} + +/** Blacklisted drivers */ +static struct efi_blacklist efi_blacklists[] = { + { + .name = "Dell Ip4Config", + .blacklist = efi_blacklist_dell_ip4config, + }, +}; + +/** + * Find driver blacklisting, if any + * + * @v driver Driver binding handle + * @ret blacklist Driver blacklisting, or NULL + * @ret rc Return status code + */ +static int efi_blacklist ( EFI_HANDLE driver, + struct efi_blacklist **blacklist ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DRIVER_BINDING_PROTOCOL *binding; + void *interface; + } binding; + union { + EFI_LOADED_IMAGE_PROTOCOL *loaded; + void *interface; + } loaded; + union { + EFI_COMPONENT_NAME_PROTOCOL *wtf; + void *interface; + } wtf; + unsigned int i; + EFI_HANDLE image; + EFI_STATUS efirc; + int rc; + + DBGC2 ( &efi_blacklists, "EFIBL checking %s\n", + efi_handle_name ( driver ) ); + + /* Mark as not blacklisted */ + *blacklist = NULL; + + /* Open driver binding protocol */ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_driver_binding_protocol_guid, + &binding.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIBL %s could not open driver binding " + "protocol: %s\n", efi_handle_name ( driver ), + strerror ( rc ) ); + goto err_binding; + } + image = binding.binding->ImageHandle; + + /* Open loaded image protocol */ + if ( ( efirc = bs->OpenProtocol ( + image, &efi_loaded_image_protocol_guid, + &loaded.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIBL %s could not open", + efi_handle_name ( driver ) ); + DBGC ( driver, " %s loaded image protocol: %s\n", + efi_handle_name ( image ), strerror ( rc ) ); + goto err_loaded; + } + + /* Open component name protocol, if present*/ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_component_name_protocol_guid, + &wtf.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + /* Ignore failure; is not required to be present */ + wtf.interface = NULL; + } + + /* Check blacklistings */ + for ( i = 0 ; i < ( sizeof ( efi_blacklists ) / + sizeof ( efi_blacklists[0] ) ) ; i++ ) { + if ( efi_blacklists[i].blacklist ( binding.binding, + loaded.loaded, wtf.wtf ) ) { + *blacklist = &efi_blacklists[i]; + break; + } + } + + /* Success */ + rc = 0; + + /* Close protocols */ + if ( wtf.wtf ) { + bs->CloseProtocol ( driver, &efi_component_name_protocol_guid, + efi_image_handle, driver ); + } + bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, + efi_image_handle, image ); + err_loaded: + bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid, + efi_image_handle, driver ); + err_binding: + return rc; +} + +/** + * Unload any blacklisted drivers + * + */ +void efi_unload_blacklist ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_blacklist *blacklist; + EFI_HANDLE *drivers; + EFI_HANDLE driver; + UINTN num_drivers; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Locate all driver binding protocol handles */ + if ( ( efirc = bs->LocateHandleBuffer ( + ByProtocol, &efi_driver_binding_protocol_guid, + NULL, &num_drivers, &drivers ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_blacklists, "EFIBL could not list all drivers: " + "%s\n", strerror ( rc ) ); + return; + } + + /* Unload any blacklisted drivers */ + for ( i = 0 ; i < num_drivers ; i++ ) { + driver = drivers[i]; + if ( ( rc = efi_blacklist ( driver, &blacklist ) ) != 0 ) { + DBGC ( driver, "EFIBL could not determine " + "blacklisting for %s: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + continue; + } + if ( ! blacklist ) + continue; + DBGC ( driver, "EFIBL unloading %s (%s)\n", + efi_handle_name ( driver ), blacklist->name ); + if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) { + DBGC ( driver, "EFIBL could not unload %s: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + } + } + + /* Free handle list */ + bs->FreePool ( drivers ); +} diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index 18b931e68..de3572c75 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** * EFI entry point @@ -75,6 +76,10 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, */ static int efi_probe ( struct root_device *rootdev __unused ) { + /* Unloaded any blacklisted drivers */ + efi_unload_blacklist(); + + /* Connect our drivers */ return efi_driver_connect_all(); } @@ -85,6 +90,7 @@ static int efi_probe ( struct root_device *rootdev __unused ) { */ static void efi_remove ( struct root_device *rootdev __unused ) { + /* Disconnect our drivers */ efi_driver_disconnect_all(); } -- cgit v1.2.3-55-g7522 From 272fe32529103dd39875a9fbed5cfdf1a059e294 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Feb 2019 11:32:25 +0000 Subject: [tls] Support stateful session resumption Record the session ID (if any) provided by the server and attempt to reuse it for any concurrent connections to the same server. If multiple connections are initiated concurrently (e.g. when using PeerDist) then defer sending the ClientHello for all but the first connection, to allow time for the first connection to potentially obtain a session ID (and thereby speed up the negotiation for all remaining connections). Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 31 +++++++- src/net/tls.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 220 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index b1e702e18..0375a722a 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -242,13 +242,40 @@ struct md5_sha1_digest { /** MD5+SHA1 digest size */ #define MD5_SHA1_DIGEST_SIZE sizeof ( struct md5_sha1_digest ) -/** A TLS connection */ -struct tls_connection { +/** A TLS session */ +struct tls_session { /** Reference counter */ struct refcnt refcnt; + /** List of sessions */ + struct list_head list; /** Server name */ const char *name; + /** Session ID */ + uint8_t id[32]; + /** Length of session ID */ + size_t id_len; + /** Master secret */ + uint8_t master_secret[48]; + + /** List of connections */ + struct list_head conn; +}; + +/** A TLS connection */ +struct tls_connection { + /** Reference counter */ + struct refcnt refcnt; + + /** Session */ + struct tls_session *session; + /** List of connections within the same session */ + struct list_head list; + /** Session ID */ + uint8_t session_id[32]; + /** Length of session ID */ + size_t session_id_len; + /** Plaintext stream */ struct interface plainstream; /** Ciphertext stream */ diff --git a/src/net/tls.c b/src/net/tls.c index 9d994cd77..a2f1f6528 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -175,6 +175,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); __einfo_uniqify ( EINFO_EPROTO, 0x01, \ "Illegal protocol version upgrade" ) +/** List of TLS session */ +static LIST_HEAD ( tls_sessions ); + +static void tls_tx_resume_all ( struct tls_session *session ); static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ); static void tls_clear_cipher ( struct tls_connection *tls, @@ -307,6 +311,25 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { ****************************************************************************** */ +/** + * Free TLS session + * + * @v refcnt Reference counter + */ +static void free_tls_session ( struct refcnt *refcnt ) { + struct tls_session *session = + container_of ( refcnt, struct tls_session, refcnt ); + + /* Sanity check */ + assert ( list_empty ( &session->conn ) ); + + /* Remove from list of sessions */ + list_del ( &session->list ); + + /* Free session */ + free ( session ); +} + /** * Free TLS connection * @@ -315,6 +338,7 @@ struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { static void free_tls ( struct refcnt *refcnt ) { struct tls_connection *tls = container_of ( refcnt, struct tls_connection, refcnt ); + struct tls_session *session = tls->session; struct io_buffer *iobuf; struct io_buffer *tmp; @@ -330,8 +354,12 @@ static void free_tls ( struct refcnt *refcnt ) { x509_put ( tls->cert ); x509_chain_put ( tls->chain ); + /* Drop reference to session */ + assert ( list_empty ( &tls->list ) ); + ref_put ( &session->refcnt ); + /* Free TLS structure itself */ - free ( tls ); + free ( tls ); } /** @@ -353,6 +381,13 @@ static void tls_close ( struct tls_connection *tls, int rc ) { intf_shutdown ( &tls->cipherstream, rc ); intf_shutdown ( &tls->plainstream, rc ); intf_shutdown ( &tls->validator, rc ); + + /* Remove from session */ + list_del ( &tls->list ); + INIT_LIST_HEAD ( &tls->list ); + + /* Resume all other connections, in case we were the lead connection */ + tls_tx_resume_all ( tls->session ); } /****************************************************************************** @@ -928,6 +963,18 @@ static void tls_tx_resume ( struct tls_connection *tls ) { process_add ( &tls->process ); } +/** + * Resume TX state machine for all connections within a session + * + * @v session TLS session + */ +static void tls_tx_resume_all ( struct tls_session *session ) { + struct tls_connection *tls; + + list_for_each_entry ( tls, &session->conn, list ) + tls_tx_resume ( tls ); +} + /** * Transmit Handshake record * @@ -953,11 +1000,14 @@ static int tls_send_handshake ( struct tls_connection *tls, * @ret rc Return status code */ static int tls_send_client_hello ( struct tls_connection *tls ) { + struct tls_session *session = tls->session; + size_t name_len = strlen ( session->name ); struct { uint32_t type_length; uint16_t version; uint8_t random[32]; uint8_t session_id_len; + uint8_t session_id[session->id_len]; uint16_t cipher_suite_len; uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; uint8_t compression_methods_len; @@ -971,7 +1021,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { struct { uint8_t type; uint16_t len; - uint8_t name[ strlen ( tls->name ) ]; + uint8_t name[name_len]; } __attribute__ (( packed )) list[1]; } __attribute__ (( packed )) server_name; uint16_t max_fragment_length_type; @@ -999,12 +1049,22 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { struct tls_signature_hash_algorithm *sighash; unsigned int i; + /* Record requested session ID and associated master secret */ + memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) ); + tls->session_id_len = session->id_len; + memcpy ( tls->master_secret, session->master_secret, + sizeof ( tls->master_secret ) ); + + /* Construct record */ memset ( &hello, 0, sizeof ( hello ) ); hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | htonl ( sizeof ( hello ) - sizeof ( hello.type_length ) ) ); hello.version = htons ( tls->version ); memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); + hello.session_id_len = tls->session_id_len; + memcpy ( hello.session_id, tls->session_id, + sizeof ( hello.session_id ) ); hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES ) hello.cipher_suites[i++] = suite->code; @@ -1018,7 +1078,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { hello.extensions.server_name.list[0].type = TLS_SERVER_NAME_HOST_NAME; hello.extensions.server_name.list[0].len = htons ( sizeof ( hello.extensions.server_name.list[0].name )); - memcpy ( hello.extensions.server_name.list[0].name, tls->name, + memcpy ( hello.extensions.server_name.list[0].name, session->name, sizeof ( hello.extensions.server_name.list[0].name ) ); hello.extensions.max_fragment_length_type = htons ( TLS_MAX_FRAGMENT_LENGTH ); @@ -1513,8 +1573,34 @@ static int tls_new_server_hello ( struct tls_connection *tls, if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) return rc; - /* Generate secrets */ - tls_generate_master_secret ( tls ); + /* Reuse or generate master secret */ + if ( hello_a->session_id_len && + ( hello_a->session_id_len == tls->session_id_len ) && + ( memcmp ( session_id, tls->session_id, + tls->session_id_len ) == 0 ) ) { + + /* Session ID match: reuse master secret */ + DBGC ( tls, "TLS %p resuming session ID:\n", tls ); + DBGC_HDA ( tls, 0, tls->session_id, tls->session_id_len ); + + } else { + + /* Generate new master secret */ + tls_generate_master_secret ( tls ); + + /* Record new session ID, if present */ + if ( hello_a->session_id_len && + ( hello_a->session_id_len <= sizeof ( tls->session_id ))){ + tls->session_id_len = hello_a->session_id_len; + memcpy ( tls->session_id, session_id, + tls->session_id_len ); + DBGC ( tls, "TLS %p new session ID:\n", tls ); + DBGC_HDA ( tls, 0, tls->session_id, + tls->session_id_len ); + } + } + + /* Generate keys */ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) return rc; @@ -1739,6 +1825,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls, */ static int tls_new_finished ( struct tls_connection *tls, const void *data, size_t len ) { + struct tls_session *session = tls->session; struct digest_algorithm *digest = tls->handshake_digest; const struct { uint8_t verify_data[ sizeof ( tls->verify.server ) ]; @@ -1767,6 +1854,30 @@ static int tls_new_finished ( struct tls_connection *tls, /* Mark server as finished */ pending_put ( &tls->server_negotiation ); + /* If we are resuming a session (i.e. if the server Finished + * arrives before the client Finished is sent), then schedule + * transmission of Change Cipher and Finished. + */ + if ( is_pending ( &tls->client_negotiation ) ) { + tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED ); + tls_tx_resume ( tls ); + } + + /* Record session ID and master secret, if applicable */ + if ( tls->session_id_len ) { + session->id_len = tls->session_id_len; + memcpy ( session->id, tls->session_id, sizeof ( session->id ) ); + memcpy ( session->master_secret, tls->master_secret, + sizeof ( session->master_secret ) ); + } + + /* Move to end of session's connection list and allow other + * connections to start making progress. + */ + list_del ( &tls->list ); + list_add_tail ( &tls->list, &session->conn ); + tls_tx_resume_all ( session ); + /* Send notification of a window change */ xfer_window_changed ( &tls->plainstream ); @@ -2608,6 +2719,7 @@ static struct interface_descriptor tls_cipherstream_desc = * @v rc Reason for completion */ static void tls_validator_done ( struct tls_connection *tls, int rc ) { + struct tls_session *session = tls->session; struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; @@ -2628,9 +2740,9 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { assert ( cert != NULL ); /* Verify server name */ - if ( ( rc = x509_check_name ( cert, tls->name ) ) != 0 ) { + if ( ( rc = x509_check_name ( cert, session->name ) ) != 0 ) { DBGC ( tls, "TLS %p server certificate does not match %s: %s\n", - tls, tls->name, strerror ( rc ) ); + tls, session->name, strerror ( rc ) ); goto err; } @@ -2682,6 +2794,8 @@ static struct interface_descriptor tls_validator_desc = * @v tls TLS connection */ static void tls_tx_step ( struct tls_connection *tls ) { + struct tls_session *session = tls->session; + struct tls_connection *conn; int rc; /* Wait for cipherstream to become ready */ @@ -2690,6 +2804,17 @@ static void tls_tx_step ( struct tls_connection *tls ) { /* Send first pending transmission */ if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { + /* Wait for session ID to become available unless we + * are the lead connection within the session. + */ + if ( session->id_len == 0 ) { + list_for_each_entry ( conn, &session->conn, list ) { + if ( conn == tls ) + break; + if ( is_pending ( &conn->server_negotiation ) ) + return; + } + } /* Send Client Hello */ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { DBGC ( tls, "TLS %p could not send Client Hello: %s\n", @@ -2766,6 +2891,60 @@ static void tls_tx_step ( struct tls_connection *tls ) { static struct process_descriptor tls_process_desc = PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step ); +/****************************************************************************** + * + * Session management + * + ****************************************************************************** + */ + +/** + * Find or create session for TLS connection + * + * @v tls TLS connection + * @v name Server name + * @ret rc Return status code + */ +static int tls_session ( struct tls_connection *tls, const char *name ) { + struct tls_session *session; + char *name_copy; + int rc; + + /* Find existing matching session, if any */ + list_for_each_entry ( session, &tls_sessions, list ) { + if ( strcmp ( name, session->name ) == 0 ) { + ref_get ( &session->refcnt ); + tls->session = session; + DBGC ( tls, "TLS %p joining session %s\n", tls, name ); + return 0; + } + } + + /* Create new session */ + session = zalloc ( sizeof ( *session ) + strlen ( name ) + + 1 /* NUL */ ); + if ( ! session ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &session->refcnt, free_tls_session ); + name_copy = ( ( ( void * ) session ) + sizeof ( *session ) ); + strcpy ( name_copy, name ); + session->name = name_copy; + INIT_LIST_HEAD ( &session->conn ); + list_add ( &session->list, &tls_sessions ); + + /* Record session */ + tls->session = session; + + DBGC ( tls, "TLS %p created session %s\n", tls, name ); + return 0; + + ref_put ( &session->refcnt ); + err_alloc: + return rc; +} + /****************************************************************************** * * Instantiator @@ -2786,7 +2965,7 @@ int add_tls ( struct interface *xfer, const char *name, } memset ( tls, 0, sizeof ( *tls ) ); ref_init ( &tls->refcnt, free_tls ); - tls->name = name; + INIT_LIST_HEAD ( &tls->list ); intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt ); intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt ); intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt ); @@ -2809,6 +2988,9 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { goto err_random; } + if ( ( rc = tls_session ( tls, name ) ) != 0 ) + goto err_session; + list_add_tail ( &tls->list, &tls->session->conn ); /* Start negotiation */ tls_restart ( tls ); @@ -2819,6 +3001,7 @@ int add_tls ( struct interface *xfer, const char *name, ref_put ( &tls->refcnt ); return 0; + err_session: err_random: ref_put ( &tls->refcnt ); err_alloc: -- cgit v1.2.3-55-g7522 From 799781f1683ef4e0e331a39556e9687c4311ad3e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Feb 2019 23:50:11 +0000 Subject: [tls] Fix incorrectly duplicated error number Signed-off-by: Michael Brown --- src/net/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/tls.c b/src/net/tls.c index a2f1f6528..43fe4b4d2 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -104,7 +104,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); "Invalid MAC" ) #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, \ + __einfo_uniqify ( EINFO_EIO, 0x01, \ "Unknown alert level" ) #define ENOMEM_CONTEXT __einfo_error ( EINFO_ENOMEM_CONTEXT ) #define EINFO_ENOMEM_CONTEXT \ -- cgit v1.2.3-55-g7522 From eaba1a22b8552f0410fe1519d7d0b606dc9ef3bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Mar 2019 15:02:02 +0000 Subject: [tls] Support stateless session resumption Add support for RFC5077 session ticket extensions to allow for stateless TLS session resumption. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 12 +++++ src/net/tls.c | 129 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 122 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 0375a722a..4bffde7c2 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -63,6 +63,7 @@ struct tls_header { #define TLS_HELLO_REQUEST 0 #define TLS_CLIENT_HELLO 1 #define TLS_SERVER_HELLO 2 +#define TLS_NEW_SESSION_TICKET 4 #define TLS_CERTIFICATE 11 #define TLS_SERVER_KEY_EXCHANGE 12 #define TLS_CERTIFICATE_REQUEST 13 @@ -108,6 +109,9 @@ struct tls_header { /* TLS signature algorithms extension */ #define TLS_SIGNATURE_ALGORITHMS 13 +/* TLS session ticket extension */ +#define TLS_SESSION_TICKET 35 + /* TLS renegotiation information extension */ #define TLS_RENEGOTIATION_INFO 0xff01 @@ -255,6 +259,10 @@ struct tls_session { uint8_t id[32]; /** Length of session ID */ size_t id_len; + /** Session ticket */ + void *ticket; + /** Length of session ticket */ + size_t ticket_len; /** Master secret */ uint8_t master_secret[48]; @@ -275,6 +283,10 @@ struct tls_connection { uint8_t session_id[32]; /** Length of session ID */ size_t session_id_len; + /** New session ticket */ + void *new_session_ticket; + /** Length of new session ticket */ + size_t new_session_ticket_len; /** Plaintext stream */ struct interface plainstream; diff --git a/src/net/tls.c b/src/net/tls.c index 43fe4b4d2..1cd37e776 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -102,6 +102,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EINVAL_MAC \ __einfo_uniqify ( EINFO_EINVAL, 0x0d, \ "Invalid MAC" ) +#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET ) +#define EINFO_EINVAL_TICKET \ + __einfo_uniqify ( EINFO_EINVAL, 0x0e, \ + "Invalid New Session Ticket record") #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ __einfo_uniqify ( EINFO_EIO, 0x01, \ @@ -326,6 +330,9 @@ static void free_tls_session ( struct refcnt *refcnt ) { /* Remove from list of sessions */ list_del ( &session->list ); + /* Free session ticket */ + free ( session->ticket ); + /* Free session */ free ( session ); } @@ -343,6 +350,7 @@ static void free_tls ( struct refcnt *refcnt ) { struct io_buffer *tmp; /* Free dynamically-allocated resources */ + free ( tls->new_session_ticket ); tls_clear_cipher ( tls, &tls->tx_cipherspec ); tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); @@ -1007,7 +1015,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { uint16_t version; uint8_t random[32]; uint8_t session_id_len; - uint8_t session_id[session->id_len]; + uint8_t session_id[tls->session_id_len]; uint16_t cipher_suite_len; uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; uint8_t compression_methods_len; @@ -1043,18 +1051,17 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { uint8_t data[ tls->secure_renegotiation ? sizeof ( tls->verify.client ) :0]; } __attribute__ (( packed )) renegotiation_info; + uint16_t session_ticket_type; + uint16_t session_ticket_len; + struct { + uint8_t data[session->ticket_len]; + } __attribute__ (( packed )) session_ticket; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; struct tls_cipher_suite *suite; struct tls_signature_hash_algorithm *sighash; unsigned int i; - /* Record requested session ID and associated master secret */ - memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) ); - tls->session_id_len = session->id_len; - memcpy ( tls->master_secret, session->master_secret, - sizeof ( tls->master_secret ) ); - /* Construct record */ memset ( &hello, 0, sizeof ( hello ) ); hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | @@ -1102,6 +1109,11 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { = sizeof ( hello.extensions.renegotiation_info.data ); memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client, sizeof ( hello.extensions.renegotiation_info.data ) ); + hello.extensions.session_ticket_type = htons ( TLS_SESSION_TICKET ); + hello.extensions.session_ticket_len + = htons ( sizeof ( hello.extensions.session_ticket ) ); + memcpy ( hello.extensions.session_ticket.data, session->ticket, + sizeof ( hello.extensions.session_ticket.data ) ); return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1631,6 +1643,57 @@ static int tls_new_server_hello ( struct tls_connection *tls, return 0; } +/** + * Receive New Session Ticket handshake record + * + * @v tls TLS connection + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_session_ticket ( struct tls_connection *tls, + const void *data, size_t len ) { + const struct { + uint32_t lifetime; + uint16_t len; + uint8_t ticket[0]; + } __attribute__ (( packed )) *new_session_ticket = data; + size_t ticket_len; + + /* Parse header */ + if ( sizeof ( *new_session_ticket ) > len ) { + DBGC ( tls, "TLS %p received underlength New Session Ticket\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_TICKET; + } + ticket_len = ntohs ( new_session_ticket->len ); + if ( ticket_len > ( len - sizeof ( *new_session_ticket ) ) ) { + DBGC ( tls, "TLS %p received overlength New Session Ticket\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL_TICKET; + } + + /* Free any unapplied new session ticket */ + free ( tls->new_session_ticket ); + tls->new_session_ticket = NULL; + tls->new_session_ticket_len = 0; + + /* Record ticket */ + tls->new_session_ticket = malloc ( ticket_len ); + if ( ! tls->new_session_ticket ) + return -ENOMEM; + memcpy ( tls->new_session_ticket, new_session_ticket->ticket, + ticket_len ); + tls->new_session_ticket_len = ticket_len; + DBGC ( tls, "TLS %p new session ticket:\n", tls ); + DBGC_HDA ( tls, 0, tls->new_session_ticket, + tls->new_session_ticket_len ); + + return 0; +} + /** * Parse certificate chain * @@ -1863,12 +1926,21 @@ static int tls_new_finished ( struct tls_connection *tls, tls_tx_resume ( tls ); } - /* Record session ID and master secret, if applicable */ + /* Record session ID, ticket, and master secret, if applicable */ + if ( tls->session_id_len || tls->new_session_ticket_len ) { + memcpy ( session->master_secret, tls->master_secret, + sizeof ( session->master_secret ) ); + } if ( tls->session_id_len ) { session->id_len = tls->session_id_len; memcpy ( session->id, tls->session_id, sizeof ( session->id ) ); - memcpy ( session->master_secret, tls->master_secret, - sizeof ( session->master_secret ) ); + } + if ( tls->new_session_ticket_len ) { + free ( session->ticket ); + session->ticket = tls->new_session_ticket; + session->ticket_len = tls->new_session_ticket_len; + tls->new_session_ticket = NULL; + tls->new_session_ticket_len = 0; } /* Move to end of session's connection list and allow other @@ -1933,6 +2005,10 @@ static int tls_new_handshake ( struct tls_connection *tls, case TLS_SERVER_HELLO: rc = tls_new_server_hello ( tls, payload, payload_len ); break; + case TLS_NEW_SESSION_TICKET: + rc = tls_new_session_ticket ( tls, payload, + payload_len ); + break; case TLS_CERTIFICATE: rc = tls_new_certificate ( tls, payload, payload_len ); break; @@ -2804,16 +2880,31 @@ static void tls_tx_step ( struct tls_connection *tls ) { /* Send first pending transmission */ if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { - /* Wait for session ID to become available unless we - * are the lead connection within the session. + /* Serialise server negotiations within a session, to + * provide a consistent view of session IDs and + * session tickets. */ - if ( session->id_len == 0 ) { - list_for_each_entry ( conn, &session->conn, list ) { - if ( conn == tls ) - break; - if ( is_pending ( &conn->server_negotiation ) ) - return; - } + list_for_each_entry ( conn, &session->conn, list ) { + if ( conn == tls ) + break; + if ( is_pending ( &conn->server_negotiation ) ) + return; + } + /* Record or generate session ID and associated master secret */ + if ( session->id_len ) { + /* Attempt to resume an existing session */ + memcpy ( tls->session_id, session->id, + sizeof ( tls->session_id ) ); + tls->session_id_len = session->id_len; + memcpy ( tls->master_secret, session->master_secret, + sizeof ( tls->master_secret ) ); + } else { + /* No existing session: use a random session ID */ + assert ( sizeof ( tls->session_id ) == + sizeof ( tls->client_random ) ); + memcpy ( tls->session_id, &tls->client_random, + sizeof ( tls->session_id ) ); + tls->session_id_len = sizeof ( tls->session_id ); } /* Send Client Hello */ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { -- cgit v1.2.3-55-g7522 From 447e5cd4474084eda5db28b467cf407c014ebe33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Mar 2019 13:47:30 +0000 Subject: [crypto] Use x509_name() in validator debug messages Display a human-readable certificate name in validator debug messages wherever possible. Signed-off-by: Michael Brown --- src/net/validator.c | 105 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/net/validator.c b/src/net/validator.c index 40f778c7d..25d81bd24 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -72,6 +72,18 @@ struct validator { size_t len ); }; +/** + * Get validator name (for debug messages) + * + * @v validator Certificate validator + * @ret name Validator name + */ +static const char * validator_name ( struct validator *validator ) { + + /* Use name of first certificate in chain */ + return x509_name ( x509_first ( validator->chain ) ); +} + /** * Free certificate validator * @@ -81,7 +93,8 @@ static void validator_free ( struct refcnt *refcnt ) { struct validator *validator = container_of ( refcnt, struct validator, refcnt ); - DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" freed\n", + validator, validator_name ( validator ) ); x509_chain_put ( validator->chain ); ocsp_put ( validator->ocsp ); xferbuf_free ( &validator->buffer ); @@ -165,8 +178,9 @@ static int validator_append ( struct validator *validator, /* Enter certificateSet */ if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not enter " - "certificateSet: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not enter " + "certificateSet: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); goto err_certificateset; } @@ -176,15 +190,16 @@ static int validator_append ( struct validator *validator, /* Add certificate to chain */ if ( ( rc = x509_append_raw ( certs, cursor.data, cursor.len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificate: %s\n", - validator, strerror ( rc) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not " + "append certificate: %s\n", validator, + validator_name ( validator ), strerror ( rc) ); DBGC_HDA ( validator, 0, cursor.data, cursor.len ); return rc; } cert = x509_last ( certs ); - DBGC ( validator, "VALIDATOR %p found certificate %s\n", - validator, x509_name ( cert ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" found certificate ", + validator, validator_name ( validator ) ); + DBGC ( validator, "%s\n", x509_name ( cert ) ); /* Move to next certificate */ asn1_skip_any ( &cursor ); @@ -193,15 +208,17 @@ static int validator_append ( struct validator *validator, /* Append certificates to chain */ last = x509_last ( validator->chain ); if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificates: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not append " + "certificates: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); goto err_auto_append; } /* Check that at least one certificate has been added */ if ( last == x509_last ( validator->chain ) ) { - DBGC ( validator, "VALIDATOR %p failed to append any " - "applicable certificates\n", validator ); + DBGC ( validator, "VALIDATOR %p \"%s\" failed to append any " + "applicable certificates\n", validator, + validator_name ( validator ) ); rc = -EACCES; goto err_no_progress; } @@ -223,11 +240,12 @@ static int validator_append ( struct validator *validator, * Start download of cross-signing certificate * * @v validator Certificate validator - * @v issuer Required issuer + * @v cert X.509 certificate * @ret rc Return status code */ static int validator_start_download ( struct validator *validator, - const struct asn1_cursor *issuer ) { + struct x509_certificate *cert ) { + const struct asn1_cursor *issuer = &cert->issuer.raw; const char *crosscert; char *crosscert_copy; char *uri_string; @@ -261,8 +279,10 @@ static int validator_start_download ( struct validator *validator, crosscert, crc ); base64_encode ( issuer->data, issuer->len, ( uri_string + len ), ( uri_string_len - len ) ); - DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " - "from %s\n", validator, uri_string ); + DBGC ( validator, "VALIDATOR %p \"%s\" downloading ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" cross-signature from %s\n", + x509_name ( cert ), uri_string ); /* Set completion handler */ validator->done = validator_append; @@ -270,8 +290,9 @@ static int validator_start_download ( struct validator *validator, /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: " + "%s\n", validator, validator_name ( validator ), + uri_string, strerror ( rc ) ); goto err_open_uri_string; } @@ -307,16 +328,18 @@ static int validator_ocsp_validate ( struct validator *validator, /* Record OCSP response */ if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not record OCSP " - "response: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not record OCSP " + "response: %s\n", validator, + validator_name ( validator ),strerror ( rc ) ); return rc; } /* Validate OCSP response */ now = time ( NULL ); if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not validate OCSP " - "response: %s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not validate " + "OCSP response: %s\n", validator, + validator_name ( validator ), strerror ( rc ) ); return rc; } @@ -344,8 +367,9 @@ static int validator_start_ocsp ( struct validator *validator, /* Create OCSP check */ assert ( validator->ocsp == NULL ); if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not create OCSP check: " - "%s\n", validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not create OCSP " + "check: %s\n", validator, validator_name ( validator ), + strerror ( rc ) ); return rc; } @@ -354,12 +378,15 @@ static int validator_start_ocsp ( struct validator *validator, /* Open URI */ uri_string = validator->ocsp->uri_string; - DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n", - validator, uri_string ); + DBGC ( validator, "VALIDATOR %p \"%s\" checking ", + validator, validator_name ( validator ) ); + DBGC ( validator, "\"%s\" via %s\n", + x509_name ( cert ), uri_string ); if ( ( rc = xfer_open_uri_string ( &validator->xfer, uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not open %s: " + "%s\n", validator, validator_name ( validator ), + uri_string, strerror ( rc ) ); return rc; } @@ -385,11 +412,13 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { /* Check for errors */ if ( rc != 0 ) { - DBGC ( validator, "VALIDATOR %p transfer failed: %s\n", - validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" transfer failed: %s\n", + validator, validator_name ( validator ), + strerror ( rc ) ); goto err_transfer; } - DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" transfer complete\n", + validator, validator_name ( validator ) ); /* Process completed download */ assert ( validator->done != NULL ); @@ -426,8 +455,9 @@ static int validator_xfer_deliver ( struct validator *validator, /* Add data to buffer */ if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ), meta ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not receive data: %s\n", - validator, strerror ( rc ) ); + DBGC ( validator, "VALIDATOR %p \"%s\" could not receive " + "data: %s\n", validator, validator_name ( validator ), + strerror ( rc ) ); validator_finished ( validator, rc ); return rc; } @@ -471,6 +501,8 @@ static void validator_step ( struct validator *validator ) { now = time ( NULL ); if ( ( rc = x509_validate_chain ( validator->chain, now, NULL, NULL ) ) == 0 ) { + DBGC ( validator, "VALIDATOR %p \"%s\" validated\n", + validator, validator_name ( validator ) ); validator_finished ( validator, 0 ); return; } @@ -514,8 +546,7 @@ static void validator_step ( struct validator *validator ) { /* Otherwise, try to download a suitable cross-signing * certificate. */ - if ( ( rc = validator_start_download ( validator, - &last->issuer.raw ) ) != 0 ) { + if ( ( rc = validator_start_download ( validator, last ) ) != 0 ) { validator_finished ( validator, rc ); return; } @@ -567,8 +598,8 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) { /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &validator->job, job ); ref_put ( &validator->refcnt ); - DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n", - validator, validator->chain ); + DBGC2 ( validator, "VALIDATOR %p \"%s\" validating X509 chain %p\n", + validator, validator_name ( validator ), validator->chain ); return 0; validator_finished ( validator, rc ); -- cgit v1.2.3-55-g7522 From b28ccfc725c9a52401aaa09de0734a44bd44a02d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Mar 2019 15:23:19 +0000 Subject: [tls] Display cross-certificate and OCSP status messages TLS connections will almost always create background connections to perform cross-signed certificate downloads and OCSP checks. There is currently no direct visibility into which checks are taking place, which makes troubleshooting difficult in the absence of either a packet capture or a debug build. Use the job progress message buffer to report the current cross-signed certificate download or OCSP status check, where applicable. Signed-off-by: Michael Brown --- src/net/tls.c | 20 +++++++++++++++ src/net/validator.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/net/tls.c b/src/net/tls.c index 1cd37e776..510bef8c4 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -47,6 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /* Disambiguate the various error causes */ @@ -2570,12 +2571,31 @@ static int tls_plainstream_deliver ( struct tls_connection *tls, return rc; } +/** + * Report job progress + * + * @v tls TLS connection + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int tls_progress ( struct tls_connection *tls, + struct job_progress *progress ) { + + /* Return cipherstream or validator progress as applicable */ + if ( tls_ready ( tls ) ) { + return job_progress ( &tls->cipherstream, progress ); + } else { + return job_progress ( &tls->validator, progress ); + } +} + /** TLS plaintext stream interface operations */ static struct interface_operation tls_plainstream_ops[] = { INTF_OP ( xfer_deliver, struct tls_connection *, tls_plainstream_deliver ), INTF_OP ( xfer_window, struct tls_connection *, tls_plainstream_window ), + INTF_OP ( job_progress, struct tls_connection *, tls_progress ), INTF_OP ( intf_close, struct tls_connection *, tls_close ), }; diff --git a/src/net/validator.c b/src/net/validator.c index 25d81bd24..f6b03ff41 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -49,6 +50,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +struct validator; + +/** A certificate validator action */ +struct validator_action { + /** Name */ + const char *name; + /** Action to take upon completed transfer */ + int ( * done ) ( struct validator *validator, const void *data, + size_t len ); +}; + /** A certificate validator */ struct validator { /** Reference count */ @@ -67,9 +79,16 @@ struct validator { struct ocsp_check *ocsp; /** Data buffer */ struct xfer_buffer buffer; - /** Action to take upon completed transfer */ - int ( * done ) ( struct validator *validator, const void *data, - size_t len ); + + /** Current action */ + const struct validator_action *action; + /** Current certificate + * + * This will always be present within the certificate chain + * and so this pointer does not hold a reference to the + * certificate. + */ + struct x509_certificate *cert; }; /** @@ -123,8 +142,29 @@ static void validator_finished ( struct validator *validator, int rc ) { * */ +/** + * Report job progress + * + * @v validator Certificate validator + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int validator_progress ( struct validator *validator, + struct job_progress *progress ) { + + /* Report current action, if applicable */ + if ( validator->action ) { + snprintf ( progress->message, sizeof ( progress->message ), + "%s %s", validator->action->name, + x509_name ( validator->cert ) ); + } + + return 0; +} + /** Certificate validator job control interface operations */ static struct interface_operation validator_job_operations[] = { + INTF_OP ( job_progress, struct validator *, validator_progress ), INTF_OP ( intf_close, struct validator *, validator_finished ), }; @@ -236,6 +276,12 @@ static int validator_append ( struct validator *validator, return rc; } +/** Cross-signing certificate download validator action */ +static const struct validator_action validator_crosscert = { + .name = "XCRT", + .done = validator_append, +}; + /** * Start download of cross-signing certificate * @@ -285,7 +331,8 @@ static int validator_start_download ( struct validator *validator, x509_name ( cert ), uri_string ); /* Set completion handler */ - validator->done = validator_append; + validator->action = &validator_crosscert; + validator->cert = cert; /* Open URI */ if ( ( rc = xfer_open_uri_string ( &validator->xfer, @@ -350,6 +397,12 @@ static int validator_ocsp_validate ( struct validator *validator, return 0; } +/** OCSP validator action */ +static const struct validator_action validator_ocsp = { + .name = "OCSP", + .done = validator_ocsp_validate, +}; + /** * Start OCSP check * @@ -374,7 +427,8 @@ static int validator_start_ocsp ( struct validator *validator, } /* Set completion handler */ - validator->done = validator_ocsp_validate; + validator->action = &validator_ocsp; + validator->cert = cert; /* Open URI */ uri_string = validator->ocsp->uri_string; @@ -421,9 +475,9 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { validator, validator_name ( validator ) ); /* Process completed download */ - assert ( validator->done != NULL ); - if ( ( rc = validator->done ( validator, validator->buffer.data, - validator->buffer.len ) ) != 0 ) + assert ( validator->action != NULL ); + if ( ( rc = validator->action->done ( validator, validator->buffer.data, + validator->buffer.len ) ) != 0 ) goto err_append; /* Free downloaded data */ -- cgit v1.2.3-55-g7522 From 7b63c1275f33e0fa20c0e59dcc1756899533823c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:27:33 +0000 Subject: [tls] Display validator messages only while validation is in progress Allow the cipherstream to report progress status messages during connection establishment. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 2 ++ src/net/tls.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 4bffde7c2..febbdc589 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -335,6 +335,8 @@ struct tls_connection { struct pending_operation client_negotiation; /** Server security negotiation pending operation */ struct pending_operation server_negotiation; + /** Certificate validation pending operation */ + struct pending_operation validation; /** TX sequence number */ uint64_t tx_seq; diff --git a/src/net/tls.c b/src/net/tls.c index 510bef8c4..746274d61 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -382,6 +382,7 @@ static void tls_close ( struct tls_connection *tls, int rc ) { /* Remove pending operations, if applicable */ pending_put ( &tls->client_negotiation ); pending_put ( &tls->server_negotiation ); + pending_put ( &tls->validation ); /* Remove process */ process_del ( &tls->process ); @@ -950,6 +951,7 @@ static void tls_restart ( struct tls_connection *tls ) { assert ( ! tls->tx_pending ); assert ( ! is_pending ( &tls->client_negotiation ) ); assert ( ! is_pending ( &tls->server_negotiation ) ); + assert ( ! is_pending ( &tls->validation ) ); /* (Re)initialise handshake context */ digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); @@ -1875,6 +1877,7 @@ static int tls_new_server_hello_done ( struct tls_connection *tls, "%s\n", tls, strerror ( rc ) ); return rc; } + pending_get ( &tls->validation ); return 0; } @@ -2582,10 +2585,10 @@ static int tls_progress ( struct tls_connection *tls, struct job_progress *progress ) { /* Return cipherstream or validator progress as applicable */ - if ( tls_ready ( tls ) ) { - return job_progress ( &tls->cipherstream, progress ); - } else { + if ( is_pending ( &tls->validation ) ) { return job_progress ( &tls->validator, progress ); + } else { + return job_progress ( &tls->cipherstream, progress ); } } @@ -2820,6 +2823,9 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) { struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; + /* Mark validation as complete */ + pending_put ( &tls->validation ); + /* Close validator interface */ intf_restart ( &tls->validator, rc ); -- cgit v1.2.3-55-g7522 From f6b2bf9507599709d30bcb74af9bffdb179e5338 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:29:06 +0000 Subject: [tcp] Display "connecting" status until connection is established Provide increased visibility into the progress of TCP connections by displaying an explicit "connecting" status message while waiting for the TCP handshake to complete. Signed-off-by: Michael Brown --- src/net/tcp.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src') diff --git a/src/net/tcp.c b/src/net/tcp.c index c445100ad..6bba44282 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1704,10 +1705,30 @@ static int tcp_xfer_deliver ( struct tcp_connection *tcp, return 0; } +/** + * Report job progress + * + * @v tcp TCP connection + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int tcp_progress ( struct tcp_connection *tcp, + struct job_progress *progress ) { + + /* Report connection in progress if applicable */ + if ( ! TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) { + snprintf ( progress->message, sizeof ( progress->message ), + "connecting" ); + } + + return 0; +} + /** TCP data transfer interface operations */ static struct interface_operation tcp_xfer_operations[] = { INTF_OP ( xfer_deliver, struct tcp_connection *, tcp_xfer_deliver ), INTF_OP ( xfer_window, struct tcp_connection *, tcp_xfer_window ), + INTF_OP ( job_progress, struct tcp_connection *, tcp_progress ), INTF_OP ( intf_close, struct tcp_connection *, tcp_xfer_close ), }; -- cgit v1.2.3-55-g7522 From b6ffe28a21c53a0946d95751c905d9e0b6c3b630 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Mar 2019 17:58:56 +0000 Subject: [ocsp] Accept response certID with missing hashAlgorithm parameters One of the design goals of ASN.1 DER is to provide a canonical serialization of a data structure, thereby allowing for equality of values to be tested by simply comparing the serialized bytes. Some OCSP servers will modify the request certID to omit the optional (and null) "parameters" portion of the hashAlgorithm. This is arguably legal but breaks the ability to perform a straightforward bitwise comparison on the entire certID field between request and response. Fix by comparing the OID-identified hashAlgorithm separately from the remaining certID fields. Originally-fixed-by: Thilo Fromm Signed-off-by: Michael Brown --- src/crypto/ocsp.c | 42 ++++++++++++++++++++++++++++++------------ src/include/ipxe/ocsp.h | 4 ++-- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index b83f4c035..2c747fb39 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -145,7 +145,7 @@ static void ocsp_free ( struct refcnt *refcnt ) { static int ocsp_request ( struct ocsp_check *ocsp ) { struct digest_algorithm *digest = &ocsp_digest_algorithm; struct asn1_builder *builder = &ocsp->request.builder; - struct asn1_cursor *cert_id = &ocsp->request.cert_id; + struct asn1_cursor *cert_id_tail = &ocsp->request.cert_id_tail; uint8_t digest_ctx[digest->ctxsize]; uint8_t name_digest[digest->digestsize]; uint8_t pubkey_digest[digest->digestsize]; @@ -186,12 +186,14 @@ static int ocsp_request ( struct ocsp_check *ocsp ) { DBGC2_HDA ( ocsp, 0, builder->data, builder->len ); /* Parse certificate ID for comparison with response */ - cert_id->data = builder->data; - cert_id->len = builder->len; - if ( ( rc = ( asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ), - asn1_enter ( cert_id, ASN1_SEQUENCE ) ) ) != 0 ) { + cert_id_tail->data = builder->data; + cert_id_tail->len = builder->len; + if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), + asn1_skip ( cert_id_tail, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; @@ -475,15 +477,31 @@ static int ocsp_parse_responder_id ( struct ocsp_check *ocsp, static int ocsp_parse_cert_id ( struct ocsp_check *ocsp, const struct asn1_cursor *raw ) { struct asn1_cursor cursor; + struct asn1_algorithm *algorithm; + int rc; - /* Check certID matches request */ + /* Check certID algorithm */ memcpy ( &cursor, raw, sizeof ( cursor ) ); - asn1_shrink_any ( &cursor ); - if ( asn1_compare ( &cursor, &ocsp->request.cert_id ) != 0 ) { + asn1_enter ( &cursor, ASN1_SEQUENCE ); + if ( ( rc = asn1_digest_algorithm ( &cursor, &algorithm ) ) != 0 ) { + DBGC ( ocsp, "OCSP %p \"%s\" certID unknown algorithm: %s\n", + ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); + return rc; + } + if ( algorithm->digest != &ocsp_digest_algorithm ) { + DBGC ( ocsp, "OCSP %p \"%s\" certID wrong algorithm %s\n", + ocsp, x509_name ( ocsp->cert ), + algorithm->digest->name ); + return -EACCES_CERT_MISMATCH; + } + + /* Check remaining certID fields */ + asn1_skip ( &cursor, ASN1_SEQUENCE ); + if ( asn1_compare ( &cursor, &ocsp->request.cert_id_tail ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n", ocsp, x509_name ( ocsp->cert ) ); - DBGC_HDA ( ocsp, 0, ocsp->request.cert_id.data, - ocsp->request.cert_id.len ); + DBGC_HDA ( ocsp, 0, ocsp->request.cert_id_tail.data, + ocsp->request.cert_id_tail.len ); DBGC_HDA ( ocsp, 0, cursor.data, cursor.len ); return -EACCES_CERT_MISMATCH; } diff --git a/src/include/ipxe/ocsp.h b/src/include/ipxe/ocsp.h index be0bddc50..9eb70b2cc 100644 --- a/src/include/ipxe/ocsp.h +++ b/src/include/ipxe/ocsp.h @@ -42,8 +42,8 @@ struct ocsp_check; struct ocsp_request { /** Request builder */ struct asn1_builder builder; - /** Certificate ID */ - struct asn1_cursor cert_id; + /** Certificate ID (excluding hashAlgorithm) */ + struct asn1_cursor cert_id_tail; }; /** An OCSP responder */ -- cgit v1.2.3-55-g7522 From ebf2eaf515e46abd43bc798e7e4ba77bfe529218 Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Thu, 14 Feb 2019 22:21:55 +0100 Subject: [intel] Add PCI ID for I219-V and -LM 6 to 9 Signed-off-by: Christian Nilsson Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index a2e6d535b..bb0b673b9 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1146,9 +1146,17 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), -- cgit v1.2.3-55-g7522 From afee77d816f42c7e405c065395c6a7f4dc2aade1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Apr 2019 14:43:23 +0100 Subject: [pci] Add support for PCI MSI-X interrupts The Intel 40 Gigabit Ethernet virtual functions support only MSI-X interrupts, and will write back completed interrupt descriptors only when the device attempts to raise an interrupt (or when a complete cacheline of receive descriptors has been completed). We cannot actually use MSI-X interrupts within iPXE, since we never have ownership of the APIC. However, an MSI-X interrupt is fundamentally just a DMA write of a single dword to an arbitrary address. We can therefore configure the device to "raise" an interrupt by writing a meaningless value to an otherwise unused memory location: this is sufficient to trigger the receive descriptor writeback logic. Signed-off-by: Michael Brown --- src/drivers/bus/pcimsix.c | 251 +++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/pci.h | 11 ++ src/include/ipxe/pcimsix.h | 77 ++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 src/drivers/bus/pcimsix.c create mode 100644 src/include/ipxe/pcimsix.h (limited to 'src') diff --git a/src/drivers/bus/pcimsix.c b/src/drivers/bus/pcimsix.c new file mode 100644 index 000000000..80893c418 --- /dev/null +++ b/src/drivers/bus/pcimsix.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2019 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** @file + * + * PCI MSI-X interrupts + * + */ + +/** + * Get MSI-X descriptor name (for debugging) + * + * @v cfg Configuration space offset + * @ret name Descriptor name + */ +static const char * pci_msix_name ( unsigned int cfg ) { + + switch ( cfg ) { + case PCI_MSIX_DESC_TABLE: return "table"; + case PCI_MSIX_DESC_PBA: return "PBA"; + default: return ""; + } +} + +/** + * Map MSI-X BAR portion + * + * @v pci PCI device + * @v msix MSI-X capability + * @v cfg Configuration space offset + * @ret io I/O address + */ +static void * pci_msix_ioremap ( struct pci_device *pci, struct pci_msix *msix, + unsigned int cfg ) { + uint32_t desc; + unsigned int bar; + unsigned long start; + unsigned long offset; + unsigned long base; + void *io; + + /* Read descriptor */ + pci_read_config_dword ( pci, ( msix->cap + cfg ), &desc ); + + /* Get BAR */ + bar = PCI_MSIX_DESC_BIR ( desc ); + offset = PCI_MSIX_DESC_OFFSET ( desc ); + start = pci_bar_start ( pci, PCI_BASE_ADDRESS ( bar ) ); + if ( ! start ) { + DBGC ( msix, "MSI-X %p %s could not find BAR%d\n", + msix, pci_msix_name ( cfg ), bar ); + return NULL; + } + base = ( start + offset ); + DBGC ( msix, "MSI-X %p %s at %#08lx (BAR%d+%#lx)\n", + msix, pci_msix_name ( cfg ), base, bar, offset ); + + /* Map BAR portion */ + io = ioremap ( ( start + offset ), PCI_MSIX_LEN ); + if ( ! io ) { + DBGC ( msix, "MSI-X %p %s could not map %#08lx\n", + msix, pci_msix_name ( cfg ), base ); + return NULL; + } + + return io; +} + +/** + * Enable MSI-X interrupts + * + * @v pci PCI device + * @v msix MSI-X capability + * @ret rc Return status code + */ +int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ) { + uint16_t ctrl; + int rc; + + /* Locate capability */ + msix->cap = pci_find_capability ( pci, PCI_CAP_ID_MSIX ); + if ( ! msix->cap ) { + DBGC ( msix, "MSI-X %p found no MSI-X capability in " + PCI_FMT "\n", msix, PCI_ARGS ( pci ) ); + rc = -ENOENT; + goto err_cap; + } + + /* Extract interrupt count */ + pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl ); + msix->count = ( PCI_MSIX_CTRL_SIZE ( ctrl ) + 1 ); + DBGC ( msix, "MSI-X %p has %d vectors for " PCI_FMT "\n", + msix, msix->count, PCI_ARGS ( pci ) ); + + /* Map MSI-X table */ + msix->table = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_TABLE ); + if ( ! msix->table ) { + rc = -ENOENT; + goto err_table; + } + + /* Map pending bit array */ + msix->pba = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_PBA ); + if ( ! msix->pba ) { + rc = -ENOENT; + goto err_pba; + } + + /* Enable MSI-X */ + ctrl &= ~PCI_MSIX_CTRL_MASK; + ctrl |= PCI_MSIX_CTRL_ENABLE; + pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl ); + + return 0; + + iounmap ( msix->pba ); + err_pba: + iounmap ( msix->table ); + err_table: + err_cap: + return rc; +} + +/** + * Disable MSI-X interrupts + * + * @v pci PCI device + * @v msix MSI-X capability + */ +void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ) { + uint16_t ctrl; + + /* Disable MSI-X */ + pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl ); + ctrl &= ~PCI_MSIX_CTRL_ENABLE; + pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl ); + + /* Unmap pending bit array */ + iounmap ( msix->pba ); + + /* Unmap MSI-X table */ + iounmap ( msix->table ); +} + +/** + * Map MSI-X interrupt vector + * + * @v msix MSI-X capability + * @v vector MSI-X vector + * @v address Message address + * @v data Message data + */ +void pci_msix_map ( struct pci_msix *msix, unsigned int vector, + physaddr_t address, uint32_t data ) { + void *base; + + /* Sanity check */ + assert ( vector < msix->count ); + + /* Map interrupt vector */ + base = ( msix->table + PCI_MSIX_VECTOR ( vector ) ); + writel ( ( address & 0xffffffffUL ), ( base + PCI_MSIX_ADDRESS_LO ) ); + if ( sizeof ( address ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) address ) >> 32 ), + ( base + PCI_MSIX_ADDRESS_HI ) ); + } else { + writel ( 0, ( base + PCI_MSIX_ADDRESS_HI ) ); + } + writel ( data, ( base + PCI_MSIX_DATA ) ); +} + +/** + * Control MSI-X interrupt vector + * + * @v msix MSI-X capability + * @v vector MSI-X vector + * @v mask Control mask + */ +void pci_msix_control ( struct pci_msix *msix, unsigned int vector, + uint32_t mask ) { + void *base; + uint32_t ctrl; + + /* Mask/unmask interrupt vector */ + base = ( msix->table + PCI_MSIX_VECTOR ( vector ) ); + ctrl = readl ( base + PCI_MSIX_CONTROL ); + ctrl &= ~PCI_MSIX_CONTROL_MASK; + ctrl |= mask; + writel ( ctrl, ( base + PCI_MSIX_CONTROL ) ); +} + +/** + * Dump MSI-X interrupt state (for debugging) + * + * @v msix MSI-X capability + * @v vector MSI-X vector + */ +void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ) { + void *base; + uint32_t address_hi; + uint32_t address_lo; + physaddr_t address; + uint32_t data; + uint32_t ctrl; + uint32_t pba; + + /* Do nothing in non-debug builds */ + if ( ! DBG_LOG ) + return; + + /* Mask/unmask interrupt vector */ + base = ( msix->table + PCI_MSIX_VECTOR ( vector ) ); + address_hi = readl ( base + PCI_MSIX_ADDRESS_HI ); + address_lo = readl ( base + PCI_MSIX_ADDRESS_LO ); + data = readl ( base + PCI_MSIX_DATA ); + ctrl = readl ( base + PCI_MSIX_CONTROL ); + pba = readl ( msix->pba ); + address = ( ( ( ( uint64_t ) address_hi ) << 32 ) | address_lo ); + DBGC ( msix, "MSI-X %p vector %d %#08x => %#08lx%s%s\n", + msix, vector, data, address, + ( ( ctrl & PCI_MSIX_CONTROL_MASK ) ? " (masked)" : "" ), + ( ( pba & ( 1 << vector ) ) ? " (pending)" : "" ) ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index ce67fc66d..02e13d11b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -205,6 +205,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 ) #define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 ) #define ERRFILE_intelxl ( ERRFILE_DRIVER | 0x00cb0000 ) +#define ERRFILE_pcimsix ( ERRFILE_DRIVER | 0x00cc0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index ddd8c8d1e..272c4c06f 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -94,6 +94,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_CAP_ID_VPD 0x03 /**< Vital product data */ #define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */ #define PCI_CAP_ID_EXP 0x10 /**< PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /**< MSI-X */ #define PCI_CAP_ID_EA 0x14 /**< Enhanced Allocation */ /** Next capability */ @@ -109,6 +110,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_EXP_DEVCTL 0x08 #define PCI_EXP_DEVCTL_FLR 0x8000 /**< Function level reset */ +/** MSI-X interrupts */ +#define PCI_MSIX_CTRL 0x02 +#define PCI_MSIX_CTRL_ENABLE 0x8000 /**< Enable MSI-X */ +#define PCI_MSIX_CTRL_MASK 0x4000 /**< Mask all interrupts */ +#define PCI_MSIX_CTRL_SIZE(x) ( (x) & 0x07ff ) /**< Table size */ +#define PCI_MSIX_DESC_TABLE 0x04 +#define PCI_MSIX_DESC_PBA 0x08 +#define PCI_MSIX_DESC_BIR(x) ( (x) & 0x00000007 ) /**< BAR index */ +#define PCI_MSIX_DESC_OFFSET(x) ( (x) & 0xfffffff8 ) /**< BAR offset */ + /** Uncorrectable error status */ #define PCI_ERR_UNCOR_STATUS 0x04 diff --git a/src/include/ipxe/pcimsix.h b/src/include/ipxe/pcimsix.h new file mode 100644 index 000000000..aa2aaf017 --- /dev/null +++ b/src/include/ipxe/pcimsix.h @@ -0,0 +1,77 @@ +#ifndef _IPXE_PCIMSIX_H +#define _IPXE_PCIMSIX_H + +/** @file + * + * PCI MSI-X interrupts + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** MSI-X BAR mapped length */ +#define PCI_MSIX_LEN 0x1000 + +/** MSI-X vector offset */ +#define PCI_MSIX_VECTOR(n) ( (n) * 0x10 ) + +/** MSI-X vector address low 32 bits */ +#define PCI_MSIX_ADDRESS_LO 0x0 + +/** MSI-X vector address high 32 bits */ +#define PCI_MSIX_ADDRESS_HI 0x4 + +/** MSI-X vector data */ +#define PCI_MSIX_DATA 0x8 + +/** MSI-X vector control */ +#define PCI_MSIX_CONTROL 0xc +#define PCI_MSIX_CONTROL_MASK 0x00000001 /**< Vector is masked */ + +/** PCI MSI-X capability */ +struct pci_msix { + /** Capability offset */ + unsigned int cap; + /** Number of vectors */ + unsigned int count; + /** MSI-X table */ + void *table; + /** Pending bit array */ + void *pba; +}; + +extern int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ); +extern void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ); +extern void pci_msix_map ( struct pci_msix *msix, unsigned int vector, + physaddr_t address, uint32_t data ); +extern void pci_msix_control ( struct pci_msix *msix, unsigned int vector, + uint32_t mask ); +extern void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ); + +/** + * Mask MSI-X interrupt vector + * + * @v msix MSI-X capability + * @v vector MSI-X vector + */ +static inline __attribute__ (( always_inline )) void +pci_msix_mask ( struct pci_msix *msix, unsigned int vector ) { + + pci_msix_control ( msix, vector, PCI_MSIX_CONTROL_MASK ); +} + +/** + * Unmask MSI-X interrupt vector + * + * @v msix MSI-X capability + * @v vector MSI-X vector + */ +static inline __attribute__ (( always_inline )) void +pci_msix_unmask ( struct pci_msix *msix, unsigned int vector ) { + + pci_msix_control ( msix, vector, 0 ); +} + +#endif /* _IPXE_PCIMSIX_H */ -- cgit v1.2.3-55-g7522 From fe680c8228563369804948010954128aacb7db74 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 27 Apr 2019 20:12:01 +0100 Subject: [vlan] Provide vlan_netdev_rx() and vlan_netdev_rx_err() The Hermon driver uses vlan_find() to identify the appropriate VLAN device for packets that are received with the VLAN tag already stripped out by the hardware. Generalise this capability and expose it for use by other network card drivers. Signed-off-by: Michael Brown --- src/drivers/infiniband/hermon.c | 18 ++++++---------- src/include/ipxe/vlan.h | 8 +++++-- src/net/netdevice.c | 34 +++++++++++++++++++++++------ src/net/vlan.c | 47 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index a1d2a3bd5..9675c156b 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -3207,22 +3207,16 @@ static void hermon_eth_complete_recv ( struct ib_device *ibdev __unused, struct ib_address_vector *source, struct io_buffer *iobuf, int rc ) { struct net_device *netdev = ib_qp_get_ownerdata ( qp ); - struct net_device *vlan; - - /* Find VLAN device, if applicable */ - if ( source->vlan_present ) { - if ( ( vlan = vlan_find ( netdev, source->vlan ) ) != NULL ) { - netdev = vlan; - } else if ( rc == 0 ) { - rc = -ENODEV; - } - } + unsigned int tag; + + /* Identify VLAN tag, if applicable */ + tag = ( source->vlan_present ? source->vlan : 0 ); /* Hand off to network layer */ if ( rc == 0 ) { - netdev_rx ( netdev, iobuf ); + vlan_netdev_rx ( netdev, tag, iobuf ); } else { - netdev_rx_err ( netdev, iobuf, rc ); + vlan_netdev_rx_err ( netdev, tag, iobuf, rc ); } } diff --git a/src/include/ipxe/vlan.h b/src/include/ipxe/vlan.h index 439e0c16d..7f93439b3 100644 --- a/src/include/ipxe/vlan.h +++ b/src/include/ipxe/vlan.h @@ -10,6 +10,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include + /** A VLAN header */ struct vlan_header { /** Tag control information */ @@ -59,12 +61,14 @@ struct vlan_header { */ #define VLAN_PRIORITY_IS_VALID( priority ) ( (priority) <= 7 ) -extern struct net_device * vlan_find ( struct net_device *trunk, - unsigned int tag ); extern unsigned int vlan_tag ( struct net_device *netdev ); extern int vlan_can_be_trunk ( struct net_device *trunk ); extern int vlan_create ( struct net_device *trunk, unsigned int tag, unsigned int priority ); extern int vlan_destroy ( struct net_device *netdev ); +extern void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag, + struct io_buffer *iobuf ); +extern void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag, + struct io_buffer *iobuf, int rc ); #endif /* _IPXE_VLAN_H */ diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 71a37eccc..3b02e64bd 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -1126,15 +1126,35 @@ __weak unsigned int vlan_tag ( struct net_device *netdev __unused ) { } /** - * Identify VLAN device (when VLAN support is not present) + * Add VLAN tag-stripped packet to queue (when VLAN support is not present) * - * @v trunk Trunk network device - * @v tag VLAN tag - * @ret netdev VLAN device, if any + * @v netdev Network device + * @v tag VLAN tag, or zero + * @v iobuf I/O buffer */ -__weak struct net_device * vlan_find ( struct net_device *trunk __unused, - unsigned int tag __unused ) { - return NULL; +__weak void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag, + struct io_buffer *iobuf ) { + + if ( tag == 0 ) { + netdev_rx ( netdev, iobuf ); + } else { + netdev_rx_err ( netdev, iobuf, -ENODEV ); + } +} + +/** + * Discard received VLAN tag-stripped packet (when VLAN support is not present) + * + * @v netdev Network device + * @v tag VLAN tag, or zero + * @v iobuf I/O buffer, or NULL + * @v rc Packet status code + */ +__weak void vlan_netdev_rx_err ( struct net_device *netdev, + unsigned int tag __unused, + struct io_buffer *iobuf, int rc ) { + + netdev_rx_err ( netdev, iobuf, rc ); } /** Networking stack process */ diff --git a/src/net/vlan.c b/src/net/vlan.c index f515c2dc9..90f2934de 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -199,7 +199,8 @@ static void vlan_sync ( struct net_device *netdev ) { * @v tag VLAN tag * @ret netdev VLAN device, if any */ -struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) { +static struct net_device * vlan_find ( struct net_device *trunk, + unsigned int tag ) { struct net_device *netdev; struct vlan_device *vlan; @@ -506,3 +507,47 @@ struct net_driver vlan_driver __net_driver = { .notify = vlan_notify, .remove = vlan_remove, }; + +/** + * Add VLAN tag-stripped packet to receive queue + * + * @v netdev Network device + * @v tag VLAN tag, or zero + * @v iobuf I/O buffer + */ +void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag, + struct io_buffer *iobuf ) { + struct net_device *vlan; + + /* Identify VLAN device, if applicable */ + if ( tag ) { + if ( ( vlan = vlan_find ( netdev, tag ) ) == NULL ) { + netdev_rx_err ( netdev, iobuf, -ENODEV ); + return; + } + netdev = vlan; + } + + /* Hand off to network device */ + netdev_rx ( netdev, iobuf ); +} + +/** + * Discard received VLAN tag-stripped packet + * + * @v netdev Network device + * @v tag VLAN tag, or zero + * @v iobuf I/O buffer, or NULL + * @v rc Packet status code + */ +void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag, + struct io_buffer *iobuf, int rc ) { + struct net_device *vlan; + + /* Identify VLAN device, if applicable */ + if ( tag && ( ( vlan = vlan_find ( netdev, tag ) ) != NULL ) ) + netdev = vlan; + + /* Hand off to network device */ + netdev_rx_err ( netdev, iobuf, rc ); +} -- cgit v1.2.3-55-g7522 From c901b5ca455659002cedb61a73fa080c9955e737 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 27 Apr 2019 20:21:22 +0100 Subject: [intelxl] Use VLAN tag in receive descriptor if present The physical function driver does not allow the virtual function to request that VLAN tags are left unstripped. Extract and use the VLAN tag from the receive descriptor if present. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 13 +++++++++++-- src/drivers/net/intelxl.h | 9 ++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 3e40fa4ae..66221cbaf 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -1254,6 +1255,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) { struct intelxl_rx_writeback_descriptor *rx_wb; struct io_buffer *iobuf; unsigned int rx_idx; + unsigned int tag; size_t len; /* Check for received packets */ @@ -1273,16 +1275,23 @@ static void intelxl_poll_rx ( struct net_device *netdev ) { len = INTELXL_RX_WB_LEN ( le32_to_cpu ( rx_wb->len ) ); iob_put ( iobuf, len ); + /* Find VLAN device, if applicable */ + if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_VLAN ) ) { + tag = VLAN_TAG ( le16_to_cpu ( rx_wb->vlan ) ); + } else { + tag = 0; + } + /* Hand off to network stack */ if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_RXE ) ) { DBGC ( intelxl, "INTELXL %p RX %d error (length %zd, " "flags %08x)\n", intelxl, rx_idx, len, le32_to_cpu ( rx_wb->flags ) ); - netdev_rx_err ( netdev, iobuf, -EIO ); + vlan_netdev_rx_err ( netdev, tag, iobuf, -EIO ); } else { DBGC2 ( intelxl, "INTELXL %p RX %d complete (length " "%zd)\n", intelxl, rx_idx, len ); - netdev_rx ( netdev, iobuf ); + vlan_netdev_rx ( netdev, tag, iobuf ); } intelxl->rx.cons++; } diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 02d9b98a2..fd3fc75b8 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -582,7 +582,11 @@ struct intelxl_rx_data_descriptor { /** Receive writeback descriptor */ struct intelxl_rx_writeback_descriptor { /** Reserved */ - uint8_t reserved[8]; + uint8_t reserved_a[2]; + /** VLAN tag */ + uint16_t vlan; + /** Reserved */ + uint8_t reserved_b[4]; /** Flags */ uint32_t flags; /** Length */ @@ -592,6 +596,9 @@ struct intelxl_rx_writeback_descriptor { /** Receive writeback descriptor complete */ #define INTELXL_RX_WB_FL_DD 0x00000001UL +/** Receive writeback descriptor VLAN tag present */ +#define INTELXL_RX_WB_FL_VLAN 0x00000004UL + /** Receive writeback descriptor error */ #define INTELXL_RX_WB_FL_RXE 0x00080000UL -- cgit v1.2.3-55-g7522 From c5ccfe79cf9188c4b4e55370f91c8377808008bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 22 Mar 2019 15:04:12 +0000 Subject: [intelxl] Allow for virtual function admin queue register maps The register map for the virtual functions appears to have been constructed using a random number generator. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 47 +++++++++++++++++++++++++++++++---------------- src/drivers/net/intelxl.h | 33 ++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 66221cbaf..1f0ca2c08 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -123,6 +123,15 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, ****************************************************************************** */ +/** Admin queue register offsets */ +static const struct intelxl_admin_offsets intelxl_admin_offsets = { + .bal = INTELXL_ADMIN_BAL, + .bah = INTELXL_ADMIN_BAH, + .len = INTELXL_ADMIN_LEN, + .head = INTELXL_ADMIN_HEAD, + .tail = INTELXL_ADMIN_TAIL, +}; + /** * Create admin queue * @@ -133,7 +142,8 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, static int intelxl_create_admin ( struct intelxl_nic *intelxl, struct intelxl_admin *admin ) { size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); - void *admin_regs = ( intelxl->regs + admin->reg ); + const struct intelxl_admin_offsets *regs = admin->regs; + void *admin_regs = ( intelxl->regs + admin->base ); physaddr_t address; /* Allocate admin queue */ @@ -147,30 +157,30 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, memset ( admin->desc, 0, len ); /* Reset head and tail registers */ - writel ( 0, admin_regs + INTELXL_ADMIN_HEAD ); - writel ( 0, admin_regs + INTELXL_ADMIN_TAIL ); + writel ( 0, admin_regs + regs->head ); + writel ( 0, admin_regs + regs->tail ); /* Reset queue index */ admin->index = 0; /* Program queue address */ address = virt_to_bus ( admin->desc ); - writel ( ( address & 0xffffffffUL ), admin_regs + INTELXL_ADMIN_BAL ); + writel ( ( address & 0xffffffffUL ), admin_regs + regs->bal ); if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { writel ( ( ( ( uint64_t ) address ) >> 32 ), - admin_regs + INTELXL_ADMIN_BAH ); + admin_regs + regs->bah ); } else { - writel ( 0, admin_regs + INTELXL_ADMIN_BAH ); + writel ( 0, admin_regs + regs->bah ); } /* Program queue length and enable queue */ writel ( ( INTELXL_ADMIN_LEN_LEN ( INTELXL_ADMIN_NUM_DESC ) | INTELXL_ADMIN_LEN_ENABLE ), - admin_regs + INTELXL_ADMIN_LEN ); + admin_regs + regs->len ); DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf " "[%08llx,%08llx)\n", intelxl, - ( ( admin->reg == INTELXL_ADMIN_CMD ) ? 'T' : 'R' ), + ( ( admin == &intelxl->command ) ? 'T' : 'R' ), ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ), ( ( unsigned long long ) virt_to_bus ( admin->buffer ) ), @@ -188,10 +198,11 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, struct intelxl_admin *admin ) { size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); - void *admin_regs = ( intelxl->regs + admin->reg ); + const struct intelxl_admin_offsets *regs = admin->regs; + void *admin_regs = ( intelxl->regs + admin->base ); /* Disable queue */ - writel ( 0, admin_regs + INTELXL_ADMIN_LEN ); + writel ( 0, admin_regs + regs->len ); /* Free queue */ free_dma ( admin->desc, ( len + sizeof ( *admin->buffer ) ) ); @@ -207,7 +218,8 @@ static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, static int intelxl_admin_command ( struct intelxl_nic *intelxl, struct intelxl_admin_descriptor *cmd ) { struct intelxl_admin *admin = &intelxl->command; - void *admin_regs = ( intelxl->regs + admin->reg ); + const struct intelxl_admin_offsets *regs = admin->regs; + void *admin_regs = ( intelxl->regs + admin->base ); struct intelxl_admin_descriptor *desc; uint64_t buffer; unsigned int index; @@ -245,7 +257,7 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl, /* Post command descriptor */ wmb(); - writel ( tail, admin_regs + INTELXL_ADMIN_TAIL ); + writel ( tail, admin_regs + regs->tail ); /* Wait for completion */ for ( i = 0 ; i < INTELXL_ADMIN_MAX_WAIT_MS ; i++ ) { @@ -558,13 +570,14 @@ static int intelxl_admin_link ( struct net_device *netdev ) { */ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) { struct intelxl_admin *admin = &intelxl->event; - void *admin_regs = ( intelxl->regs + admin->reg ); + const struct intelxl_admin_offsets *regs = admin->regs; + void *admin_regs = ( intelxl->regs + admin->base ); unsigned int tail; /* Update tail pointer */ tail = ( ( admin->index + INTELXL_ADMIN_NUM_DESC - 1 ) % INTELXL_ADMIN_NUM_DESC ); - writel ( tail, admin_regs + INTELXL_ADMIN_TAIL ); + writel ( tail, admin_regs + regs->tail ); } /** @@ -1383,8 +1396,10 @@ static int intelxl_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->pf = PCI_FUNC ( pci->busdevfn ); - intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD ); - intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT ); + intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, + &intelxl_admin_offsets ); + intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, + &intelxl_admin_offsets ); intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC, intelxl_context_tx ); intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index fd3fc75b8..138fc2ef0 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -53,6 +53,24 @@ struct intelxl_nic; /** Admin Queue Tail Register (offset) */ #define INTELXL_ADMIN_TAIL 0x400 +/** Admin queue register offsets + * + * The physical and virtual function register maps have no discernible + * relationship. + */ +struct intelxl_admin_offsets { + /** Base Address Low Register offset */ + unsigned int bal; + /** Base Address High Register offset */ + unsigned int bah; + /** Length Register offset */ + unsigned int len; + /** Head Register offset */ + unsigned int head; + /** Tail Register offset */ + unsigned int tail; +}; + /** Admin queue data buffer command parameters */ struct intelxl_admin_buffer_params { /** Reserved */ @@ -343,8 +361,10 @@ struct intelxl_admin { /** Queue index */ unsigned int index; - /** Register block */ - unsigned int reg; + /** Register block base */ + unsigned int base; + /** Register offsets */ + const struct intelxl_admin_offsets *regs; /** Data buffer */ union intelxl_admin_buffer *buffer; }; @@ -353,12 +373,15 @@ struct intelxl_admin { * Initialise admin queue * * @v admin Admin queue - * @v reg Register block + * @v base Register block base + * @v regs Register offsets */ static inline __attribute__ (( always_inline )) void -intelxl_init_admin ( struct intelxl_admin *admin, unsigned int reg ) { +intelxl_init_admin ( struct intelxl_admin *admin, unsigned int base, + const struct intelxl_admin_offsets *regs ) { - admin->reg = reg; + admin->base = base; + admin->regs = regs; } /** Number of admin queue descriptors */ -- cgit v1.2.3-55-g7522 From 8f3e648b6ce2b14940b8eb30efab3cb05820ca8a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 12:18:12 +0100 Subject: [intelxl] Use one admin queue buffer per admin queue descriptor We currently use a single data buffer shared between all admin queue descriptors. This works for the physical function driver since we have at most one command in progress and only a single event (which does not use a data buffer). The communication path between the physical and virtual function drivers uses the event data buffer, and there is no way to prevent a solicited event (i.e. a response to a request) from being overwritten by an unsolicited event (e.g. a link status change). Provide individual data buffers for each admin event queue descriptor (and for each admin command queue descriptor, for the sake of consistency). Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 309 +++++++++++++++++++++++++++++----------------- src/drivers/net/intelxl.h | 6 +- 2 files changed, 197 insertions(+), 118 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 1f0ca2c08..c0fb4c24e 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -141,17 +141,17 @@ static const struct intelxl_admin_offsets intelxl_admin_offsets = { */ static int intelxl_create_admin ( struct intelxl_nic *intelxl, struct intelxl_admin *admin ) { + size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC ); size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); const struct intelxl_admin_offsets *regs = admin->regs; void *admin_regs = ( intelxl->regs + admin->base ); physaddr_t address; /* Allocate admin queue */ - admin->desc = malloc_dma ( ( len + sizeof ( *admin->buffer ) ), - INTELXL_ALIGN ); - if ( ! admin->desc ) + admin->buf = malloc_dma ( ( buf_len + len ), INTELXL_ALIGN ); + if ( ! admin->buf ) return -ENOMEM; - admin->buffer = ( ( ( void * ) admin->desc ) + len ); + admin->desc = ( ( ( void * ) admin->buf ) + buf_len ); /* Initialise admin queue */ memset ( admin->desc, 0, len ); @@ -183,9 +183,9 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, ( ( admin == &intelxl->command ) ? 'T' : 'R' ), ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ), - ( ( unsigned long long ) virt_to_bus ( admin->buffer ) ), - ( ( unsigned long long ) ( virt_to_bus ( admin->buffer ) + - sizeof ( admin->buffer[0] ) ) ) ); + ( ( unsigned long long ) virt_to_bus ( admin->buf ) ), + ( ( unsigned long long ) ( virt_to_bus ( admin->buf ) + + buf_len ) ) ); return 0; } @@ -197,6 +197,7 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, */ static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, struct intelxl_admin *admin ) { + size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC ); size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); const struct intelxl_admin_offsets *regs = admin->regs; void *admin_regs = ( intelxl->regs + admin->base ); @@ -205,23 +206,80 @@ static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, writel ( 0, admin_regs + regs->len ); /* Free queue */ - free_dma ( admin->desc, ( len + sizeof ( *admin->buffer ) ) ); + free_dma ( admin->buf, ( buf_len + len ) ); +} + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +static struct intelxl_admin_descriptor * +intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin *admin = &intelxl->command; + struct intelxl_admin_descriptor *cmd; + + /* Get and initialise next descriptor */ + cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + memset ( cmd, 0, sizeof ( *cmd ) ); + return cmd; +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static union intelxl_admin_buffer * +intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ) { + struct intelxl_admin *admin = &intelxl->command; + union intelxl_admin_buffer *buf; + + /* Get next data buffer */ + buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + memset ( buf, 0, sizeof ( *buf ) ); + return buf; +} + +/** + * Initialise admin event queue descriptor + * + * @v intelxl Intel device + * @v index Event queue index + */ +static void intelxl_admin_event_init ( struct intelxl_nic *intelxl, + unsigned int index ) { + struct intelxl_admin *admin = &intelxl->event; + struct intelxl_admin_descriptor *evt; + union intelxl_admin_buffer *buf; + uint64_t address; + + /* Initialise descriptor */ + evt = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ]; + buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ]; + address = virt_to_bus ( buf ); + evt->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + evt->len = cpu_to_le16 ( sizeof ( *buf ) ); + evt->params.buffer.high = cpu_to_le32 ( address >> 32 ); + evt->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL ); } /** * Issue admin queue command * * @v intelxl Intel device - * @v cmd Command descriptor * @ret rc Return status code */ -static int intelxl_admin_command ( struct intelxl_nic *intelxl, - struct intelxl_admin_descriptor *cmd ) { +static int intelxl_admin_command ( struct intelxl_nic *intelxl ) { struct intelxl_admin *admin = &intelxl->command; const struct intelxl_admin_offsets *regs = admin->regs; void *admin_regs = ( intelxl->regs + admin->base ); - struct intelxl_admin_descriptor *desc; - uint64_t buffer; + struct intelxl_admin_descriptor *cmd; + union intelxl_admin_buffer *buf; + uint64_t address; + uint32_t cookie; unsigned int index; unsigned int tail; unsigned int i; @@ -230,32 +288,36 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl, /* Get next queue entry */ index = admin->index++; tail = ( admin->index % INTELXL_ADMIN_NUM_DESC ); - desc = &admin->desc[index % INTELXL_ADMIN_NUM_DESC]; - - /* Clear must-be-zero flags */ - cmd->flags &= ~cpu_to_le16 ( INTELXL_ADMIN_FL_DD | - INTELXL_ADMIN_FL_CMP | - INTELXL_ADMIN_FL_ERR ); - - /* Clear return value */ - cmd->ret = 0; + cmd = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ]; + buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ]; + DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x:\n", + intelxl, index, le16_to_cpu ( cmd->opcode ) ); - /* Populate cookie */ - cmd->cookie = cpu_to_le32 ( index ); + /* Sanity checks */ + assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_DD ) ) ); + assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_CMP ) ) ); + assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_ERR ) ) ); + assert ( cmd->ret == 0 ); /* Populate data buffer address if applicable */ if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { - buffer = virt_to_bus ( admin->buffer ); - cmd->params.buffer.high = cpu_to_le32 ( buffer >> 32 ); - cmd->params.buffer.low = cpu_to_le32 ( buffer & 0xffffffffUL ); + address = virt_to_bus ( buf ); + cmd->params.buffer.high = cpu_to_le32 ( address >> 32 ); + cmd->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL ); } - /* Copy command descriptor to queue entry */ - memcpy ( desc, cmd, sizeof ( *desc ) ); - DBGC2 ( intelxl, "INTELXL %p admin command %#x:\n", intelxl, index ); - DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + /* Populate cookie */ + cmd->cookie = cpu_to_le32 ( index ); + + /* Record cookie */ + cookie = cmd->cookie; /* Post command descriptor */ + DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, + le16_to_cpu ( cmd->len ) ); + } wmb(); writel ( tail, admin_regs + regs->tail ); @@ -263,36 +325,33 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl, for ( i = 0 ; i < INTELXL_ADMIN_MAX_WAIT_MS ; i++ ) { /* If response is not complete, delay 1ms and retry */ - if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) { + if ( ! ( cmd->flags & INTELXL_ADMIN_FL_DD ) ) { mdelay ( 1 ); continue; } DBGC2 ( intelxl, "INTELXL %p admin command %#x response:\n", intelxl, index ); - DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, - sizeof ( *desc ) ); + DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, + sizeof ( *cmd ) ); /* Check for cookie mismatch */ - if ( desc->cookie != cmd->cookie ) { + if ( cmd->cookie != cookie ) { DBGC ( intelxl, "INTELXL %p admin command %#x bad " "cookie %#x\n", intelxl, index, - le32_to_cpu ( desc->cookie ) ); + le32_to_cpu ( cmd->cookie ) ); rc = -EPROTO; goto err; } /* Check for errors */ - if ( desc->ret != 0 ) { + if ( cmd->ret != 0 ) { DBGC ( intelxl, "INTELXL %p admin command %#x error " "%d\n", intelxl, index, - le16_to_cpu ( desc->ret ) ); + le16_to_cpu ( cmd->ret ) ); rc = -EIO; goto err; } - /* Copy response back to command descriptor */ - memcpy ( cmd, desc, sizeof ( *cmd ) ); - /* Success */ return 0; } @@ -301,8 +360,7 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl, DBGC ( intelxl, "INTELXL %p timed out waiting for admin command %#x:\n", intelxl, index ); err: - DBGC_HDA ( intelxl, virt_to_phys ( desc ), cmd, sizeof ( *cmd ) ); - DBGC_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + DBGC_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); return rc; } @@ -313,17 +371,18 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl, * @ret rc Return status code */ static int intelxl_admin_version ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_version_params *version = &cmd.params.version; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_version_params *version; unsigned int api; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + version = &cmd->params.version; /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; api = le16_to_cpu ( version->api.major ); DBGC ( intelxl, "INTELXL %p firmware v%d.%d API v%d.%d\n", @@ -348,24 +407,25 @@ static int intelxl_admin_version ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_driver_params *driver = &cmd.params.driver; - struct intelxl_admin_driver_buffer *buf = - &intelxl->command.buffer->driver; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_driver_params *driver; + union intelxl_admin_buffer *buf; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER ); - cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); - cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->driver ) ); + driver = &cmd->params.driver; driver->major = product_major_version; driver->minor = product_minor_version; - snprintf ( buf->name, sizeof ( buf->name ), "%s", + buf = intelxl_admin_command_buffer ( intelxl ); + snprintf ( buf->driver.name, sizeof ( buf->driver.name ), "%s", ( product_name[0] ? product_name : product_short_name ) ); /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; return 0; @@ -378,17 +438,18 @@ static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_shutdown_params *shutdown = &cmd.params.shutdown; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_shutdown_params *shutdown; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN ); + shutdown = &cmd->params.shutdown; shutdown->unloading = INTELXL_ADMIN_SHUTDOWN_UNLOADING; /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; return 0; @@ -401,36 +462,38 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_switch_params *sw = &cmd.params.sw; - struct intelxl_admin_switch_buffer *buf = &intelxl->command.buffer->sw; - struct intelxl_admin_switch_config *cfg = &buf->cfg; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_switch_params *sw; + union intelxl_admin_buffer *buf; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); - cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); - cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) ); + sw = &cmd->params.sw; + buf = intelxl_admin_command_buffer ( intelxl ); /* Get each configuration in turn */ do { /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; /* Dump raw configuration */ DBGC2 ( intelxl, "INTELXL %p SEID %#04x:\n", - intelxl, le16_to_cpu ( cfg->seid ) ); - DBGC2_HDA ( intelxl, 0, cfg, sizeof ( *cfg ) ); + intelxl, le16_to_cpu ( buf->sw.cfg.seid ) ); + DBGC2_HDA ( intelxl, 0, &buf->sw.cfg, sizeof ( buf->sw.cfg ) ); /* Parse response */ - if ( cfg->type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) { - intelxl->vsi = le16_to_cpu ( cfg->seid ); + if ( buf->sw.cfg.type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) { + intelxl->vsi = le16_to_cpu ( buf->sw.cfg.seid ); DBGC ( intelxl, "INTELXL %p VSI %#04x uplink %#04x " "downlink %#04x conn %#02x\n", intelxl, - intelxl->vsi, le16_to_cpu ( cfg->uplink ), - le16_to_cpu ( cfg->downlink ), cfg->connection ); + intelxl->vsi, le16_to_cpu ( buf->sw.cfg.uplink ), + le16_to_cpu ( buf->sw.cfg.downlink ), + buf->sw.cfg.connection ); } } while ( sw->next ); @@ -451,25 +514,27 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_vsi_params *vsi = &cmd.params.vsi; - struct intelxl_admin_vsi_buffer *buf = &intelxl->command.buffer->vsi; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_vsi_params *vsi; + union intelxl_admin_buffer *buf; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI ); - cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); - cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->vsi ) ); + vsi = &cmd->params.vsi; vsi->vsi = cpu_to_le16 ( intelxl->vsi ); + buf = intelxl_admin_command_buffer ( intelxl ); /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; /* Parse response */ - intelxl->queue = le16_to_cpu ( buf->queue[0] ); - intelxl->qset = le16_to_cpu ( buf->qset[0] ); + intelxl->queue = le16_to_cpu ( buf->vsi.queue[0] ); + intelxl->qset = le16_to_cpu ( buf->vsi.qset[0] ); DBGC ( intelxl, "INTELXL %p VSI %#04x queue %#04x qset %#04x\n", intelxl, intelxl->vsi, intelxl->queue, intelxl->qset ); @@ -483,24 +548,25 @@ static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_promisc_params *promisc = &cmd.params.promisc; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_promisc_params *promisc; uint16_t flags; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC ); flags = ( INTELXL_ADMIN_PROMISC_FL_UNICAST | INTELXL_ADMIN_PROMISC_FL_MULTICAST | INTELXL_ADMIN_PROMISC_FL_BROADCAST | INTELXL_ADMIN_PROMISC_FL_VLAN ); + promisc = &cmd->params.promisc; promisc->flags = cpu_to_le16 ( flags ); promisc->valid = cpu_to_le16 ( flags ); promisc->vsi = cpu_to_le16 ( intelxl->vsi ); /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; return 0; @@ -513,18 +579,19 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { * @ret rc Return status code */ static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_autoneg_params *autoneg = &cmd.params.autoneg; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_autoneg_params *autoneg; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG ); + autoneg = &cmd->params.autoneg; autoneg->flags = ( INTELXL_ADMIN_AUTONEG_FL_RESTART | INTELXL_ADMIN_AUTONEG_FL_ENABLE ); /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; return 0; @@ -538,17 +605,18 @@ static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) { */ static int intelxl_admin_link ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - struct intelxl_admin_descriptor cmd; - struct intelxl_admin_link_params *link = &cmd.params.link; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_link_params *link; int rc; /* Populate descriptor */ - memset ( &cmd, 0, sizeof ( cmd ) ); - cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + link = &cmd->params.link; link->notify = INTELXL_ADMIN_LINK_NOTIFY; /* Issue command */ - if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; DBGC ( intelxl, "INTELXL %p PHY %#02x speed %#02x status %#02x\n", intelxl, link->phy, link->speed, link->status ); @@ -577,6 +645,7 @@ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) { /* Update tail pointer */ tail = ( ( admin->index + INTELXL_ADMIN_NUM_DESC - 1 ) % INTELXL_ADMIN_NUM_DESC ); + wmb(); writel ( tail, admin_regs + regs->tail ); } @@ -588,39 +657,42 @@ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) { static void intelxl_poll_admin ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin *admin = &intelxl->event; - struct intelxl_admin_descriptor *desc; + struct intelxl_admin_descriptor *evt; + union intelxl_admin_buffer *buf; /* Check for events */ while ( 1 ) { - /* Get next event descriptor */ - desc = &admin->desc[admin->index % INTELXL_ADMIN_NUM_DESC]; + /* Get next event descriptor and data buffer */ + evt = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ]; /* Stop if descriptor is not yet completed */ - if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) + if ( ! ( evt->flags & INTELXL_ADMIN_FL_DD ) ) return; DBGC2 ( intelxl, "INTELXL %p admin event %#x:\n", intelxl, admin->index ); - DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, - sizeof ( *desc ) ); + DBGC2_HDA ( intelxl, virt_to_phys ( evt ), evt, + sizeof ( *evt ) ); + if ( evt->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, + le16_to_cpu ( evt->len ) ); + } /* Handle event */ - switch ( desc->opcode ) { + switch ( evt->opcode ) { case cpu_to_le16 ( INTELXL_ADMIN_LINK ): intelxl_admin_link ( netdev ); break; default: DBGC ( intelxl, "INTELXL %p admin event %#x " "unrecognised opcode %#04x\n", intelxl, - admin->index, le16_to_cpu ( desc->opcode ) ); + admin->index, le16_to_cpu ( evt->opcode ) ); break; } - /* Clear event completion flag */ - desc->flags = 0; - wmb(); - - /* Update index and refill queue */ + /* Reset descriptor and refill queue */ + intelxl_admin_event_init ( intelxl, admin->index ); admin->index++; intelxl_refill_admin ( intelxl ); } @@ -633,6 +705,7 @@ static void intelxl_poll_admin ( struct net_device *netdev ) { * @ret rc Return status code */ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { + unsigned int i; int rc; /* Create admin event queue */ @@ -643,6 +716,10 @@ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->command ) ) != 0 ) goto err_create_command; + /* Initialise all admin event queue descriptors */ + for ( i = 0 ; i < INTELXL_ADMIN_NUM_DESC ; i++ ) + intelxl_admin_event_init ( intelxl, i ); + /* Post all descriptors to event queue */ intelxl_refill_admin ( intelxl ); diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 138fc2ef0..0175c1f9d 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -319,6 +319,8 @@ union intelxl_admin_buffer { struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ struct intelxl_admin_vsi_buffer vsi; + /** Alignment padding */ + uint8_t pad[INTELXL_ALIGN]; } __attribute__ (( packed )); /** Admin queue descriptor */ @@ -358,6 +360,8 @@ struct intelxl_admin_descriptor { struct intelxl_admin { /** Descriptors */ struct intelxl_admin_descriptor *desc; + /** Data buffers */ + union intelxl_admin_buffer *buf; /** Queue index */ unsigned int index; @@ -365,8 +369,6 @@ struct intelxl_admin { unsigned int base; /** Register offsets */ const struct intelxl_admin_offsets *regs; - /** Data buffer */ - union intelxl_admin_buffer *buffer; }; /** -- cgit v1.2.3-55-g7522 From 7b68c310f94af6466579a47dfbde38d90275f192 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 12:45:37 +0100 Subject: [intelxl] Allow admin queues to be reinitialised A virtual function reset is triggered via an admin queue command and will reset the admin queue configuration registers. Allow the admin queues to be reinitialised after such a reset, without requiring the overhead (and potential failure paths) of freeing and reallocating the queues. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 126 +++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index c0fb4c24e..a4dca5fb5 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -45,6 +45,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +static void intelxl_reopen_admin ( struct intelxl_nic *intelxl ); + /****************************************************************************** * * Device reset @@ -133,19 +135,16 @@ static const struct intelxl_admin_offsets intelxl_admin_offsets = { }; /** - * Create admin queue + * Allocate admin queue * * @v intelxl Intel device * @v admin Admin queue * @ret rc Return status code */ -static int intelxl_create_admin ( struct intelxl_nic *intelxl, - struct intelxl_admin *admin ) { +static int intelxl_alloc_admin ( struct intelxl_nic *intelxl, + struct intelxl_admin *admin ) { size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC ); size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); - const struct intelxl_admin_offsets *regs = admin->regs; - void *admin_regs = ( intelxl->regs + admin->base ); - physaddr_t address; /* Allocate admin queue */ admin->buf = malloc_dma ( ( buf_len + len ), INTELXL_ALIGN ); @@ -153,6 +152,30 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, return -ENOMEM; admin->desc = ( ( ( void * ) admin->buf ) + buf_len ); + DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf " + "[%08llx,%08llx)\n", intelxl, + ( ( admin == &intelxl->command ) ? 'T' : 'R' ), + ( ( unsigned long long ) virt_to_bus ( admin->desc ) ), + ( ( unsigned long long ) ( virt_to_bus ( admin->desc ) + len ) ), + ( ( unsigned long long ) virt_to_bus ( admin->buf ) ), + ( ( unsigned long long ) ( virt_to_bus ( admin->buf ) + + buf_len ) ) ); + return 0; +} + +/** + * Enable admin queue + * + * @v intelxl Intel device + * @v admin Admin queue + */ +static void intelxl_enable_admin ( struct intelxl_nic *intelxl, + struct intelxl_admin *admin ) { + size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); + const struct intelxl_admin_offsets *regs = admin->regs; + void *admin_regs = ( intelxl->regs + admin->base ); + physaddr_t address; + /* Initialise admin queue */ memset ( admin->desc, 0, len ); @@ -177,33 +200,33 @@ static int intelxl_create_admin ( struct intelxl_nic *intelxl, writel ( ( INTELXL_ADMIN_LEN_LEN ( INTELXL_ADMIN_NUM_DESC ) | INTELXL_ADMIN_LEN_ENABLE ), admin_regs + regs->len ); - - DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf " - "[%08llx,%08llx)\n", intelxl, - ( ( admin == &intelxl->command ) ? 'T' : 'R' ), - ( ( unsigned long long ) address ), - ( ( unsigned long long ) address + len ), - ( ( unsigned long long ) virt_to_bus ( admin->buf ) ), - ( ( unsigned long long ) ( virt_to_bus ( admin->buf ) + - buf_len ) ) ); - return 0; } /** - * Destroy admin queue + * Disable admin queue * * @v intelxl Intel device * @v admin Admin queue */ -static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, +static void intelxl_disable_admin ( struct intelxl_nic *intelxl, struct intelxl_admin *admin ) { - size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC ); - size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); const struct intelxl_admin_offsets *regs = admin->regs; void *admin_regs = ( intelxl->regs + admin->base ); /* Disable queue */ writel ( 0, admin_regs + regs->len ); +} + +/** + * Free admin queue + * + * @v intelxl Intel device + * @v admin Admin queue + */ +static void intelxl_free_admin ( struct intelxl_nic *intelxl __unused, + struct intelxl_admin *admin ) { + size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC ); + size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); /* Free queue */ free_dma ( admin->buf, ( buf_len + len ) ); @@ -705,23 +728,18 @@ static void intelxl_poll_admin ( struct net_device *netdev ) { * @ret rc Return status code */ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { - unsigned int i; int rc; - /* Create admin event queue */ - if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->event ) ) != 0 ) - goto err_create_event; + /* Allocate admin event queue */ + if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->event ) ) != 0 ) + goto err_alloc_event; - /* Create admin command queue */ - if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->command ) ) != 0 ) - goto err_create_command; - - /* Initialise all admin event queue descriptors */ - for ( i = 0 ; i < INTELXL_ADMIN_NUM_DESC ; i++ ) - intelxl_admin_event_init ( intelxl, i ); + /* Allocate admin command queue */ + if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->command ) ) != 0 ) + goto err_alloc_command; - /* Post all descriptors to event queue */ - intelxl_refill_admin ( intelxl ); + /* (Re)open admin queues */ + intelxl_reopen_admin ( intelxl ); /* Get firmware version */ if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) @@ -735,13 +753,37 @@ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { err_driver: err_version: - intelxl_destroy_admin ( intelxl, &intelxl->command ); - err_create_command: - intelxl_destroy_admin ( intelxl, &intelxl->event ); - err_create_event: + intelxl_disable_admin ( intelxl, &intelxl->command ); + intelxl_disable_admin ( intelxl, &intelxl->event ); + intelxl_free_admin ( intelxl, &intelxl->command ); + err_alloc_command: + intelxl_free_admin ( intelxl, &intelxl->event ); + err_alloc_event: return rc; } +/** + * Reopen admin queues (after virtual function reset) + * + * @v intelxl Intel device + */ +static void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) { + unsigned int i; + + /* Enable admin event queue */ + intelxl_enable_admin ( intelxl, &intelxl->event ); + + /* Enable admin command queue */ + intelxl_enable_admin ( intelxl, &intelxl->command ); + + /* Initialise all admin event queue descriptors */ + for ( i = 0 ; i < INTELXL_ADMIN_NUM_DESC ; i++ ) + intelxl_admin_event_init ( intelxl, i ); + + /* Post all descriptors to event queue */ + intelxl_refill_admin ( intelxl ); +} + /** * Close admin queues * @@ -752,11 +794,13 @@ static void intelxl_close_admin ( struct intelxl_nic *intelxl ) { /* Shut down admin queues */ intelxl_admin_shutdown ( intelxl ); - /* Destroy admin command queue */ - intelxl_destroy_admin ( intelxl, &intelxl->command ); + /* Disable admin queues */ + intelxl_disable_admin ( intelxl, &intelxl->command ); + intelxl_disable_admin ( intelxl, &intelxl->event ); - /* Destroy admin event queue */ - intelxl_destroy_admin ( intelxl, &intelxl->event ); + /* Free admin queues */ + intelxl_free_admin ( intelxl, &intelxl->command ); + intelxl_free_admin ( intelxl, &intelxl->event ); } /****************************************************************************** -- cgit v1.2.3-55-g7522 From 17298d01211a8611b9206086fff04624297a4197 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 13:00:12 +0100 Subject: [intelxl] Allow admin cookie to hold extended opcode and return code The "send to PF" and "send to VF" admin queue descriptors (ab)use the cookie field to hold the extended opcode and return code values. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 10 +++++++--- src/drivers/net/intelxl.h | 13 +++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index a4dca5fb5..1cf5cad4b 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -313,8 +313,11 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl ) { tail = ( admin->index % INTELXL_ADMIN_NUM_DESC ); cmd = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ]; buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ]; - DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x:\n", + DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x", intelxl, index, le16_to_cpu ( cmd->opcode ) ); + if ( cmd->vopcode ) + DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->vopcode ) ); + DBGC2 ( intelxl, ":\n" ); /* Sanity checks */ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_DD ) ) ); @@ -329,8 +332,9 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl ) { cmd->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL ); } - /* Populate cookie */ - cmd->cookie = cpu_to_le32 ( index ); + /* Populate cookie, if not being (ab)used for VF opcode */ + if ( ! cmd->vopcode ) + cmd->cookie = cpu_to_le32 ( index ); /* Record cookie */ cookie = cmd->cookie; diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 0175c1f9d..5067b8074 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -333,10 +333,15 @@ struct intelxl_admin_descriptor { uint16_t len; /** Return value */ uint16_t ret; - /** Cookie */ - uint32_t cookie; - /** Reserved */ - uint32_t reserved; + /** Opaque cookie / VF opcode */ + union { + /** Cookie */ + uint32_t cookie; + /** VF opcode */ + uint32_t vopcode; + }; + /** VF return value */ + int32_t vret; /** Parameters */ union intelxl_admin_params params; } __attribute__ (( packed )); -- cgit v1.2.3-55-g7522 From 7676924571a1c7623d4ffc42560663db91a36bc3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 13:09:43 +0100 Subject: [intelxl] Provide a mechanism for handling "send to VF" events Provide a weak stub function for handling the "send to VF" event used for communications between the physical and virtual function drivers. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 18 ++++++++++++++++++ src/drivers/net/intelxl.h | 10 ++++++++++ 2 files changed, 28 insertions(+) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 1cf5cad4b..5cf89aa12 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -658,6 +658,21 @@ static int intelxl_admin_link ( struct net_device *netdev ) { return 0; } +/** + * Handle virtual function event (when VF driver is not present) + * + * @v netdev Network device + * @v evt Admin queue event descriptor + * @v buf Admin queue event data buffer + */ +__weak void +intelxlvf_admin_event ( struct net_device *netdev __unused, + struct intelxl_admin_descriptor *evt __unused, + union intelxl_admin_buffer *buf __unused ) { + + /* Nothing to do */ +} + /** * Refill admin event queue * @@ -711,6 +726,9 @@ static void intelxl_poll_admin ( struct net_device *netdev ) { case cpu_to_le16 ( INTELXL_ADMIN_LINK ): intelxl_admin_link ( netdev ); break; + case cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ): + intelxlvf_admin_event ( netdev, evt, buf ); + break; default: DBGC ( intelxl, "INTELXL %p admin event %#x " "unrecognised opcode %#04x\n", intelxl, diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 5067b8074..6bd5e3e8b 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -289,6 +289,12 @@ struct intelxl_admin_link_params { /** Link is up */ #define INTELXL_ADMIN_LINK_UP 0x01 +/** Admin queue Send Message to PF command */ +#define INTELXL_ADMIN_SEND_TO_PF 0x0801 + +/** Admin queue Send Message to VF command */ +#define INTELXL_ADMIN_SEND_TO_VF 0x0802 + /** Admin queue command parameters */ union intelxl_admin_params { /** Additional data buffer command parameters */ @@ -824,4 +830,8 @@ struct intelxl_nic { struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; }; +extern void intelxlvf_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ); + #endif /* _INTELXL_H */ -- cgit v1.2.3-55-g7522 From f460a436caa46045442e0d368b2913f7f25dd0b2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 16:25:47 +0100 Subject: [intelxl] Use 32-byte receive descriptors The physical function driver does not allow the virtual function to request the use of 16-byte receive descriptors. Switch to using 32-byte receive descriptors. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 26 ++++++++++++++------------ src/drivers/net/intelxl.h | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 5cf89aa12..a218bdbd4 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -1018,7 +1018,7 @@ static int intelxl_context_rx ( struct intelxl_nic *intelxl, base_count = INTELXL_CTX_RX_BASE_COUNT ( address, INTELXL_RX_NUM_DESC ); ctx.rx.base_count = cpu_to_le64 ( base_count ); ctx.rx.len = cpu_to_le16 ( INTELXL_CTX_RX_LEN ( intelxl->mfs ) ); - ctx.rx.flags = INTELXL_CTX_RX_FL_CRCSTRIP; + ctx.rx.flags = ( INTELXL_CTX_RX_FL_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP ); ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) ); /* Program context */ @@ -1101,20 +1101,20 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, int rc; /* Allocate descriptor ring */ - ring->desc = malloc_dma ( ring->len, INTELXL_ALIGN ); - if ( ! ring->desc ) { + ring->desc.raw = malloc_dma ( ring->len, INTELXL_ALIGN ); + if ( ! ring->desc.raw ) { rc = -ENOMEM; goto err_alloc; } /* Initialise descriptor ring */ - memset ( ring->desc, 0, ring->len ); + memset ( ring->desc.raw, 0, ring->len ); /* Reset tail pointer */ writel ( 0, ( ring_regs + INTELXL_QXX_TAIL ) ); /* Program queue context */ - address = virt_to_bus ( ring->desc ); + address = virt_to_bus ( ring->desc.raw ); if ( ( rc = ring->context ( intelxl, address ) ) != 0 ) goto err_context; @@ -1135,7 +1135,7 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, intelxl_disable_ring ( intelxl, ring ); err_enable: err_context: - free_dma ( ring->desc, ring->len ); + free_dma ( ring->desc.raw, ring->len ); err_alloc: return rc; } @@ -1157,8 +1157,8 @@ static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, } /* Free descriptor ring */ - free_dma ( ring->desc, ring->len ); - ring->desc = NULL; + free_dma ( ring->desc.raw, ring->len ); + ring->desc.raw = NULL; } /** @@ -1186,7 +1186,7 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) { /* Get next receive descriptor */ rx_idx = ( intelxl->rx.prod++ % INTELXL_RX_NUM_DESC ); - rx = &intelxl->rx.desc[rx_idx].rx; + rx = &intelxl->rx.desc.rx[rx_idx].data; /* Populate receive descriptor */ address = virt_to_bus ( iobuf->data ); @@ -1351,7 +1351,7 @@ static int intelxl_transmit ( struct net_device *netdev, } tx_idx = ( intelxl->tx.prod++ % INTELXL_TX_NUM_DESC ); tx_tail = ( intelxl->tx.prod % INTELXL_TX_NUM_DESC ); - tx = &intelxl->tx.desc[tx_idx].tx; + tx = &intelxl->tx.desc.tx[tx_idx].data; /* Populate transmit descriptor */ address = virt_to_bus ( iobuf->data ); @@ -1387,7 +1387,7 @@ static void intelxl_poll_tx ( struct net_device *netdev ) { /* Get next transmit descriptor */ tx_idx = ( intelxl->tx.cons % INTELXL_TX_NUM_DESC ); - tx_wb = &intelxl->tx.desc[tx_idx].tx_wb; + tx_wb = &intelxl->tx.desc.tx[tx_idx].wb; /* Stop if descriptor is still in use */ if ( ! ( tx_wb->flags & INTELXL_TX_WB_FL_DD ) ) @@ -1419,7 +1419,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) { /* Get next receive descriptor */ rx_idx = ( intelxl->rx.cons % INTELXL_RX_NUM_DESC ); - rx_wb = &intelxl->rx.desc[rx_idx].rx_wb; + rx_wb = &intelxl->rx.desc.rx[rx_idx].wb; /* Stop if descriptor is still in use */ if ( ! ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_DD ) ) ) @@ -1544,8 +1544,10 @@ static int intelxl_probe ( struct pci_device *pci ) { intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, &intelxl_admin_offsets ); intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC, + sizeof ( intelxl->tx.desc.tx[0] ), intelxl_context_tx ); intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + sizeof ( intelxl->rx.desc.rx[0] ), intelxl_context_rx ); /* Fix up PCI device */ diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 6bd5e3e8b..883728e32 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -502,6 +502,9 @@ struct intelxl_context_rx { /** Receive queue data buffer length */ #define INTELXL_CTX_RX_LEN( len ) ( (len) >> 1 ) +/** Use 32-byte receive descriptors */ +#define INTELXL_CTX_RX_FL_DSIZE 0x10 + /** Strip CRC from received packets */ #define INTELXL_CTX_RX_FL_CRCSTRIP 0x20 @@ -605,6 +608,14 @@ struct intelxl_tx_writeback_descriptor { /** Transmit writeback descriptor complete */ #define INTELXL_TX_WB_FL_DD 0x01 +/** Transmit descriptor */ +union intelxl_tx_descriptor { + /** Transmit data descriptor */ + struct intelxl_tx_data_descriptor data; + /** Transmit writeback descriptor */ + struct intelxl_tx_writeback_descriptor wb; +}; + /** Receive data descriptor */ struct intelxl_rx_data_descriptor { /** Buffer address */ @@ -612,7 +623,7 @@ struct intelxl_rx_data_descriptor { /** Flags */ uint32_t flags; /** Reserved */ - uint8_t reserved[4]; + uint8_t reserved[20]; } __attribute__ (( packed )); /** Receive writeback descriptor */ @@ -627,6 +638,8 @@ struct intelxl_rx_writeback_descriptor { uint32_t flags; /** Length */ uint32_t len; + /** Reserved */ + uint8_t reserved_c[16]; } __attribute__ (( packed )); /** Receive writeback descriptor complete */ @@ -642,21 +655,24 @@ struct intelxl_rx_writeback_descriptor { #define INTELXL_RX_WB_LEN(len) ( ( (len) >> 6 ) & 0x3fff ) /** Packet descriptor */ -union intelxl_descriptor { - /** Transmit data descriptor */ - struct intelxl_tx_data_descriptor tx; - /** Transmit writeback descriptor */ - struct intelxl_tx_writeback_descriptor tx_wb; +union intelxl_rx_descriptor { /** Receive data descriptor */ - struct intelxl_rx_data_descriptor rx; + struct intelxl_rx_data_descriptor data; /** Receive writeback descriptor */ - struct intelxl_rx_writeback_descriptor rx_wb; + struct intelxl_rx_writeback_descriptor wb; }; /** Descriptor ring */ struct intelxl_ring { /** Descriptors */ - union intelxl_descriptor *desc; + union { + /** Transmit descriptors */ + union intelxl_tx_descriptor *tx; + /** Receive descriptors */ + union intelxl_rx_descriptor *rx; + /** Raw data */ + void *raw; + } desc; /** Producer index */ unsigned int prod; /** Consumer index */ @@ -679,14 +695,15 @@ struct intelxl_ring { * * @v ring Descriptor ring * @v count Number of descriptors + * @v len Length of a single descriptor * @v context Method to program queue context */ static inline __attribute__ (( always_inline)) void -intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, +intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, int ( * context ) ( struct intelxl_nic *intelxl, physaddr_t address ) ) { - ring->len = ( count * sizeof ( ring->desc[0] ) ); + ring->len = ( count * len ); ring->context = context; } -- cgit v1.2.3-55-g7522 From 9907fd54d358a993cbc389d6d956c1a2da923df9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 16:36:24 +0100 Subject: [intelxl] Allow for arbitrary placement of ring tail registers The virtual function transmit and receive ring tail register offsets do not match those of the physical function. Allow the tail register offsets to be specified separately. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 14 +++++++------- src/drivers/net/intelxl.h | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index a218bdbd4..ed6e64a6e 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -1096,7 +1096,6 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, */ static int intelxl_create_ring ( struct intelxl_nic *intelxl, struct intelxl_ring *ring ) { - void *ring_regs = ( intelxl->regs + ring->reg ); physaddr_t address; int rc; @@ -1111,7 +1110,7 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, memset ( ring->desc.raw, 0, ring->len ); /* Reset tail pointer */ - writel ( 0, ( ring_regs + INTELXL_QXX_TAIL ) ); + writel ( 0, ( intelxl->regs + ring->tail ) ); /* Program queue context */ address = virt_to_bus ( ring->desc.raw ); @@ -1127,7 +1126,8 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, ring->cons = 0; DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n", - intelxl, ring->reg, ( ( unsigned long long ) address ), + intelxl, ( ring->reg + ring->tail ), + ( ( unsigned long long ) address ), ( ( unsigned long long ) address + ring->len ) ); return 0; @@ -1207,8 +1207,7 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) { if ( refilled ) { wmb(); rx_tail = ( intelxl->rx.prod % INTELXL_RX_NUM_DESC ); - writel ( rx_tail, - ( intelxl->regs + intelxl->rx.reg + INTELXL_QXX_TAIL)); + writel ( rx_tail, ( intelxl->regs + intelxl->rx.tail ) ); } } @@ -1363,8 +1362,7 @@ static int intelxl_transmit ( struct net_device *netdev, wmb(); /* Notify card that there are packets ready to transmit */ - writel ( tx_tail, - ( intelxl->regs + intelxl->tx.reg + INTELXL_QXX_TAIL ) ); + writel ( tx_tail, ( intelxl->regs + intelxl->tx.tail ) ); DBGC2 ( intelxl, "INTELXL %p TX %d is [%llx,%llx)\n", intelxl, tx_idx, ( ( unsigned long long ) address ), @@ -1595,7 +1593,9 @@ static int intelxl_probe ( struct pci_device *pci ) { /* Configure queue register addresses */ intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); + intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL ); intelxl->rx.reg = INTELXL_QRX ( intelxl->queue ); + intelxl->rx.tail = ( intelxl->rx.reg + INTELXL_QXX_TAIL ); /* Configure interrupt causes */ writel ( ( INTELXL_QINT_TQCTL_NEXTQ_INDX_NONE | diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 883728e32..7a3f4d7fe 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -680,6 +680,8 @@ struct intelxl_ring { /** Register block */ unsigned int reg; + /** Tail register */ + unsigned int tail; /** Length (in bytes) */ size_t len; /** Program queue context -- cgit v1.2.3-55-g7522 From 2dbd9c0a3ca37e90a7e570acec35f02e54bba4aa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 16:47:16 +0100 Subject: [intelxl] Split out ring creation from context programming The virtual function driver will use the same transmit and receive descriptor ring structures, but will not itself construct and program the ring context. Split out ring creation and destruction from the programming of the ring context, to allow code to be shared between physical and virtual function drivers. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 104 +++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index ed6e64a6e..3e6f460ac 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -832,6 +832,62 @@ static void intelxl_close_admin ( struct intelxl_nic *intelxl ) { ****************************************************************************** */ +/** + * Allocate descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int intelxl_alloc_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + physaddr_t address; + int rc; + + /* Allocate descriptor ring */ + ring->desc.raw = malloc_dma ( ring->len, INTELXL_ALIGN ); + if ( ! ring->desc.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + address = virt_to_bus ( ring->desc.raw ); + + /* Initialise descriptor ring */ + memset ( ring->desc.raw, 0, ring->len ); + + /* Reset tail pointer */ + writel ( 0, ( intelxl->regs + ring->tail ) ); + + /* Reset counters */ + ring->prod = 0; + ring->cons = 0; + + DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n", + intelxl, ( ring->reg + ring->tail ), + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + ring->len ) ); + + return 0; + + free_dma ( ring->desc.raw, ring->len ); + err_alloc: + return rc; +} + +/** + * Free descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + */ +static void intelxl_free_ring ( struct intelxl_nic *intelxl __unused, + struct intelxl_ring *ring ) { + + /* Free descriptor ring */ + free_dma ( ring->desc.raw, ring->len ); + ring->desc.raw = NULL; +} + /** * Dump queue context (for debugging) * @@ -1100,17 +1156,8 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, int rc; /* Allocate descriptor ring */ - ring->desc.raw = malloc_dma ( ring->len, INTELXL_ALIGN ); - if ( ! ring->desc.raw ) { - rc = -ENOMEM; + if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 ) goto err_alloc; - } - - /* Initialise descriptor ring */ - memset ( ring->desc.raw, 0, ring->len ); - - /* Reset tail pointer */ - writel ( 0, ( intelxl->regs + ring->tail ) ); /* Program queue context */ address = virt_to_bus ( ring->desc.raw ); @@ -1121,21 +1168,12 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, if ( ( rc = intelxl_enable_ring ( intelxl, ring ) ) != 0 ) goto err_enable; - /* Reset counters */ - ring->prod = 0; - ring->cons = 0; - - DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n", - intelxl, ( ring->reg + ring->tail ), - ( ( unsigned long long ) address ), - ( ( unsigned long long ) address + ring->len ) ); - return 0; intelxl_disable_ring ( intelxl, ring ); err_enable: err_context: - free_dma ( ring->desc.raw, ring->len ); + intelxl_free_ring ( intelxl, ring ); err_alloc: return rc; } @@ -1157,8 +1195,7 @@ static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, } /* Free descriptor ring */ - free_dma ( ring->desc.raw, ring->len ); - ring->desc.raw = NULL; + intelxl_free_ring ( intelxl, ring ); } /** @@ -1211,6 +1248,22 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) { } } +/** + * Discard unused receive I/O buffers + * + * @v intelxl Intel device + */ +static void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { + unsigned int i; + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) { + if ( intelxl->rx_iobuf[i] ) + free_iob ( intelxl->rx_iobuf[i] ); + intelxl->rx_iobuf[i] = NULL; + } +} + /****************************************************************************** * * Network device interface @@ -1297,7 +1350,6 @@ static int intelxl_open ( struct net_device *netdev ) { static void intelxl_close ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; unsigned int queue; - unsigned int i; /* Dump contexts (for debugging) */ intelxl_context_dump ( intelxl, INTELXL_PFCM_LANCTXCTL_TYPE_TX, @@ -1319,11 +1371,7 @@ static void intelxl_close ( struct net_device *netdev ) { intelxl_destroy_ring ( intelxl, &intelxl->rx ); /* Discard any unused receive buffers */ - for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) { - if ( intelxl->rx_iobuf[i] ) - free_iob ( intelxl->rx_iobuf[i] ); - intelxl->rx_iobuf[i] = NULL; - } + intelxl_empty_rx ( intelxl ); } /** -- cgit v1.2.3-55-g7522 From 1e0342ebd8590036c7dee8ad9f43a0ed56cded68 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 17:11:31 +0100 Subject: [intelxl] Allow for arbitrary placement of interrupt control register Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 15 ++++++--------- src/drivers/net/intelxl.h | 8 +++++--- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 3e6f460ac..3a59a7462 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -1509,9 +1509,9 @@ static void intelxl_poll ( struct net_device *netdev ) { /* Acknowledge interrupts, if applicable */ if ( netdev_irq_enabled ( netdev ) ) { - writel ( ( INTELXL_PFINT_DYN_CTL0_CLEARPBA | - INTELXL_PFINT_DYN_CTL0_INTENA_MASK ), - intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); + writel ( ( INTELXL_INT_DYN_CTL_CLEARPBA | + INTELXL_INT_DYN_CTL_INTENA_MASK ), + ( intelxl->regs + intelxl->intr ) ); } /* Poll for completed packets */ @@ -1536,12 +1536,8 @@ static void intelxl_poll ( struct net_device *netdev ) { static void intelxl_irq ( struct net_device *netdev, int enable ) { struct intelxl_nic *intelxl = netdev->priv; - if ( enable ) { - writel ( INTELXL_PFINT_DYN_CTL0_INTENA, - intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); - } else { - writel ( 0, intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); - } + writel ( ( enable ? INTELXL_INT_DYN_CTL_INTENA : 0 ), + ( intelxl->regs + intelxl->intr ) ); } /** Network device operations */ @@ -1585,6 +1581,7 @@ static int intelxl_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->pf = PCI_FUNC ( pci->busdevfn ); + intelxl->intr = INTELXL_PFINT_DYN_CTL0; intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, &intelxl_admin_offsets ); intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 7a3f4d7fe..8158e072f 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -735,9 +735,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** PF Interrupt Zero Dynamic Control Register */ #define INTELXL_PFINT_DYN_CTL0 0x038480 -#define INTELXL_PFINT_DYN_CTL0_INTENA 0x00000001UL /**< Enable */ -#define INTELXL_PFINT_DYN_CTL0_CLEARPBA 0x00000002UL /**< Acknowledge */ -#define INTELXL_PFINT_DYN_CTL0_INTENA_MASK 0x80000000UL /**< Ignore enable */ +#define INTELXL_INT_DYN_CTL_INTENA 0x00000001UL /**< Enable */ +#define INTELXL_INT_DYN_CTL_CLEARPBA 0x00000002UL /**< Acknowledge */ +#define INTELXL_INT_DYN_CTL_INTENA_MASK 0x80000000UL /**< Ignore enable */ /** PF Interrupt Zero Linked List Register */ #define INTELXL_PFINT_LNKLST0 0x038500 @@ -835,6 +835,8 @@ struct intelxl_nic { unsigned int vsi; /** Queue set handle */ unsigned int qset; + /** Interrupt control register */ + unsigned int intr; /** Admin command queue */ struct intelxl_admin command; -- cgit v1.2.3-55-g7522 From 3078a952a83d8af37f40a82e93461c087efa7e5a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 14:38:43 +0100 Subject: [intelxl] Expose functions required by virtual function driver Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 31 ++++++++++++++----------------- src/drivers/net/intelxl.h | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 3a59a7462..973f1d916 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -45,8 +45,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -static void intelxl_reopen_admin ( struct intelxl_nic *intelxl ); - /****************************************************************************** * * Device reset @@ -238,7 +236,7 @@ static void intelxl_free_admin ( struct intelxl_nic *intelxl __unused, * @v intelxl Intel device * @ret cmd Command descriptor */ -static struct intelxl_admin_descriptor * +struct intelxl_admin_descriptor * intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ) { struct intelxl_admin *admin = &intelxl->command; struct intelxl_admin_descriptor *cmd; @@ -255,7 +253,7 @@ intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ) { * @v intelxl Intel device * @ret buf Data buffer */ -static union intelxl_admin_buffer * +union intelxl_admin_buffer * intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ) { struct intelxl_admin *admin = &intelxl->command; union intelxl_admin_buffer *buf; @@ -295,7 +293,7 @@ static void intelxl_admin_event_init ( struct intelxl_nic *intelxl, * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_admin_command ( struct intelxl_nic *intelxl ) { +int intelxl_admin_command ( struct intelxl_nic *intelxl ) { struct intelxl_admin *admin = &intelxl->command; const struct intelxl_admin_offsets *regs = admin->regs; void *admin_regs = ( intelxl->regs + admin->base ); @@ -696,7 +694,7 @@ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) { * * @v netdev Network device */ -static void intelxl_poll_admin ( struct net_device *netdev ) { +void intelxl_poll_admin ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_admin *admin = &intelxl->event; struct intelxl_admin_descriptor *evt; @@ -749,7 +747,7 @@ static void intelxl_poll_admin ( struct net_device *netdev ) { * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { +int intelxl_open_admin ( struct intelxl_nic *intelxl ) { int rc; /* Allocate admin event queue */ @@ -789,7 +787,7 @@ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { * * @v intelxl Intel device */ -static void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) { +void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) { unsigned int i; /* Enable admin event queue */ @@ -811,7 +809,7 @@ static void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) { * * @v intelxl Intel device */ -static void intelxl_close_admin ( struct intelxl_nic *intelxl ) { +void intelxl_close_admin ( struct intelxl_nic *intelxl ) { /* Shut down admin queues */ intelxl_admin_shutdown ( intelxl ); @@ -839,8 +837,8 @@ static void intelxl_close_admin ( struct intelxl_nic *intelxl ) { * @v ring Descriptor ring * @ret rc Return status code */ -static int intelxl_alloc_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +int intelxl_alloc_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { physaddr_t address; int rc; @@ -880,8 +878,8 @@ static int intelxl_alloc_ring ( struct intelxl_nic *intelxl, * @v intelxl Intel device * @v ring Descriptor ring */ -static void intelxl_free_ring ( struct intelxl_nic *intelxl __unused, - struct intelxl_ring *ring ) { +void intelxl_free_ring ( struct intelxl_nic *intelxl __unused, + struct intelxl_ring *ring ) { /* Free descriptor ring */ free_dma ( ring->desc.raw, ring->len ); @@ -1253,7 +1251,7 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) { * * @v intelxl Intel device */ -static void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { +void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { unsigned int i; /* Discard any unused receive buffers */ @@ -1381,8 +1379,7 @@ static void intelxl_close ( struct net_device *netdev ) { * @v iobuf I/O buffer * @ret rc Return status code */ -static int intelxl_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) { +int intelxl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct intelxl_nic *intelxl = netdev->priv; struct intelxl_tx_data_descriptor *tx; unsigned int tx_idx; @@ -1504,7 +1501,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) { * * @v netdev Network device */ -static void intelxl_poll ( struct net_device *netdev ) { +void intelxl_poll ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; /* Acknowledge interrupts, if applicable */ diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 8158e072f..2e7444152 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -851,6 +851,24 @@ struct intelxl_nic { struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; }; +extern struct intelxl_admin_descriptor * +intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ); +extern union intelxl_admin_buffer * +intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ); +extern int intelxl_admin_command ( struct intelxl_nic *intelxl ); +extern void intelxl_poll_admin ( struct net_device *netdev ); +extern int intelxl_open_admin ( struct intelxl_nic *intelxl ); +extern void intelxl_reopen_admin ( struct intelxl_nic *intelxl ); +extern void intelxl_close_admin ( struct intelxl_nic *intelxl ); +extern int intelxl_alloc_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); +extern void intelxl_free_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); +extern void intelxl_empty_rx ( struct intelxl_nic *intelxl ); +extern int intelxl_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ); +extern void intelxl_poll ( struct net_device *netdev ); + extern void intelxlvf_admin_event ( struct net_device *netdev, struct intelxl_admin_descriptor *evt, union intelxl_admin_buffer *buf ); -- cgit v1.2.3-55-g7522 From 92b46b7858c8bf99abc9007c550be72d8eb4f132 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 22:11:14 +0100 Subject: [intelxl] Choose to operate in non-PXE mode The physical function defaults to operating in "PXE mode" after a power-on reset. In this mode, receive descriptors are fetched and written back as single descriptors. In normal (non-PXE mode) operation, receive descriptors are fetched and written back only as complete cachelines unless an interrupt is raised. There is no way to return to PXE mode from non-PXE mode, and there is no way for the virtual function driver to operate in PXE mode. Choose to operate in non-PXE mode. This requires us to trick the hardware into believing that it is raising an interrupt, so that it will not defer writing back receive descriptors until a complete cacheline (i.e. four packets) have been consumed. We do so by configuring the hardware to use MSI-X with a dummy target location in place of the usual APIC register. Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 133 +++++++++++++++++++++++++++++++++++++++------- src/drivers/net/intelxl.h | 50 ++++++++++++++--- 2 files changed, 155 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index 973f1d916..c98ba265c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -116,6 +116,56 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, return 0; } +/****************************************************************************** + * + * MSI-X interrupts + * + ****************************************************************************** + */ + +/** + * Enable MSI-X dummy interrupt + * + * @v intelxl Intel device + * @v pci PCI device + * @ret rc Return status code + */ +int intelxl_msix_enable ( struct intelxl_nic *intelxl, + struct pci_device *pci ) { + int rc; + + /* Enable MSI-X capability */ + if ( ( rc = pci_msix_enable ( pci, &intelxl->msix ) ) != 0 ) { + DBGC ( intelxl, "INTELXL %p could not enable MSI-X: %s\n", + intelxl, strerror ( rc ) ); + return rc; + } + + /* Configure interrupt zero to write to dummy location */ + pci_msix_map ( &intelxl->msix, 0, virt_to_bus ( &intelxl->msg ), 0 ); + + /* Enable dummy interrupt zero */ + pci_msix_unmask ( &intelxl->msix, 0 ); + + return 0; +} + +/** + * Disable MSI-X dummy interrupt + * + * @v intelxl Intel device + * @v pci PCI device + */ +void intelxl_msix_disable ( struct intelxl_nic *intelxl, + struct pci_device *pci ) { + + /* Disable dummy interrupt zero */ + pci_msix_mask ( &intelxl->msix, 0 ); + + /* Disable MSI-X capability */ + pci_msix_disable ( pci, &intelxl->msix ); +} + /****************************************************************************** * * Admin queue @@ -480,6 +530,39 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { return 0; } +/** + * Clear PXE mode + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_clear_pxe_params *pxe; + uint32_t gllan_rctl_0; + int rc; + + /* Do nothing if device is already out of PXE mode */ + gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 ); + if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) { + DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n", + intelxl ); + return 0; + } + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE ); + pxe = &cmd->params.pxe; + pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /** * Get switch configuration * @@ -1504,13 +1587,6 @@ static void intelxl_poll_rx ( struct net_device *netdev ) { void intelxl_poll ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - /* Acknowledge interrupts, if applicable */ - if ( netdev_irq_enabled ( netdev ) ) { - writel ( ( INTELXL_INT_DYN_CTL_CLEARPBA | - INTELXL_INT_DYN_CTL_INTENA_MASK ), - ( intelxl->regs + intelxl->intr ) ); - } - /* Poll for completed packets */ intelxl_poll_tx ( netdev ); @@ -1522,19 +1598,23 @@ void intelxl_poll ( struct net_device *netdev ) { /* Refill RX ring */ intelxl_refill_rx ( intelxl ); -} - -/** - * Enable or disable interrupts - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void intelxl_irq ( struct net_device *netdev, int enable ) { - struct intelxl_nic *intelxl = netdev->priv; - writel ( ( enable ? INTELXL_INT_DYN_CTL_INTENA : 0 ), - ( intelxl->regs + intelxl->intr ) ); + /* Rearm interrupt, since otherwise receive descriptors will + * be written back only after a complete cacheline (four + * packets) have been received. + * + * There is unfortunately no efficient way to determine + * whether or not rearming the interrupt is necessary. If we + * are running inside a hypervisor (e.g. using a VF or PF as a + * passed-through PCI device), then the MSI-X write is + * redirected by the hypervisor to the real host APIC and the + * host ISR then raises an interrupt within the guest. We + * therefore cannot poll the nominal MSI-X target location to + * watch for the value being written. We could read from the + * INT_DYN_CTL register, but this is even less efficient than + * just unconditionally rearming the interrupt. + */ + writel ( INTELXL_INT_DYN_CTL_INTENA, intelxl->regs + intelxl->intr ); } /** Network device operations */ @@ -1543,7 +1623,6 @@ static struct net_device_operations intelxl_operations = { .close = intelxl_close, .transmit = intelxl_transmit, .poll = intelxl_poll, - .irq = intelxl_irq, }; /****************************************************************************** @@ -1617,10 +1696,18 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 ) goto err_fetch_mac; + /* Enable MSI-X dummy interrupt */ + if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + goto err_msix; + /* Open admin queues */ if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) goto err_open_admin; + /* Clear PXE mode */ + if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) + goto err_admin_clear_pxe; + /* Get switch configuration */ if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 ) goto err_admin_switch; @@ -1667,8 +1754,11 @@ static int intelxl_probe ( struct pci_device *pci ) { err_admin_promisc: err_admin_vsi: err_admin_switch: + err_admin_clear_pxe: intelxl_close_admin ( intelxl ); err_open_admin: + intelxl_msix_disable ( intelxl, pci ); + err_msix: err_fetch_mac: intelxl_reset ( intelxl ); err_reset: @@ -1695,6 +1785,9 @@ static void intelxl_remove ( struct pci_device *pci ) { /* Close admin queues */ intelxl_close_admin ( intelxl ); + /* Disable MSI-X dummy interrupt */ + intelxl_msix_disable ( intelxl, pci ); + /* Reset the NIC */ intelxl_reset ( intelxl ); diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 2e7444152..414477434 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include struct intelxl_nic; @@ -143,6 +144,20 @@ struct intelxl_admin_shutdown_params { /** Driver is unloading */ #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01 +/** Admin queue Clear PXE Mode command */ +#define INTELXL_ADMIN_CLEAR_PXE 0x0110 + +/** Admin queue Clear PXE Mode command parameters */ +struct intelxl_admin_clear_pxe_params { + /** Magic value */ + uint8_t magic; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** Clear PXE Mode magic value */ +#define INTELXL_ADMIN_CLEAR_PXE_MAGIC 0x02 + /** Admin queue Get Switch Configuration command */ #define INTELXL_ADMIN_SWITCH 0x0200 @@ -305,6 +320,8 @@ union intelxl_admin_params { struct intelxl_admin_driver_params driver; /** Shutdown command parameters */ struct intelxl_admin_shutdown_params shutdown; + /** Clear PXE Mode command parameters */ + struct intelxl_admin_clear_pxe_params pxe; /** Get Switch Configuration command parameters */ struct intelxl_admin_switch_params sw; /** Get VSI Parameters command parameters */ @@ -563,6 +580,10 @@ struct intelxl_context_rx { /** Queue Tail Pointer Register (offset) */ #define INTELXL_QXX_TAIL 0x8000 +/** Global RLAN Control 0 register */ +#define INTELXL_GLLAN_RCTL_0 0x12a500 +#define INTELXL_GLLAN_RCTL_0_PXE_MODE 0x00000001UL /**< PXE mode */ + /** Transmit data descriptor */ struct intelxl_tx_data_descriptor { /** Buffer address */ @@ -709,22 +730,27 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, ring->context = context; } -/** Number of transmit descriptors */ -#define INTELXL_TX_NUM_DESC 16 +/** Number of transmit descriptors + * + * Chosen to exceed the receive ring fill level, in order to avoid + * running out of transmit descriptors when sending TCP ACKs. + */ +#define INTELXL_TX_NUM_DESC 64 /** Transmit descriptor ring maximum fill level */ #define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 ) /** Number of receive descriptors * - * In PXE mode (i.e. able to post single receive descriptors), 8 - * descriptors is the only permitted value covering all possible - * numbers of PFs. + * Must be a multiple of 32. */ -#define INTELXL_RX_NUM_DESC 8 +#define INTELXL_RX_NUM_DESC 32 -/** Receive descriptor ring fill level */ -#define INTELXL_RX_FILL ( INTELXL_RX_NUM_DESC - 1 ) +/** Receive descriptor ring fill level + * + * Must be a multiple of 8 and greater than 8. + */ +#define INTELXL_RX_FILL 16 /****************************************************************************** * @@ -837,6 +863,10 @@ struct intelxl_nic { unsigned int qset; /** Interrupt control register */ unsigned int intr; + /** MSI-X capability */ + struct pci_msix msix; + /** MSI-X dummy interrupt target */ + uint32_t msg; /** Admin command queue */ struct intelxl_admin command; @@ -851,6 +881,10 @@ struct intelxl_nic { struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; }; +extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, + struct pci_device *pci ); +extern void intelxl_msix_disable ( struct intelxl_nic *intelxl, + struct pci_device *pci ); extern struct intelxl_admin_descriptor * intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ); extern union intelxl_admin_buffer * -- cgit v1.2.3-55-g7522 From a95966955c6904b975b0ccec0dd338385fb3066b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Apr 2019 17:15:49 +0100 Subject: [intelxl] Add driver for Intel 40 Gigabit Ethernet NIC virtual functions Signed-off-by: Michael Brown --- src/drivers/net/intelxl.h | 185 +++++++++++- src/drivers/net/intelxlvf.c | 719 ++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/intelxlvf.h | 86 ++++++ src/include/ipxe/errfile.h | 1 + 4 files changed, 989 insertions(+), 2 deletions(-) create mode 100644 src/drivers/net/intelxlvf.c create mode 100644 src/drivers/net/intelxlvf.h (limited to 'src') diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index 414477434..80586cef0 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -20,9 +20,9 @@ struct intelxl_nic; /** Alignment * - * No data structure requires greater than 128 byte alignment. + * No data structure requires greater than 256 byte alignment. */ -#define INTELXL_ALIGN 128 +#define INTELXL_ALIGN 256 /****************************************************************************** * @@ -310,6 +310,166 @@ struct intelxl_admin_link_params { /** Admin queue Send Message to VF command */ #define INTELXL_ADMIN_SEND_TO_VF 0x0802 +/** Admin Queue VF Reset opcode */ +#define INTELXL_ADMIN_VF_RESET 0x00000002 + +/** Admin Queue VF Get Resources opcode */ +#define INTELXL_ADMIN_VF_GET_RESOURCES 0x00000003 + +/** Admin Queue VF Get Resources data buffer */ +struct intelxl_admin_vf_get_resources_buffer { + /** Reserved */ + uint8_t reserved_a[20]; + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_b[8]; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Admin Queue VF Status Change Event opcode */ +#define INTELXL_ADMIN_VF_STATUS 0x00000011 + +/** Link status change event type */ +#define INTELXL_ADMIN_VF_STATUS_LINK 0x00000001 + +/** Link status change event data */ +struct intelxl_admin_vf_status_link { + /** Link speed */ + uint32_t speed; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved[3]; +} __attribute__ (( packed )); + +/** Admin Queue VF Status Change Event data buffer */ +struct intelxl_admin_vf_status_buffer { + /** Event type */ + uint32_t event; + /** Event data */ + union { + /** Link change event data */ + struct intelxl_admin_vf_status_link link; + } data; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Queues opcode */ +#define INTELXL_ADMIN_VF_CONFIGURE 0x00000006 + +/** Admin Queue VF Configure Queues data buffer */ +struct intelxl_admin_vf_configure_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Number of queue pairs */ + uint16_t count; + /** Reserved */ + uint8_t reserved_a[4]; + /** Transmit queue */ + struct { + /** VSI switching element ID */ + uint16_t vsi; + /** Queue ID */ + uint16_t id; + /** Queue count */ + uint16_t count; + /** Reserved */ + uint8_t reserved_a[2]; + /** Base address */ + uint64_t base; + /** Reserved */ + uint8_t reserved_b[8]; + } __attribute__ (( packed )) tx; + /** Receive queue */ + struct { + /** VSI switching element ID */ + uint16_t vsi; + /** Queue ID */ + uint16_t id; + /** Queue count */ + uint32_t count; + /** Reserved */ + uint8_t reserved_a[4]; + /** Data buffer length */ + uint32_t len; + /** Maximum frame size */ + uint32_t mfs; + /** Reserved */ + uint8_t reserved_b[4]; + /** Base address */ + uint64_t base; + /** Reserved */ + uint8_t reserved_c[8]; + } __attribute__ (( packed )) rx; + /** Reserved + * + * This field exists only due to a bug in the PF driver's + * message validation logic, which causes it to miscalculate + * the expected message length. + */ + uint8_t reserved_b[64]; +} __attribute__ (( packed )); + +/** Admin Queue VF IRQ Map opcode */ +#define INTELXL_ADMIN_VF_IRQ_MAP 0x00000007 + +/** Admin Queue VF IRQ Map data buffer */ +struct intelxl_admin_vf_irq_map_buffer { + /** Number of interrupt vectors */ + uint16_t count; + /** VSI switching element ID */ + uint16_t vsi; + /** Interrupt vector ID */ + uint16_t vec; + /** Receive queue bitmap */ + uint16_t rxmap; + /** Transmit queue bitmap */ + uint16_t txmap; + /** Receive interrupt throttling index */ + uint16_t rxitr; + /** Transmit interrupt throttling index */ + uint16_t txitr; + /** Reserved + * + * This field exists only due to a bug in the PF driver's + * message validation logic, which causes it to miscalculate + * the expected message length. + */ + uint8_t reserved[12]; +} __attribute__ (( packed )); + +/** Admin Queue VF Enable Queues opcode */ +#define INTELXL_ADMIN_VF_ENABLE 0x00000008 + +/** Admin Queue VF Disable Queues opcode */ +#define INTELXL_ADMIN_VF_DISABLE 0x00000009 + +/** Admin Queue VF Enable/Disable Queues data buffer */ +struct intelxl_admin_vf_queues_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved[2]; + /** Receive queue bitmask */ + uint32_t rx; + /** Transmit queue bitmask */ + uint32_t tx; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Promiscuous Mode opcode */ +#define INTELXL_ADMIN_VF_PROMISC 0x0000000e + +/** Admin Queue VF Configure Promiscuous Mode data buffer */ +struct intelxl_admin_vf_promisc_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Flags */ + uint16_t flags; +} __attribute__ (( packed )); + /** Admin queue command parameters */ union intelxl_admin_params { /** Additional data buffer command parameters */ @@ -342,6 +502,18 @@ union intelxl_admin_buffer { struct intelxl_admin_switch_buffer sw; /** Get VSI Parameters data buffer */ struct intelxl_admin_vsi_buffer vsi; + /** VF Get Resources data buffer */ + struct intelxl_admin_vf_get_resources_buffer res; + /** VF Status Change Event data buffer */ + struct intelxl_admin_vf_status_buffer stat; + /** VF Configure Queues data buffer */ + struct intelxl_admin_vf_configure_buffer cfg; + /** VF Enable/Disable Queues data buffer */ + struct intelxl_admin_vf_queues_buffer queues; + /** VF Configure Promiscuous Mode data buffer */ + struct intelxl_admin_vf_promisc_buffer promisc; + /*** VF IRQ Map data buffer */ + struct intelxl_admin_vf_irq_map_buffer irq; /** Alignment padding */ uint8_t pad[INTELXL_ALIGN]; } __attribute__ (( packed )); @@ -867,12 +1039,21 @@ struct intelxl_nic { struct pci_msix msix; /** MSI-X dummy interrupt target */ uint32_t msg; + /** PCI Express capability offset */ + unsigned int exp; /** Admin command queue */ struct intelxl_admin command; /** Admin event queue */ struct intelxl_admin event; + /** Current VF opcode */ + unsigned int vopcode; + /** Current VF return value */ + int vret; + /** Current VF event data buffer */ + union intelxl_admin_buffer vbuf; + /** Transmit descriptor ring */ struct intelxl_ring tx; /** Receive descriptor ring */ diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c new file mode 100644 index 000000000..8f76daf3d --- /dev/null +++ b/src/drivers/net/intelxlvf.c @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2019 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include "intelxlvf.h" + +/** @file + * + * Intel 40 Gigabit Ethernet virtual function network card driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware via PCIe function-level reset + * + * @v intelxl Intel device + */ +static void intelxlvf_reset_flr ( struct intelxl_nic *intelxl, + struct pci_device *pci ) { + uint16_t control; + + /* Perform a PCIe function-level reset */ + pci_read_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ), + &control ); + pci_write_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ), + ( control | PCI_EXP_DEVCTL_FLR ) ); + mdelay ( INTELXL_RESET_DELAY_MS ); +} + +/** + * Wait for admin event queue to be torn down + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_wait_teardown ( struct intelxl_nic *intelxl ) { + uint32_t admin_evt_len; + unsigned int i; + + /* Wait for admin event queue to be torn down */ + for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check admin event queue length register */ + admin_evt_len = readl ( intelxl->regs + INTELXLVF_ADMIN + + INTELXLVF_ADMIN_EVT_LEN ); + if ( ! ( admin_evt_len & INTELXL_ADMIN_LEN_ENABLE ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intelxl, "INTELXL %p timed out waiting for teardown (%#08x)\n", + intelxl, admin_evt_len ); + return -ETIMEDOUT; +} + +/** + * Wait for virtual function to be marked as active + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) { + uint32_t vfgen_rstat; + unsigned int vfr_state; + unsigned int i; + + /* Wait for virtual function to be marked as active */ + for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check status as written by physical function driver */ + vfgen_rstat = readl ( intelxl->regs + INTELXLVF_VFGEN_RSTAT ); + vfr_state = INTELXLVF_VFGEN_RSTAT_VFR_STATE ( vfgen_rstat ); + if ( vfr_state == INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intelxl, "INTELXL %p timed out waiting for activation " + "(%#08x)\n", intelxl, vfgen_rstat ); + return -ETIMEDOUT; +} + +/** + * Reset hardware via admin queue + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *cmd; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_RESET ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + goto err_command; + + /* Wait for minimum reset time */ + mdelay ( INTELXL_RESET_DELAY_MS ); + + /* Wait for reset to take effect */ + if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 ) + goto err_teardown; + + /* Wait for virtual function to become active */ + if ( ( rc = intelxlvf_reset_wait_active ( intelxl ) ) != 0 ) + goto err_active; + + err_active: + err_teardown: + intelxl_reopen_admin ( intelxl ); + err_command: + return rc; +} + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** Admin command queue register offsets */ +static const struct intelxl_admin_offsets intelxlvf_admin_command_offsets = { + .bal = INTELXLVF_ADMIN_CMD_BAL, + .bah = INTELXLVF_ADMIN_CMD_BAH, + .len = INTELXLVF_ADMIN_CMD_LEN, + .head = INTELXLVF_ADMIN_CMD_HEAD, + .tail = INTELXLVF_ADMIN_CMD_TAIL, +}; + +/** Admin event queue register offsets */ +static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = { + .bal = INTELXLVF_ADMIN_EVT_BAL, + .bah = INTELXLVF_ADMIN_EVT_BAH, + .len = INTELXLVF_ADMIN_EVT_LEN, + .head = INTELXLVF_ADMIN_EVT_HEAD, + .tail = INTELXLVF_ADMIN_EVT_TAIL, +}; + +/** + * Issue admin queue virtual function command + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_command ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin *admin = &intelxl->command; + struct intelxl_admin_descriptor *cmd; + unsigned int i; + int rc; + + /* Populate descriptor */ + cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF ); + + /* Record opcode */ + intelxl->vopcode = le32_to_cpu ( cmd->vopcode ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + goto err_command; + + /* Wait for response */ + for ( i = 0 ; i < INTELXLVF_ADMIN_MAX_WAIT_MS ; i++ ) { + + /* Poll admin event queue */ + intelxl_poll_admin ( netdev ); + + /* If response has not arrived, delay 1ms and retry */ + if ( intelxl->vopcode ) { + mdelay ( 1 ); + continue; + } + + /* Check for errors */ + if ( intelxl->vret != 0 ) + return -EIO; + + return 0; + } + + rc = -ETIMEDOUT; + DBGC ( intelxl, "INTELXL %p timed out waiting for admin VF command " + "%#x\n", intelxl, intelxl->vopcode ); + err_command: + intelxl->vopcode = 0; + return rc; +} + +/** + * Handle link status event + * + * @v netdev Network device + * @v link Link status + */ +static void intelxlvf_admin_link ( struct net_device *netdev, + struct intelxl_admin_vf_status_link *link ) { + struct intelxl_nic *intelxl = netdev->priv; + + DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl, + link->status, link->speed ); + + /* Update network device */ + if ( link->status ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/** + * Handle status change event + * + * @v netdev Network device + * @v stat Status change event + */ +static void +intelxlvf_admin_status ( struct net_device *netdev, + struct intelxl_admin_vf_status_buffer *stat ) { + struct intelxl_nic *intelxl = netdev->priv; + + /* Handle event */ + switch ( stat->event ) { + case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ): + intelxlvf_admin_link ( netdev, &stat->data.link ); + break; + default: + DBGC ( intelxl, "INTELXL %p unrecognised status change " + "event %#x:\n", intelxl, le32_to_cpu ( stat->event ) ); + DBGC_HDA ( intelxl, 0, stat, sizeof ( *stat ) ); + break; + } +} + +/** + * Handle virtual function event + * + * @v netdev Network device + * @v evt Admin queue event descriptor + * @v buf Admin queue event data buffer + */ +void intelxlvf_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ) { + struct intelxl_nic *intelxl = netdev->priv; + unsigned int vopcode = le32_to_cpu ( evt->vopcode ); + + /* Record command response if applicable */ + if ( vopcode == intelxl->vopcode ) { + memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) ); + intelxl->vopcode = 0; + intelxl->vret = le32_to_cpu ( evt->vret ); + if ( intelxl->vret != 0 ) { + DBGC ( intelxl, "INTELXL %p admin VF command %#x " + "error %d\n", intelxl, vopcode, intelxl->vret ); + DBGC_HDA ( intelxl, virt_to_bus ( evt ), evt, + sizeof ( *evt ) ); + DBGC_HDA ( intelxl, virt_to_bus ( buf ), buf, + le16_to_cpu ( evt->len ) ); + } + return; + } + + /* Handle unsolicited events */ + switch ( vopcode ) { + case INTELXL_ADMIN_VF_STATUS: + intelxlvf_admin_status ( netdev, &buf->stat ); + break; + default: + DBGC ( intelxl, "INTELXL %p unrecognised VF event %#x:\n", + intelxl, vopcode ); + DBGC_HDA ( intelxl, 0, evt, sizeof ( *evt ) ); + DBGC_HDA ( intelxl, 0, buf, le16_to_cpu ( evt->len ) ); + break; + } +} + +/** + * Get resources + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_vf_get_resources_buffer *res; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + + /* Parse response */ + res = &intelxl->vbuf.res; + intelxl->vsi = le16_to_cpu ( res->vsi ); + memcpy ( netdev->hw_addr, res->mac, ETH_ALEN ); + DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi ); + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Configure queues + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_configure ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + union intelxl_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) ); + buf = intelxl_admin_command_buffer ( intelxl ); + buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->cfg.count = cpu_to_le16 ( 1 ); + buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->cfg.tx.count = cpu_to_le16 ( INTELXL_TX_NUM_DESC ); + buf->cfg.tx.base = cpu_to_le64 ( virt_to_bus ( intelxl->tx.desc.raw ) ); + buf->cfg.rx.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->cfg.rx.count = cpu_to_le32 ( INTELXL_RX_NUM_DESC ); + buf->cfg.rx.len = cpu_to_le32 ( intelxl->mfs ); + buf->cfg.rx.mfs = cpu_to_le32 ( intelxl->mfs ); + buf->cfg.rx.base = cpu_to_le64 ( virt_to_bus ( intelxl->rx.desc.raw ) ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Configure IRQ mapping + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + union intelxl_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) ); + buf = intelxl_admin_command_buffer ( intelxl ); + buf->irq.count = cpu_to_le16 ( 1 ); + buf->irq.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->irq.rxmap = cpu_to_le16 ( 0x0001 ); + buf->irq.txmap = cpu_to_le16 ( 0x0001 ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enable/disable queues + * + * @v netdev Network device + * @v enable Enable queues + * @ret rc Return status code + */ +static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + union intelxl_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXL_ADMIN_VF_ENABLE ) : + cpu_to_le32 ( INTELXL_ADMIN_VF_DISABLE ) ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) ); + buf = intelxl_admin_command_buffer ( intelxl ); + buf->queues.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->queues.rx = cpu_to_le32 ( 1 ); + buf->queues.tx = cpu_to_le32 ( 1 ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Configure promiscuous mode + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_promisc ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + union intelxl_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) ); + buf = intelxl_admin_command_buffer ( intelxl ); + buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST | + INTELXL_ADMIN_PROMISC_FL_MULTICAST ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_open ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + int rc; + + /* Calculate maximum frame size */ + intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); + + /* Allocate transmit descriptor ring */ + if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->tx ) ) != 0 ) + goto err_alloc_tx; + + /* Allocate receive descriptor ring */ + if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->rx ) ) != 0 ) + goto err_alloc_rx; + + /* Configure queues */ + if ( ( rc = intelxlvf_admin_configure ( netdev ) ) != 0 ) + goto err_configure; + + /* Configure IRQ map */ + if ( ( rc = intelxlvf_admin_irq_map ( netdev ) ) != 0 ) + goto err_irq_map; + + /* Enable queues */ + if ( ( rc = intelxlvf_admin_queues ( netdev, 1 ) ) != 0 ) + goto err_enable; + + /* Configure promiscuous mode */ + if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 ) + goto err_promisc; + + return 0; + + err_promisc: + intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE ); + err_enable: + err_irq_map: + err_configure: + intelxl_free_ring ( intelxl, &intelxl->rx ); + err_alloc_rx: + intelxl_free_ring ( intelxl, &intelxl->tx ); + err_alloc_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void intelxlvf_close ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + int rc; + + /* Disable queues */ + if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Free receive descriptor ring */ + intelxl_free_ring ( intelxl, &intelxl->rx ); + + /* Free transmit descriptor ring */ + intelxl_free_ring ( intelxl, &intelxl->tx ); + + /* Discard any unused receive buffers */ + intelxl_empty_rx ( intelxl ); +} + +/** Network device operations */ +static struct net_device_operations intelxlvf_operations = { + .open = intelxlvf_open, + .close = intelxlvf_close, + .transmit = intelxl_transmit, + .poll = intelxl_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int intelxlvf_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intelxl_nic *intelxl; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intelxl ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &intelxlvf_operations ); + intelxl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intelxl, 0, sizeof ( *intelxl ) ); + intelxl->intr = INTELXLVF_VFINT_DYN_CTL0; + intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN, + &intelxlvf_admin_command_offsets ); + intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN, + &intelxlvf_admin_event_offsets ); + intelxlvf_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC, + sizeof ( intelxl->tx.desc.tx[0] ), + INTELXLVF_QTX_TAIL ); + intelxlvf_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + sizeof ( intelxl->rx.desc.rx[0] ), + INTELXLVF_QRX_TAIL ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intelxl->regs = ioremap ( pci->membase, INTELXLVF_BAR_SIZE ); + if ( ! intelxl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Locate PCI Express capability */ + intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP ); + if ( ! intelxl->exp ) { + DBGC ( intelxl, "INTELXL %p missing PCIe capability\n", + intelxl ); + rc = -ENXIO; + goto err_exp; + } + + /* Reset the function via PCIe FLR */ + intelxlvf_reset_flr ( intelxl, pci ); + + /* Enable MSI-X dummy interrupt */ + if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + goto err_msix; + + /* Open admin queues */ + if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) + goto err_open_admin; + + /* Reset the function via admin queue */ + if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 ) + goto err_reset_admin; + + /* Get MAC address */ + if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) + goto err_get_resources; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_get_resources: + err_reset_admin: + intelxl_close_admin ( intelxl ); + err_open_admin: + intelxl_msix_disable ( intelxl, pci ); + err_msix: + intelxlvf_reset_flr ( intelxl, pci ); + err_exp: + iounmap ( intelxl->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void intelxlvf_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct intelxl_nic *intelxl = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset the function via admin queue */ + intelxlvf_reset_admin ( intelxl ); + + /* Close admin queues */ + intelxl_close_admin ( intelxl ); + + /* Disable MSI-X dummy interrupt */ + intelxl_msix_disable ( intelxl, pci ); + + /* Reset the function via PCIe FLR */ + intelxlvf_reset_flr ( intelxl, pci ); + + /* Free network device */ + iounmap ( intelxl->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id intelxlvf_nics[] = { + PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ), + PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ), + PCI_ROM ( 0x8086, 0x1889, "xl710-vf-ad", "XL710 VF (adaptive)", 0 ), + PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ), + PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ), +}; + +/** PCI driver */ +struct pci_driver intelxlvf_driver __pci_driver = { + .ids = intelxlvf_nics, + .id_count = ( sizeof ( intelxlvf_nics ) / + sizeof ( intelxlvf_nics[0] ) ), + .probe = intelxlvf_probe, + .remove = intelxlvf_remove, +}; diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h new file mode 100644 index 000000000..ffcae5674 --- /dev/null +++ b/src/drivers/net/intelxlvf.h @@ -0,0 +1,86 @@ +#ifndef _INTELXLVF_H +#define _INTELXLVF_H + +/** @file + * + * Intel 40 Gigabit Ethernet virtual function network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "intelxl.h" + +/** BAR size */ +#define INTELXLVF_BAR_SIZE 0x10000 + +/** Transmit Queue Tail Register */ +#define INTELXLVF_QTX_TAIL 0x00000 + +/** Receive Queue Tail Register */ +#define INTELXLVF_QRX_TAIL 0x02000 + +/** VF Interrupt Zero Dynamic Control Register */ +#define INTELXLVF_VFINT_DYN_CTL0 0x5c00 + +/** VF Admin Queue register block */ +#define INTELXLVF_ADMIN 0x6000 + +/** Admin Command Queue Base Address Low Register (offset) */ +#define INTELXLVF_ADMIN_CMD_BAL 0x1c00 + +/** Admin Command Queue Base Address High Register (offset) */ +#define INTELXLVF_ADMIN_CMD_BAH 0x1800 + +/** Admin Command Queue Length Register (offset) */ +#define INTELXLVF_ADMIN_CMD_LEN 0x0800 + +/** Admin Command Queue Head Register (offset) */ +#define INTELXLVF_ADMIN_CMD_HEAD 0x0400 + +/** Admin Command Queue Tail Register (offset) */ +#define INTELXLVF_ADMIN_CMD_TAIL 0x2400 + +/** Admin Event Queue Base Address Low Register (offset) */ +#define INTELXLVF_ADMIN_EVT_BAL 0x0c00 + +/** Admin Event Queue Base Address High Register (offset) */ +#define INTELXLVF_ADMIN_EVT_BAH 0x0000 + +/** Admin Event Queue Length Register (offset) */ +#define INTELXLVF_ADMIN_EVT_LEN 0x2000 + +/** Admin Event Queue Head Register (offset) */ +#define INTELXLVF_ADMIN_EVT_HEAD 0x1400 + +/** Admin Event Queue Tail Register (offset) */ +#define INTELXLVF_ADMIN_EVT_TAIL 0x1000 + +/** Maximum time to wait for a VF admin request to complete */ +#define INTELXLVF_ADMIN_MAX_WAIT_MS 2000 + +/** VF Reset Status Register */ +#define INTELXLVF_VFGEN_RSTAT 0x8800 +#define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 ) +#define INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE 0x2 + +/** Maximum time to wait for reset to complete */ +#define INTELXLVF_RESET_MAX_WAIT_MS 1000 + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v len Length of a single descriptor + * @v tail Tail register offset + */ +static inline __attribute__ (( always_inline)) void +intelxlvf_init_ring ( struct intelxl_ring *ring, unsigned int count, + size_t len, unsigned int tail ) { + + ring->len = ( count * len ); + ring->tail = tail; +} + +#endif /* _INTELXLVF_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 02e13d11b..1a92b6ce2 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -206,6 +206,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 ) #define ERRFILE_intelxl ( ERRFILE_DRIVER | 0x00cb0000 ) #define ERRFILE_pcimsix ( ERRFILE_DRIVER | 0x00cc0000 ) +#define ERRFILE_intelxlvf ( ERRFILE_DRIVER | 0x00cd0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) -- cgit v1.2.3-55-g7522 From 1cdf56f75157d5f4dcfb98dee7b1ba60cd6b93c1 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 2 May 2019 11:00:18 +0100 Subject: [golan] Add various new PCI device IDs Signed-off-by: Mohammed Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 18ebfb1e9..e96ba2698 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -2640,6 +2640,9 @@ static struct pci_device_id golan_nics[] = { PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ), PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ), PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ), + PCI_ROM ( 0x15b3, 0x101b, "ConnectX-6", "ConnectX-6 HCA driver, DevID 4123", 0 ), + PCI_ROM ( 0x15b3, 0x101d, "ConnectX-6DX", "ConnectX-6DX HCA driver, DevID 4125", 0 ), + PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ), }; struct pci_driver golan_driver __pci_driver = { -- cgit v1.2.3-55-g7522 From 3fb3ffccea717ce10bb29dfd8a9e761c7387626e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jul 2019 14:05:48 +0100 Subject: [build] Fix use of inline assembly on GCC 8 ARM64 builds Commit 1a7746603 ("[build] Fix use of inline assembly on GCC 4.8 ARM64 builds") switched from using "%c0" to "%a0" in order to avoid an "invalid operand prefix" error on the ARM64 version of GCC 4.8. It appears that the ARM64 version of GCC 8 now produces an "invalid address mode" error for the "%a0" form, but is happy with the original "%c0" form. Switch back to using the "%c0" form, on the assumption that the requirement for "%a0" was a temporary aberration. Originally-fixed-by: John L. Jolly Signed-off-by: Michael Brown --- src/include/errno.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/include/errno.h b/src/include/errno.h index e80bf9ca5..342384fa4 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -262,10 +262,10 @@ static inline void eplatform_discard ( int dummy __unused, ... ) {} ".align 8\n\t" \ "\n1:\n\t" \ ".long ( 4f - 1b )\n\t" \ - ".long %a0\n\t" \ + ".long %c0\n\t" \ ".long ( 2f - 1b )\n\t" \ ".long ( 3f - 1b )\n\t" \ - ".long %a1\n\t" \ + ".long %c1\n\t" \ "\n2:\t.asciz \"" __einfo_desc ( einfo ) "\"\n\t" \ "\n3:\t.asciz \"" __FILE__ "\"\n\t" \ ".align 8\n\t" \ -- cgit v1.2.3-55-g7522 From c2226b3d1a4d9fc1daeb8dc6b9034d1fb46c1308 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 14 Jul 2019 15:27:01 +0100 Subject: [arm] Provide dummy implementations for {in,out}[s]{b,w,l} It is currently not possible to build the all-drivers iPXE binaries for ARM, since there is no implementation for inb(), outb(), etc. There is no common standard for accessing I/O space on ARM platforms, and there are almost no ARM-compatible peripherals that actually require I/O space accesses. Provide dummy implementations that behave as though no device is present (i.e. ignore writes, return all bits high for reads). This is sufficient to allow the all-drivers binaries to link, and should cause drivers to behave as though no I/O space peripherals are present in the system. Signed-off-by: Michael Brown --- src/arch/arm/include/ipxe/arm_io.h | 77 +++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h index f8765af75..105f22bfb 100644 --- a/src/arch/arm/include/ipxe/arm_io.h +++ b/src/arch/arm/include/ipxe/arm_io.h @@ -43,42 +43,83 @@ IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) { * */ -#define ARM_READX( _api_func, _type, _insn_suffix, _reg_prefix ) \ +#define ARM_READX( _suffix, _type, _insn_suffix, _reg_prefix ) \ static inline __always_inline _type \ -IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \ +IOAPI_INLINE ( arm, read ## _suffix ) ( volatile _type *io_addr ) { \ _type data; \ __asm__ __volatile__ ( "ldr" _insn_suffix " %" _reg_prefix "0, %1" \ : "=r" ( data ) : "Qo" ( *io_addr ) ); \ return data; \ } #ifdef __aarch64__ -ARM_READX ( readb, uint8_t, "b", "w" ); -ARM_READX ( readw, uint16_t, "h", "w" ); -ARM_READX ( readl, uint32_t, "", "w" ); -ARM_READX ( readq, uint64_t, "", "" ); +ARM_READX ( b, uint8_t, "b", "w" ); +ARM_READX ( w, uint16_t, "h", "w" ); +ARM_READX ( l, uint32_t, "", "w" ); +ARM_READX ( q, uint64_t, "", "" ); #else -ARM_READX ( readb, uint8_t, "b", "" ); -ARM_READX ( readw, uint16_t, "h", "" ); -ARM_READX ( readl, uint32_t, "", "" ); +ARM_READX ( b, uint8_t, "b", "" ); +ARM_READX ( w, uint16_t, "h", "" ); +ARM_READX ( l, uint32_t, "", "" ); #endif -#define ARM_WRITEX( _api_func, _type, _insn_suffix, _reg_prefix ) \ +#define ARM_WRITEX( _suffix, _type, _insn_suffix, _reg_prefix ) \ static inline __always_inline void \ -IOAPI_INLINE ( arm, _api_func ) ( _type data, volatile _type *io_addr ) { \ +IOAPI_INLINE ( arm, write ## _suffix ) ( _type data, \ + volatile _type *io_addr ) { \ __asm__ __volatile__ ( "str" _insn_suffix " %" _reg_prefix "0, %1" \ : : "r" ( data ), "Qo" ( *io_addr ) ); \ } #ifdef __aarch64__ -ARM_WRITEX ( writeb, uint8_t, "b", "w" ); -ARM_WRITEX ( writew, uint16_t, "h", "w" ); -ARM_WRITEX ( writel, uint32_t, "", "w" ); -ARM_WRITEX ( writeq, uint64_t, "", "" ); +ARM_WRITEX ( b, uint8_t, "b", "w" ); +ARM_WRITEX ( w, uint16_t, "h", "w" ); +ARM_WRITEX ( l, uint32_t, "", "w" ); +ARM_WRITEX ( q, uint64_t, "", "" ); #else -ARM_WRITEX ( writeb, uint8_t, "b", "" ); -ARM_WRITEX ( writew, uint16_t, "h", "" ); -ARM_WRITEX ( writel, uint32_t, "", "" ); +ARM_WRITEX ( b, uint8_t, "b", "" ); +ARM_WRITEX ( w, uint16_t, "h", "" ); +ARM_WRITEX ( l, uint32_t, "", "" ); #endif +/* + * Dummy PIO reads and writes up to 32 bits + * + * There is no common standard for I/O-space access for ARM, and + * non-MMIO peripherals are vanishingly rare. Provide dummy + * implementations that will allow code to link and should cause + * drivers to simply fail to detect hardware at runtime. + * + */ + +#define ARM_INX( _suffix, _type ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( arm, in ## _suffix ) ( volatile _type *io_addr __unused) { \ + return ~( (_type) 0 ); \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, ins ## _suffix ) ( volatile _type *io_addr __unused, \ + _type *data, unsigned int count ) { \ + memset ( data, 0xff, count * sizeof ( *data ) ); \ +} +ARM_INX ( b, uint8_t ); +ARM_INX ( w, uint16_t ); +ARM_INX ( l, uint32_t ); + +#define ARM_OUTX( _suffix, _type ) \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, out ## _suffix ) ( _type data __unused, \ + volatile _type *io_addr __unused ) { \ + /* Do nothing */ \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, outs ## _suffix ) ( volatile _type *io_addr __unused, \ + const _type *data __unused, \ + unsigned int count __unused ) { \ + /* Do nothing */ \ +} +ARM_OUTX ( b, uint8_t ); +ARM_OUTX ( w, uint16_t ); +ARM_OUTX ( l, uint32_t ); + /* * Slow down I/O * -- cgit v1.2.3-55-g7522 From a385e2376859dc0195ec77aeab220876b201c16b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jul 2019 12:49:47 +0100 Subject: [efi] Return only registered EFI devices from efidev_parent() efidev_parent() currently assumes that any device with BUS_TYPE_EFI is part of a struct efi_device. This assumption is not valid, since the code in efi_device_info() may also create a device with BUS_TYPE_EFI. Fix by searching through the list of registered EFI devices when looking for a match, instead of relying on the bus type value. Signed-off-by: Michael Brown --- src/interface/efi/efi_driver.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 04796414a..7be2e585d 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -73,11 +73,14 @@ static struct efi_device * efidev_find ( EFI_HANDLE device ) { */ struct efi_device * efidev_parent ( struct device *dev ) { struct device *parent; + struct efi_device *efidev; - /* Walk upwards until we find an EFI device */ + /* Walk upwards until we find a registered EFI device */ while ( ( parent = dev->parent ) ) { - if ( parent->desc.bus_type == BUS_TYPE_EFI ) - return container_of ( parent, struct efi_device, dev ); + list_for_each_entry ( efidev, &efi_devices, dev.siblings ) { + if ( parent == &efidev->dev ) + return efidev; + } dev = parent; } -- cgit v1.2.3-55-g7522 From e520a51df1c65d750798420d5ba1bab4271db264 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2019 17:35:39 +0100 Subject: [fdt] Add ability to parse a MAC address from a flattened device tree The Raspberry Pi NIC has no EEPROM to hold the MAC address. The platform firmware (e.g. UEFI or U-Boot) will typically obtain the MAC address from the VideoCore firmware and add it to the device tree, which is then made available to subsequent programs such as iPXE or the Linux kernel. Add the ability to parse a flattened device tree and to extract the MAC address. Signed-off-by: Michael Brown --- src/config/config_fdt.c | 38 ++++ src/config/fdt.h | 16 ++ src/core/fdt.c | 486 +++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/fdt.h | 102 ++++++++++ 5 files changed, 643 insertions(+) create mode 100644 src/config/config_fdt.c create mode 100644 src/config/fdt.h create mode 100644 src/core/fdt.c create mode 100644 src/include/ipxe/fdt.h (limited to 'src') diff --git a/src/config/config_fdt.c b/src/config/config_fdt.c new file mode 100644 index 000000000..85d62ace1 --- /dev/null +++ b/src/config/config_fdt.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Flattened Device Tree configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in devicetree sources + */ diff --git a/src/config/fdt.h b/src/config/fdt.h new file mode 100644 index 000000000..4d13e0535 --- /dev/null +++ b/src/config/fdt.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_FDT_H +#define CONFIG_FDT_H + +/** @file + * + * Flattened Device Tree configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#include + +#endif /* CONFIG_FDT_H */ diff --git a/src/core/fdt.c b/src/core/fdt.c new file mode 100644 index 000000000..f439422cf --- /dev/null +++ b/src/core/fdt.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2019 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Flattened Device Tree + * + */ + +/** The system flattened device tree (if present) */ +static struct fdt fdt; + +/** A position within a device tree */ +struct fdt_cursor { + /** Offset within structure block */ + unsigned int offset; + /** Tree depth */ + int depth; +}; + +/** A lexical descriptor */ +struct fdt_descriptor { + /** Node or property name (if applicable) */ + const char *name; + /** Property data (if applicable) */ + const void *data; + /** Length of property data (if applicable) */ + size_t len; +}; + +/** + * Check if device tree exists + * + * @v has_fdt Device tree exists + */ +static inline __attribute__ (( always_inline )) int fdt_exists ( void ) { + + return ( fdt.hdr != NULL ); +} + +/** + * Traverse device tree + * + * @v pos Position within device tree + * @v desc Lexical descriptor to fill in + * @ret rc Return status code + */ +static int fdt_traverse ( struct fdt_cursor *pos, + struct fdt_descriptor *desc ) { + const fdt_token_t *token; + const void *data; + const struct fdt_prop *prop; + unsigned int name_off; + size_t remaining; + size_t len; + + /* Sanity checks */ + assert ( pos->offset < fdt.len ); + assert ( ( pos->offset & ( FDT_STRUCTURE_ALIGN - 1 ) ) == 0 ); + + /* Clear descriptor */ + memset ( desc, 0, sizeof ( *desc ) ); + + /* Locate token and calculate remaining space */ + token = ( fdt.raw + fdt.structure + pos->offset ); + remaining = ( fdt.len - pos->offset ); + if ( remaining < sizeof ( *token ) ) { + DBGC ( &fdt, "FDT truncated tree at +%#04x\n", pos->offset ); + return -EINVAL; + } + remaining -= sizeof ( *token ); + data = ( ( ( const void * ) token ) + sizeof ( *token ) ); + len = 0; + + /* Handle token */ + switch ( *token ) { + + case cpu_to_be32 ( FDT_BEGIN_NODE ): + + /* Start of node */ + desc->name = data; + len = ( strnlen ( desc->name, remaining ) + 1 /* NUL */ ); + if ( remaining < len ) { + DBGC ( &fdt, "FDT unterminated node name at +%#04x\n", + pos->offset ); + return -EINVAL; + } + pos->depth++; + break; + + case cpu_to_be32 ( FDT_END_NODE ): + + /* End of node */ + if ( pos->depth < 0 ) { + DBGC ( &fdt, "FDT spurious node end at +%#04x\n", + pos->offset ); + return -EINVAL; + } + pos->depth--; + if ( pos->depth < 0 ) { + /* End of (sub)tree */ + return -ENOENT; + } + break; + + case cpu_to_be32 ( FDT_PROP ): + + /* Property */ + prop = data; + if ( remaining < sizeof ( *prop ) ) { + DBGC ( &fdt, "FDT truncated property at +%#04x\n", + pos->offset ); + return -EINVAL; + } + desc->data = ( ( ( const void * ) prop ) + sizeof ( *prop ) ); + desc->len = be32_to_cpu ( prop->len ); + len = ( sizeof ( *prop ) + desc->len ); + if ( remaining < len ) { + DBGC ( &fdt, "FDT overlength property at +%#04x\n", + pos->offset ); + return -EINVAL; + } + name_off = be32_to_cpu ( prop->name_off ); + if ( name_off > fdt.strings_len ) { + DBGC ( &fdt, "FDT property name outside strings " + "block at +%#04x\n", pos->offset ); + return -EINVAL; + } + desc->name = ( fdt.raw + fdt.strings + name_off ); + break; + + case cpu_to_be32 ( FDT_NOP ): + + /* Do nothing */ + break; + + default: + + /* Unrecognised or unexpected token */ + DBGC ( &fdt, "FDT unexpected token %#08x at +%#04x\n", + be32_to_cpu ( *token ), pos->offset ); + return -EINVAL; + } + + /* Update cursor */ + len = ( ( len + FDT_STRUCTURE_ALIGN - 1 ) & + ~( FDT_STRUCTURE_ALIGN - 1 ) ); + pos->offset += ( sizeof ( *token ) + len ); + + /* Sanity checks */ + assert ( pos->offset <= fdt.len ); + + return 0; +} + +/** + * Find child node + * + * @v offset Starting node offset + * @v name Node name + * @v child Child node offset to fill in + * @ret rc Return status code + */ +static int fdt_child ( unsigned int offset, const char *name, + unsigned int *child ) { + struct fdt_cursor pos; + struct fdt_descriptor desc; + unsigned int orig_offset; + int rc; + + /* Record original offset (for debugging) */ + orig_offset = offset; + + /* Initialise cursor */ + pos.offset = offset; + pos.depth = -1; + + /* Find child node */ + while ( 1 ) { + + /* Record current offset */ + *child = pos.offset; + + /* Traverse tree */ + if ( ( rc = fdt_traverse ( &pos, &desc ) ) != 0 ) { + DBGC ( &fdt, "FDT +%#04x has no child node \"%s\": " + "%s\n", orig_offset, name, strerror ( rc ) ); + return rc; + } + + /* Check for matching immediate child node */ + if ( ( pos.depth == 1 ) && desc.name && ( ! desc.data ) ) { + DBGC2 ( &fdt, "FDT +%#04x has child node \"%s\"\n", + orig_offset, desc.name ); + if ( strcmp ( name, desc.name ) == 0 ) { + DBGC2 ( &fdt, "FDT +%#04x found child node " + "\"%s\" at +%#04x\n", orig_offset, + desc.name, *child ); + return 0; + } + } + } +} + +/** + * Find node by path + * + * @v path Node path + * @v offset Offset to fill in + * @ret rc Return status code + */ +int fdt_path ( const char *path, unsigned int *offset ) { + char *tmp = ( ( char * ) path ); + char *del; + int rc; + + /* Initialise offset */ + *offset = 0; + + /* Traverse tree one path segment at a time */ + while ( *tmp ) { + + /* Skip any leading '/' */ + while ( *tmp == '/' ) + tmp++; + + /* Find next '/' delimiter and convert to NUL */ + del = strchr ( tmp, '/' ); + if ( del ) + *del = '\0'; + + /* Find child and restore delimiter */ + rc = fdt_child ( *offset, tmp, offset ); + if ( del ) + *del = '/'; + if ( rc != 0 ) + return rc; + + /* Move to next path component, if any */ + while ( *tmp && ( *tmp != '/' ) ) + tmp++; + } + + DBGC2 ( &fdt, "FDT found path \"%s\" at +%#04x\n", path, *offset ); + return 0; +} + +/** + * Find node by alias + * + * @v name Alias name + * @v offset Offset to fill in + * @ret rc Return status code + */ +int fdt_alias ( const char *name, unsigned int *offset ) { + const char *alias; + int rc; + + /* Locate "/aliases" node */ + if ( ( rc = fdt_child ( 0, "aliases", offset ) ) != 0 ) + return rc; + + /* Locate alias property */ + if ( ( alias = fdt_string ( *offset, name ) ) == NULL ) + return -ENOENT; + DBGC ( &fdt, "FDT alias \"%s\" is \"%s\"\n", name, alias ); + + /* Locate aliased node */ + if ( ( rc = fdt_path ( alias, offset ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Find property + * + * @v offset Starting node offset + * @v name Property name + * @v desc Lexical descriptor to fill in + * @ret rc Return status code + */ +static int fdt_property ( unsigned int offset, const char *name, + struct fdt_descriptor *desc ) { + struct fdt_cursor pos; + int rc; + + /* Initialise cursor */ + pos.offset = offset; + pos.depth = -1; + + /* Find property */ + while ( 1 ) { + + /* Traverse tree */ + if ( ( rc = fdt_traverse ( &pos, desc ) ) != 0 ) { + DBGC ( &fdt, "FDT +%#04x has no property \"%s\": %s\n", + offset, name, strerror ( rc ) ); + return rc; + } + + /* Check for matching immediate child property */ + if ( ( pos.depth == 0 ) && desc->data ) { + DBGC2 ( &fdt, "FDT +%#04x has property \"%s\" len " + "%#zx\n", offset, desc->name, desc->len ); + if ( strcmp ( name, desc->name ) == 0 ) { + DBGC2 ( &fdt, "FDT +%#04x found property " + "\"%s\"\n", offset, desc->name ); + DBGC2_HDA ( &fdt, 0, desc->data, desc->len ); + return 0; + } + } + } +} + +/** + * Find string property + * + * @v offset Starting node offset + * @v name Property name + * @ret string String property, or NULL on error + */ +const char * fdt_string ( unsigned int offset, const char *name ) { + struct fdt_descriptor desc; + int rc; + + /* Find property */ + if ( ( rc = fdt_property ( offset, name, &desc ) ) != 0 ) + return NULL; + + /* Check NUL termination */ + if ( strnlen ( desc.data, desc.len ) == desc.len ) { + DBGC ( &fdt, "FDT unterminated string property \"%s\"\n", + name ); + return NULL; + } + + return desc.data; +} + +/** + * Get MAC address from property + * + * @v offset Starting node offset + * @v netdev Network device + * @ret rc Return status code + */ +int fdt_mac ( unsigned int offset, struct net_device *netdev ) { + struct fdt_descriptor desc; + size_t len; + int rc; + + /* Find applicable MAC address property */ + if ( ( ( rc = fdt_property ( offset, "mac-address", &desc ) ) != 0 ) && + ( ( rc = fdt_property ( offset, "local-mac-address", + &desc ) ) != 0 ) ) { + return rc; + } + + /* Check length */ + len = netdev->ll_protocol->hw_addr_len; + if ( len != desc.len ) { + DBGC ( &fdt, "FDT malformed MAC address \"%s\":\n", + desc.name ); + DBGC_HDA ( &fdt, 0, desc.data, desc.len ); + return -ERANGE; + } + + /* Fill in MAC address */ + memcpy ( netdev->hw_addr, desc.data, len ); + + return 0; +} + +/** + * Register device tree + * + * @v fdt Device tree header + * @ret rc Return status code + */ +int register_fdt ( const struct fdt_header *hdr ) { + const uint8_t *end; + + /* Record device tree location */ + fdt.hdr = hdr; + fdt.len = be32_to_cpu ( hdr->totalsize ); + DBGC ( &fdt, "FDT version %d at %p+%#04zx\n", + be32_to_cpu ( hdr->version ), fdt.hdr, fdt.len ); + + /* Check signature */ + if ( hdr->magic != cpu_to_be32 ( FDT_MAGIC ) ) { + DBGC ( &fdt, "FDT has invalid magic value %#08x\n", + be32_to_cpu ( hdr->magic ) ); + goto err; + } + + /* Check version */ + if ( hdr->last_comp_version != cpu_to_be32 ( FDT_VERSION ) ) { + DBGC ( &fdt, "FDT unsupported version %d\n", + be32_to_cpu ( hdr->last_comp_version ) ); + goto err; + } + + /* Record structure block location */ + fdt.structure = be32_to_cpu ( hdr->off_dt_struct ); + fdt.structure_len = be32_to_cpu ( hdr->size_dt_struct ); + DBGC ( &fdt, "FDT structure block at +[%#04x,%#04zx)\n", + fdt.structure, ( fdt.structure + fdt.structure_len ) ); + if ( ( fdt.structure > fdt.len ) || + ( fdt.structure_len > ( fdt.len - fdt.structure ) ) ) { + DBGC ( &fdt, "FDT structure block exceeds table\n" ); + goto err; + } + if ( ( fdt.structure | fdt.structure_len ) & + ( FDT_STRUCTURE_ALIGN - 1 ) ) { + DBGC ( &fdt, "FDT structure block is misaligned\n" ); + goto err; + } + + /* Record strings block location */ + fdt.strings = be32_to_cpu ( hdr->off_dt_strings ); + fdt.strings_len = be32_to_cpu ( hdr->size_dt_strings ); + DBGC ( &fdt, "FDT strings block at +[%#04x,%#04zx)\n", + fdt.strings, ( fdt.strings + fdt.strings_len ) ); + if ( ( fdt.strings > fdt.len ) || + ( fdt.strings_len > ( fdt.len - fdt.strings ) ) ) { + DBGC ( &fdt, "FDT strings block exceeds table\n" ); + goto err; + } + + /* Shrink strings block to ensure NUL termination safety */ + end = ( fdt.raw + fdt.strings + fdt.strings_len ); + for ( ; fdt.strings_len ; fdt.strings_len-- ) { + if ( *(--end) == '\0' ) + break; + } + if ( fdt.strings_len != be32_to_cpu ( hdr->size_dt_strings ) ) { + DBGC ( &fdt, "FDT strings block shrunk to +[%#04x,%#04zx)\n", + fdt.strings, ( fdt.strings + fdt.strings_len ) ); + } + + /* Print model name (for debugging) */ + DBGC ( &fdt, "FDT model is \"%s\"\n", fdt_string ( 0, "model" ) ); + + return 0; + + err: + DBGC_HDA ( &fdt, 0, hdr, sizeof ( *hdr ) ); + fdt.hdr = NULL; + return -EINVAL; +} + +/* Drag in objects via register_fdt */ +REQUIRING_SYMBOL ( register_fdt ); + +/* Drag in device tree configuration */ +REQUIRE_OBJECT ( config_fdt ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 1a92b6ce2..242f91f82 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -74,6 +74,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_block ( ERRFILE_CORE | 0x00220000 ) #define ERRFILE_sanboot ( ERRFILE_CORE | 0x00230000 ) #define ERRFILE_dummy_sanboot ( ERRFILE_CORE | 0x00240000 ) +#define ERRFILE_fdt ( ERRFILE_CORE | 0x00250000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/include/ipxe/fdt.h b/src/include/ipxe/fdt.h new file mode 100644 index 000000000..97efa100c --- /dev/null +++ b/src/include/ipxe/fdt.h @@ -0,0 +1,102 @@ +#ifndef _IPXE_FDT_H +#define _IPXE_FDT_H + +/** @file + * + * Flattened Device Tree + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +struct net_device; + +/** Device tree header */ +struct fdt_header { + /** Magic signature */ + uint32_t magic; + /** Total size of device tree */ + uint32_t totalsize; + /** Offset to structure block */ + uint32_t off_dt_struct; + /** Offset to strings block */ + uint32_t off_dt_strings; + /** Offset to memory reservation block */ + uint32_t off_mem_rsvmap; + /** Version of this data structure */ + uint32_t version; + /** Lowest version to which this structure is compatible */ + uint32_t last_comp_version; + /** Physical ID of the boot CPU */ + uint32_t boot_cpuid_phys; + /** Length of string block */ + uint32_t size_dt_strings; + /** Length of structure block */ + uint32_t size_dt_struct; +} __attribute__ (( packed )); + +/** Magic signature */ +#define FDT_MAGIC 0xd00dfeed + +/** Expected device tree version */ +#define FDT_VERSION 16 + +/** Device tree token */ +typedef uint32_t fdt_token_t; + +/** Begin node token */ +#define FDT_BEGIN_NODE 0x00000001 + +/** End node token */ +#define FDT_END_NODE 0x00000002 + +/** Property token */ +#define FDT_PROP 0x00000003 + +/** Property fragment */ +struct fdt_prop { + /** Data length */ + uint32_t len; + /** Name offset */ + uint32_t name_off; +} __attribute__ (( packed )); + +/** NOP token */ +#define FDT_NOP 0x00000004 + +/** End of structure block */ +#define FDT_END 0x00000009 + +/** Alignment of structure block */ +#define FDT_STRUCTURE_ALIGN ( sizeof ( fdt_token_t ) ) + +/** A device tree */ +struct fdt { + /** Tree data */ + union { + /** Tree header */ + const struct fdt_header *hdr; + /** Raw data */ + const void *raw; + }; + /** Length of tree */ + size_t len; + /** Offset to structure block */ + unsigned int structure; + /** Length of structure block */ + size_t structure_len; + /** Offset to strings block */ + unsigned int strings; + /** Length of strings block */ + size_t strings_len; +}; + +extern int fdt_path ( const char *path, unsigned int *offset ); +extern int fdt_alias ( const char *name, unsigned int *offset ); +extern const char * fdt_string ( unsigned int offset, const char *name ); +extern int fdt_mac ( unsigned int offset, struct net_device *netdev ); +extern int register_fdt ( const struct fdt_header *hdr ); + +#endif /* _IPXE_FDT_H */ -- cgit v1.2.3-55-g7522 From 6dde0f60bfab458208a763532c9e776b32deebb3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2019 17:42:12 +0100 Subject: [efi] Register a device tree if provided by the platform firmware Signed-off-by: Michael Brown --- src/config/config_fdt.c | 3 ++ src/config/defaults/efi.h | 1 + src/interface/efi/efi_fdt.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/interface/efi/efi_fdt.c (limited to 'src') diff --git a/src/config/config_fdt.c b/src/config/config_fdt.c index 85d62ace1..e8d425933 100644 --- a/src/config/config_fdt.c +++ b/src/config/config_fdt.c @@ -36,3 +36,6 @@ PROVIDE_REQUIRING_SYMBOL(); /* * Drag in devicetree sources */ +#ifdef FDT_EFI +REQUIRE_OBJECT ( efi_fdt ); +#endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 74effa425..53a7a7b4b 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define TIME_EFI #define REBOOT_EFI #define ACPI_EFI +#define FDT_EFI #define DOWNLOAD_PROTO_FILE /* Local filesystem access */ diff --git a/src/interface/efi/efi_fdt.c b/src/interface/efi/efi_fdt.c new file mode 100644 index 000000000..cd3f109df --- /dev/null +++ b/src/interface/efi/efi_fdt.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** @file + * + * EFI Flattened Device Tree + * + */ + +#define DEVICE_TREE_TABLE_GUID \ + { 0xb1b621d5, 0xf19c, 0x41a5, \ + { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } } + +/** EFI Flattened Device Tree configuration table */ +static struct fdt_header *efi_fdt; +EFI_USE_TABLE ( DEVICE_TREE_TABLE, &efi_fdt, 0 ); + +/** + * Initialise EFI Flattened Device Tree + * + */ +static void efi_fdt_init ( void ) { + int rc; + + /* Do nothing if no configuration table is present */ + if ( ! efi_fdt ) { + DBGC ( &efi_fdt, "EFIFDT has no configuration table\n" ); + return; + } + DBGC ( &efi_fdt, "EFIFDT configuration table at %p\n", efi_fdt ); + + /* Register device tree */ + if ( ( rc = register_fdt ( efi_fdt ) ) != 0 ) { + DBGC ( &efi_fdt, "EFIFDT could not register: %s\n", + strerror ( rc ) ); + return; + } +} + +/** EFI Flattened Device Tree initialisation function */ +struct init_fn efi_fdt_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = efi_fdt_init, +}; -- cgit v1.2.3-55-g7522 From 83e0f9f377246ba99f61e24a9074122d86ec3f20 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2019 17:43:39 +0100 Subject: [smsc95xx] Fetch MAC from device tree for Raspberry Pi Signed-off-by: Michael Brown --- src/drivers/net/smsc95xx.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src') diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 9b09657db..4ac3f42f0 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "smsc95xx.h" /** @file @@ -158,6 +159,32 @@ static int smsc95xx_vm3_fetch_mac ( struct smscusb_device *smscusb ) { return 0; } +/** + * Fetch MAC address from device tree + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +static int smsc95xx_fdt_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + unsigned int offset; + int rc; + + /* Look for "ethernet[0]" alias */ + if ( ( rc = fdt_alias ( "ethernet", &offset ) != 0 ) && + ( rc = fdt_alias ( "ethernet0", &offset ) != 0 ) ) { + return rc; + } + + /* Fetch MAC address */ + if ( ( rc = fdt_mac ( offset, netdev ) ) != 0 ) + return rc; + + DBGC ( smscusb, "SMSC95XX %p using FDT MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + /** * Fetch MAC address * @@ -173,6 +200,10 @@ static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) { SMSC95XX_E2P_BASE ) ) == 0 ) return 0; + /* Read MAC address from device tree */ + if ( ( rc = smsc95xx_fdt_fetch_mac ( smscusb ) ) == 0 ) + return 0; + /* Construct MAC address for Honeywell VM3, if applicable */ if ( ( rc = smsc95xx_vm3_fetch_mac ( smscusb ) ) == 0 ) return 0; -- cgit v1.2.3-55-g7522 From a046329012856f33eeedc428237564e66367cd90 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2019 17:45:22 +0100 Subject: [build] Add named configuration for Raspberry Pi Signed-off-by: Michael Brown --- src/config/rpi/colour.h | 0 src/config/rpi/console.h | 0 src/config/rpi/crypto.h | 0 src/config/rpi/general.h | 0 src/config/rpi/serial.h | 0 src/config/rpi/settings.h | 0 src/config/rpi/sideband.h | 0 src/config/rpi/usb.h | 13 +++++++++++++ 8 files changed, 13 insertions(+) create mode 100644 src/config/rpi/colour.h create mode 100644 src/config/rpi/console.h create mode 100644 src/config/rpi/crypto.h create mode 100644 src/config/rpi/general.h create mode 100644 src/config/rpi/serial.h create mode 100644 src/config/rpi/settings.h create mode 100644 src/config/rpi/sideband.h create mode 100644 src/config/rpi/usb.h (limited to 'src') diff --git a/src/config/rpi/colour.h b/src/config/rpi/colour.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/console.h b/src/config/rpi/console.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/crypto.h b/src/config/rpi/crypto.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/general.h b/src/config/rpi/general.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/serial.h b/src/config/rpi/serial.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/settings.h b/src/config/rpi/settings.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/sideband.h b/src/config/rpi/sideband.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/config/rpi/usb.h b/src/config/rpi/usb.h new file mode 100644 index 000000000..f17ea0de3 --- /dev/null +++ b/src/config/rpi/usb.h @@ -0,0 +1,13 @@ +/* + * Use EFI_USB_IO_PROTOCOL + * + * The Raspberry Pi uses an embedded DesignWare USB controller for + * which we do not have a native driver. Use via the + * EFI_USB_IO_PROTOCOL driver instead. + * + */ +#undef USB_HCD_XHCI +#undef USB_HCD_EHCI +#undef USB_HCD_UHCI +#define USB_HCD_USBIO +#undef USB_EFI -- cgit v1.2.3-55-g7522 From f4cc5834efe1b8fd366ec35cc016cd23527e2f42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 Jul 2019 19:15:33 +0100 Subject: [smscusb] Fetch MAC from device tree for Raspberry Pi Model B+ Signed-off-by: Michael Brown --- src/drivers/net/lan78xx.c | 4 ++++ src/drivers/net/smsc95xx.c | 31 ++----------------------------- src/drivers/net/smscusb.c | 34 ++++++++++++++++++++++++++++++++++ src/drivers/net/smscusb.h | 1 + 4 files changed, 41 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/drivers/net/lan78xx.c b/src/drivers/net/lan78xx.c index 3f705203e..a8cf57408 100644 --- a/src/drivers/net/lan78xx.c +++ b/src/drivers/net/lan78xx.c @@ -97,6 +97,10 @@ static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) { if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 ) return 0; + /* Read MAC address from device tree, if present */ + if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 ) + return 0; + /* Otherwise, generate a random MAC address */ eth_random_addr ( netdev->hw_addr ); DBGC ( smscusb, "LAN78XX %p using random MAC %s\n", diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c index 4ac3f42f0..3ec49584d 100644 --- a/src/drivers/net/smsc95xx.c +++ b/src/drivers/net/smsc95xx.c @@ -33,7 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include "smsc95xx.h" /** @file @@ -159,32 +158,6 @@ static int smsc95xx_vm3_fetch_mac ( struct smscusb_device *smscusb ) { return 0; } -/** - * Fetch MAC address from device tree - * - * @v smscusb SMSC USB device - * @ret rc Return status code - */ -static int smsc95xx_fdt_fetch_mac ( struct smscusb_device *smscusb ) { - struct net_device *netdev = smscusb->netdev; - unsigned int offset; - int rc; - - /* Look for "ethernet[0]" alias */ - if ( ( rc = fdt_alias ( "ethernet", &offset ) != 0 ) && - ( rc = fdt_alias ( "ethernet0", &offset ) != 0 ) ) { - return rc; - } - - /* Fetch MAC address */ - if ( ( rc = fdt_mac ( offset, netdev ) ) != 0 ) - return rc; - - DBGC ( smscusb, "SMSC95XX %p using FDT MAC %s\n", - smscusb, eth_ntoa ( netdev->hw_addr ) ); - return 0; -} - /** * Fetch MAC address * @@ -200,8 +173,8 @@ static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) { SMSC95XX_E2P_BASE ) ) == 0 ) return 0; - /* Read MAC address from device tree */ - if ( ( rc = smsc95xx_fdt_fetch_mac ( smscusb ) ) == 0 ) + /* Read MAC address from device tree, if present */ + if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 ) return 0; /* Construct MAC address for Honeywell VM3, if applicable */ diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c index 538d338c4..c639c58c1 100644 --- a/src/drivers/net/smscusb.c +++ b/src/drivers/net/smscusb.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "smscusb.h" /** @file @@ -439,6 +440,39 @@ int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, return 0; } +/****************************************************************************** + * + * Device tree + * + ****************************************************************************** + */ + +/** + * Fetch MAC address from device tree + * + * @v smscusb SMSC USB device + * @ret rc Return status code + */ +int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb ) { + struct net_device *netdev = smscusb->netdev; + unsigned int offset; + int rc; + + /* Look for "ethernet[0]" alias */ + if ( ( rc = fdt_alias ( "ethernet", &offset ) != 0 ) && + ( rc = fdt_alias ( "ethernet0", &offset ) != 0 ) ) { + return rc; + } + + /* Fetch MAC address */ + if ( ( rc = fdt_mac ( offset, netdev ) ) != 0 ) + return rc; + + DBGC ( smscusb, "SMSCUSB %p using FDT MAC %s\n", + smscusb, eth_ntoa ( netdev->hw_addr ) ); + return 0; +} + /****************************************************************************** * * MII access diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h index b5d9ad3fc..e866bb747 100644 --- a/src/drivers/net/smscusb.h +++ b/src/drivers/net/smscusb.h @@ -287,6 +287,7 @@ extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb, unsigned int e2p_base ); extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb, unsigned int otp_base ); +extern int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb ); extern int smscusb_mii_check_link ( struct smscusb_device *smscusb ); extern int smscusb_mii_open ( struct smscusb_device *smscusb, unsigned int phy_mask, unsigned int intrs ); -- cgit v1.2.3-55-g7522 From 412acd7854de10e7194f362a6b1a3257a17974f7 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Sun, 9 Jun 2019 13:30:11 +0300 Subject: [build] Fix "'%s' directive argument is null" error Use '%p' directive, and print handle's address if the address is null and the handle doesn't have a name. This fixes the following compilation error: interface/efi/efi_debug.c:334:3: error: '%s' directive argument is null [-Werror=format-overflow=] Signed-off-by: Valentine Barshak Signed-off-by: Michael Brown --- src/interface/efi/efi_debug.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 8ea0a822d..de9b1af55 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -331,8 +331,7 @@ void dbg_efi_protocols ( EFI_HANDLE handle ) { /* Sanity check */ if ( ! handle ) { - printf ( "HANDLE %s could not retrieve protocols\n", - efi_handle_name ( handle ) ); + printf ( "HANDLE %p could not retrieve protocols\n", handle ); return; } -- cgit v1.2.3-55-g7522 From 1dd56dbd11082fb622c2ed21cfaced4f47d798a6 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Mon, 22 Jul 2019 10:47:50 +0100 Subject: [build] Workaround compilation error with gcc 9.1 Compiling with gcc 9.1 generates lots of "taking address of packed member of ... may result in an unaligned pointer value" warnings. Some of these warnings are genuine, and indicate correctly that parts of iPXE currently require the CPU (or runtime environment) to support unaligned accesses. For example: the TCP/IP receive data path will attempt to access 32-bit fields that may not be aligned to a 32-bit boundary. Other warnings are either spurious (such as when the pointer is to a variable-length byte array, which can have no alignment requirement anyway) or unhelpful (such as when the pointer is used solely to provide a debug colour value for the DBGC() macro). There appears to be no easy way to silence the spurious warnings. Since the ability to perform unaligned accesses is already a requirement for iPXE, work around the problem by silencing this class of warnings. Signed-off-by: Valentine Barshak Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index f8334921b..4b09e81f0 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -185,6 +185,15 @@ WNST_TEST = $(CC) -Wstringop-truncation -x c -c /dev/null -o /dev/null \ >/dev/null 2>&1 WNST_FLAGS := $(shell $(WNST_TEST) && $(ECHO) '-Wno-stringop-truncation') WORKAROUND_CFLAGS += $(WNST_FLAGS) + +# gcc 9.1 generates warnings for taking address of packed member which +# may result in an unaligned pointer value. Inhibit the warnings. +# +WNAPM_TEST = $(CC) -Wno-address-of-packed-member -x c -c /dev/null \ + -o /dev/null >/dev/null 2>&1 +WNAPM_FLAGS := $(shell $(WNAPM_TEST) && \ + $(ECHO) '-Wno-address-of-packed-member') +WORKAROUND_CFLAGS += $(WNAPM_FLAGS) endif # Some versions of gas choke on division operators, treating them as -- cgit v1.2.3-55-g7522 From a4f8c6e31f6c62522cfc633bbbffa81b22f9d6f3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Jul 2019 14:51:28 +0100 Subject: [build] Do not apply WORKAROUND_CFLAGS for host compiler The WORKAROUND_CFLAGS list is constructed based on running tests on the target compiler, and the results may not be valid for the host compiler. The only relevant workaround required for the host compiler is -Wno-stringop-truncation, which is needed to avoid a spurious compiler warning for a totally correct usage of strncpy() in util/elf2efi.c. Duplicating the workaround tests for the host compiler is messy, as is conditionally applying __attribute__((nonstring)). Fix instead by disapplying WORKAROUND_CFLAGS for the host compiler, and using memcpy() with an explicitly calculated length instead of strncpy() in util/elf2efi.c. Reported-by: Ignat Korchagin Reported-by: Christopher Clark Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 2 +- src/util/elf2efi.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 4b09e81f0..1b175b950 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -454,7 +454,7 @@ endif CFLAGS += $(WORKAROUND_CFLAGS) $(EXTRA_CFLAGS) ASFLAGS += $(WORKAROUND_ASFLAGS) $(EXTRA_ASFLAGS) LDFLAGS += $(WORKAROUND_LDFLAGS) $(EXTRA_LDFLAGS) -HOST_CFLAGS += $(WORKAROUND_CFLAGS) -O2 -g +HOST_CFLAGS += -O2 -g # Inhibit -Werror if NO_WERROR is specified on make command line # diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 2c5b9df8a..bcd53c9af 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -458,6 +458,7 @@ static struct pe_section * process_section ( struct elf_file *elf, struct pe_header *pe_header ) { struct pe_section *new; const char *name; + size_t name_len; size_t section_memsz; size_t section_filesz; unsigned long code_start; @@ -494,7 +495,10 @@ static struct pe_section * process_section ( struct elf_file *elf, memset ( new, 0, sizeof ( *new ) + section_filesz ); /* Fill in section header details */ - strncpy ( ( char * ) new->hdr.Name, name, sizeof ( new->hdr.Name ) ); + name_len = strlen ( name ); + if ( name_len > sizeof ( new->hdr.Name ) ) + name_len = sizeof ( new->hdr.Name ); + memcpy ( new->hdr.Name, name, name_len ); new->hdr.Misc.VirtualSize = section_memsz; new->hdr.VirtualAddress = shdr->sh_addr; new->hdr.SizeOfRawData = section_filesz; -- cgit v1.2.3-55-g7522