diff options
| author | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
| commit | 80109b804a700384799d54134ceebf4f40a8b7a3 (patch) | |
| tree | bbdf311ed02dd2c5c1f0a8d1997519ed2c24d93e | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [tls] Add support for Ephemeral Diffie-Hellman key exchange (diff) | |
| download | ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.gz ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.xz ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.zip | |
Merge branch 'master' into openslx
62 files changed, 5696 insertions, 824 deletions
diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8226fea..f4659a1ac 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -45,7 +45,7 @@ void pcidirect_prepare ( struct pci_device *pci, int where ) { PCIDIRECT_CONFIG_ADDRESS ); } -PROVIDE_PCIAPI_INLINE ( direct, pci_num_bus ); +PROVIDE_PCIAPI_INLINE ( direct, pci_discover ); PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word ); PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_dword ); @@ -53,3 +53,5 @@ PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_byte ); PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_word ); PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_dword ); PROVIDE_PCIAPI_INLINE ( direct, pci_ioremap ); + +struct pci_api pcidirect_api = PCIAPI_RUNTIME ( direct ); diff --git a/src/arch/x86/include/bits/pci_io.h b/src/arch/x86/include/bits/pci_io.h index b41e562ee..a074d3370 100644 --- a/src/arch/x86/include/bits/pci_io.h +++ b/src/arch/x86/include/bits/pci_io.h @@ -11,5 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/pcibios.h> #include <ipxe/pcidirect.h> +#include <ipxe/pcicloud.h> #endif /* _BITS_PCI_IO_H */ diff --git a/src/arch/x86/include/ipxe/pcibios.h b/src/arch/x86/include/ipxe/pcibios.h index bae4eede1..3caea1cfe 100644 --- a/src/arch/x86/include/ipxe/pcibios.h +++ b/src/arch/x86/include/ipxe/pcibios.h @@ -145,4 +145,6 @@ PCIAPI_INLINE ( pcbios, pci_ioremap ) ( struct pci_device *pci __unused, return ioremap ( bus_addr, len ); } +extern struct pci_api pcibios_api; + #endif /* _IPXE_PCIBIOS_H */ diff --git a/src/arch/x86/include/ipxe/pcicloud.h b/src/arch/x86/include/ipxe/pcicloud.h new file mode 100644 index 000000000..52268908c --- /dev/null +++ b/src/arch/x86/include/ipxe/pcicloud.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_PCICLOUD_H +#define _IPXE_PCICLOUD_H + +/** @file + * + * Cloud VM PCI configuration space access + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef PCIAPI_CLOUD +#define PCIAPI_PREFIX_cloud +#else +#define PCIAPI_PREFIX_cloud __cloud_ +#endif + +#endif /* _IPXE_PCICLOUD_H */ diff --git a/src/arch/x86/include/ipxe/pcidirect.h b/src/arch/x86/include/ipxe/pcidirect.h index decdc8100..98c6a2bbb 100644 --- a/src/arch/x86/include/ipxe/pcidirect.h +++ b/src/arch/x86/include/ipxe/pcidirect.h @@ -26,14 +26,18 @@ struct pci_device; extern void pcidirect_prepare ( struct pci_device *pci, int where ); /** - * Determine number of PCI buses within system + * Find next PCI bus:dev.fn address range in system * - * @ret num_bus Number of buses + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -static inline __always_inline int -PCIAPI_INLINE ( direct, pci_num_bus ) ( void ) { +static inline __always_inline void +PCIAPI_INLINE ( direct, pci_discover ) ( uint32_t busdevfn __unused, + struct pci_range *range ) { + /* Scan first bus and rely on bridge detection to find higher buses */ - return 1; + range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 ); + range->count = PCI_BUSDEVFN ( 0, 1, 0, 0 ); } /** @@ -151,4 +155,6 @@ PCIAPI_INLINE ( direct, pci_ioremap ) ( struct pci_device *pci __unused, return ioremap ( bus_addr, len ); } +extern struct pci_api pcidirect_api; + #endif /* _PCIDIRECT_H */ diff --git a/src/arch/x86/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c index bf812f77f..7b7a769e3 100644 --- a/src/arch/x86/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -34,13 +34,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * Determine number of PCI buses within system + * Find next PCI bus:dev.fn address range in system * - * @ret num_bus Number of buses + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -static int pcibios_num_bus ( void ) { +static void pcibios_discover ( uint32_t busdevfn __unused, + struct pci_range *range ) { int discard_a, discard_D; - uint8_t max_bus; + uint16_t num_bus; /* We issue this call using flat real mode, to work around a * bug in some HP BIOSes. @@ -48,16 +50,20 @@ static int pcibios_num_bus ( void ) { __asm__ __volatile__ ( REAL_CODE ( "call flatten_real_mode\n\t" "stc\n\t" "int $0x1a\n\t" + "movzbw %%cl, %%cx\n\t" + "incw %%cx\n\t" "jnc 1f\n\t" "xorw %%cx, %%cx\n\t" "\n1:\n\t" ) - : "=c" ( max_bus ), "=a" ( discard_a ), + : "=c" ( num_bus ), "=a" ( discard_a ), "=D" ( discard_D ) : "a" ( PCIBIOS_INSTALLATION_CHECK >> 16 ), "D" ( 0 ) : "ebx", "edx" ); - return ( max_bus + 1 ); + /* Populate range */ + range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 ); + range->count = PCI_BUSDEVFN ( 0, num_bus, 0, 0 ); } /** @@ -114,7 +120,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ return ( status >> 8 ); } -PROVIDE_PCIAPI ( pcbios, pci_num_bus, pcibios_num_bus ); +PROVIDE_PCIAPI ( pcbios, pci_discover, pcibios_discover ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_dword ); @@ -122,3 +128,5 @@ PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_byte ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_word ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_dword ); PROVIDE_PCIAPI_INLINE ( pcbios, pci_ioremap ); + +struct pci_api pcibios_api = PCIAPI_RUNTIME ( pcbios ); diff --git a/src/arch/x86/interface/pcbios/pcicloud.c b/src/arch/x86/interface/pcbios/pcicloud.c new file mode 100644 index 000000000..97d7cac1d --- /dev/null +++ b/src/arch/x86/interface/pcbios/pcicloud.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 <stdint.h> +#include <ipxe/init.h> +#include <ipxe/pci.h> +#include <ipxe/ecam.h> +#include <ipxe/pcibios.h> +#include <ipxe/pcidirect.h> +#include <ipxe/pcicloud.h> + +/** @file + * + * Cloud VM PCI configuration space access + * + */ + +/** Selected PCI configuration space access API */ +static struct pci_api *pcicloud = &ecam_api; + +/** + * Find next PCI bus:dev.fn address range in system + * + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in + */ +static void pcicloud_discover ( uint32_t busdevfn, struct pci_range *range ) { + + pcicloud->pci_discover ( busdevfn, range ); +} + +/** + * Read byte from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static int pcicloud_read_config_byte ( struct pci_device *pci, + unsigned int where, uint8_t *value ) { + + return pcicloud->pci_read_config_byte ( pci, where, value ); +} + +/** + * Read 16-bit word from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static int pcicloud_read_config_word ( struct pci_device *pci, + unsigned int where, uint16_t *value ) { + + return pcicloud->pci_read_config_word ( pci, where, value ); +} + +/** + * Read 32-bit dword from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static int pcicloud_read_config_dword ( struct pci_device *pci, + unsigned int where, uint32_t *value ) { + + return pcicloud->pci_read_config_dword ( pci, where, value ); +} + +/** + * Write byte to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static int pcicloud_write_config_byte ( struct pci_device *pci, + unsigned int where, uint8_t value ) { + + return pcicloud->pci_write_config_byte ( pci, where, value ); +} + +/** + * Write 16-bit word to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static int pcicloud_write_config_word ( struct pci_device *pci, + unsigned int where, uint16_t value ) { + + return pcicloud->pci_write_config_word ( pci, where, value ); +} + +/** + * Write 32-bit dword to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static int pcicloud_write_config_dword ( struct pci_device *pci, + unsigned int where, uint32_t value ) { + + return pcicloud->pci_write_config_dword ( pci, where, value ); +} + +/** + * Map PCI bus address as an I/O address + * + * @v bus_addr PCI bus address + * @v len Length of region + * @ret io_addr I/O address, or NULL on error + */ +static void * pcicloud_ioremap ( struct pci_device *pci, + unsigned long bus_addr, size_t len ) { + + return pcicloud->pci_ioremap ( pci, bus_addr, len ); +} + +PROVIDE_PCIAPI ( cloud, pci_discover, pcicloud_discover ); +PROVIDE_PCIAPI ( cloud, pci_read_config_byte, pcicloud_read_config_byte ); +PROVIDE_PCIAPI ( cloud, pci_read_config_word, pcicloud_read_config_word ); +PROVIDE_PCIAPI ( cloud, pci_read_config_dword, pcicloud_read_config_dword ); +PROVIDE_PCIAPI ( cloud, pci_write_config_byte, pcicloud_write_config_byte ); +PROVIDE_PCIAPI ( cloud, pci_write_config_word, pcicloud_write_config_word ); +PROVIDE_PCIAPI ( cloud, pci_write_config_dword, pcicloud_write_config_dword ); +PROVIDE_PCIAPI ( cloud, pci_ioremap, pcicloud_ioremap ); + +/** + * Initialise cloud VM PCI configuration space access + * + */ +static void pcicloud_init ( void ) { + static struct pci_api *apis[] = { + &ecam_api, &pcibios_api, &pcidirect_api + }; + struct pci_range range; + unsigned int i; + + /* Select first API that successfully discovers an address range */ + for ( i = 0 ; i < ( sizeof ( apis ) / sizeof ( apis[0] ) ) ; i++ ) { + pcicloud = apis[i]; + pcicloud_discover ( 0, &range ); + if ( range.count != 0 ) { + DBGC ( pcicloud, "PCICLOUD selected %s API\n", + pcicloud->name ); + break; + } + } + + /* The PCI direct API can never fail discovery since the range + * is hardcoded. + */ + assert ( range.count != 0 ); +} + +/** Cloud VM PCI configuration space access initialisation function */ +struct init_fn pcicloud_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = pcicloud_init, +}; diff --git a/src/config/cloud/ioapi.h b/src/config/cloud/ioapi.h index c7c917f2e..ba0896a9a 100644 --- a/src/config/cloud/ioapi.h +++ b/src/config/cloud/ioapi.h @@ -3,5 +3,5 @@ */ #ifdef PLATFORM_pcbios #undef PCIAPI_PCBIOS -#define PCIAPI_DIRECT +#define PCIAPI_CLOUD #endif diff --git a/src/core/acpimac.c b/src/core/acpimac.c index 5920480dd..e0074ba43 100644 --- a/src/core/acpimac.c +++ b/src/core/acpimac.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/base16.h> #include <ipxe/ethernet.h> #include <ipxe/if_ether.h> +#include <ipxe/settings.h> #include <ipxe/acpimac.h> /** @file @@ -249,3 +250,39 @@ int acpi_mac ( uint8_t *hw_addr ) { return -ENOENT; } + +/** + * Fetch system MAC address setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int sysmac_fetch ( void *data, size_t len ) { + uint8_t mac[ETH_ALEN]; + int rc; + + /* Try fetching ACPI MAC address */ + if ( ( rc = acpi_mac ( mac ) ) != 0 ) + return rc; + + /* Return MAC address */ + if ( len > sizeof ( mac ) ) + len = sizeof ( mac ); + memcpy ( data, mac, len ); + return ( sizeof ( mac ) ); +} + +/** System MAC address setting */ +const struct setting sysmac_setting __setting ( SETTING_MISC, sysmac ) = { + .name = "sysmac", + .description = "System MAC", + .type = &setting_type_hex, + .scope = &builtin_scope, +}; + +/** System MAC address built-in setting */ +struct builtin_setting sysmac_builtin_setting __builtin_setting = { + .setting = &sysmac_setting, + .fetch = sysmac_fetch, +}; diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c index 2fa9b0c73..c4ca46e3a 100644 --- a/src/core/cachedhcp.c +++ b/src/core/cachedhcp.c @@ -90,29 +90,62 @@ static void cachedhcp_free ( struct cached_dhcp_packet *cache ) { */ static int cachedhcp_apply ( struct cached_dhcp_packet *cache, struct net_device *netdev ) { - struct settings *settings; + struct settings *settings = NULL; + struct ll_protocol *ll_protocol; + const uint8_t *chaddr; + uint8_t *hw_addr; + uint8_t *ll_addr; + size_t ll_addr_len; int rc; /* Do nothing if cache is empty */ if ( ! cache->dhcppkt ) return 0; + chaddr = cache->dhcppkt->dhcphdr->chaddr; - /* Do nothing unless cached packet's MAC address matches this - * network device, if specified. - */ + /* Handle association with network device, if specified */ if ( netdev ) { - if ( memcmp ( netdev->ll_addr, cache->dhcppkt->dhcphdr->chaddr, - netdev->ll_protocol->ll_addr_len ) != 0 ) { + hw_addr = netdev->hw_addr; + ll_addr = netdev->ll_addr; + ll_protocol = netdev->ll_protocol; + ll_addr_len = ll_protocol->ll_addr_len; + + /* If cached packet's MAC address matches the network + * device's permanent MAC address, then assume that + * the permanent MAC address ought to be the network + * device's current link-layer address. + * + * This situation can arise when the PXE ROM does not + * understand the system-specific mechanism for + * overriding the MAC address, and so uses the + * permanent MAC address instead. We choose to match + * this behaviour in order to minimise surprise. + */ + if ( memcmp ( hw_addr, chaddr, ll_addr_len ) == 0 ) { + if ( memcmp ( hw_addr, ll_addr, ll_addr_len ) != 0 ) { + DBGC ( colour, "CACHEDHCP %s resetting %s MAC " + "%s ", cache->name, netdev->name, + ll_protocol->ntoa ( ll_addr ) ); + DBGC ( colour, "-> %s\n", + ll_protocol->ntoa ( hw_addr ) ); + } + memcpy ( ll_addr, hw_addr, ll_addr_len ); + } + + /* Do nothing unless cached packet's MAC address + * matches this network device. + */ + if ( memcmp ( ll_addr, chaddr, ll_addr_len ) != 0 ) { DBGC ( colour, "CACHEDHCP %s does not match %s\n", cache->name, netdev->name ); return 0; } DBGC ( colour, "CACHEDHCP %s is for %s\n", cache->name, netdev->name ); - } - /* Select appropriate parent settings block */ - settings = ( netdev ? netdev_settings ( netdev ) : NULL ); + /* Use network device's settings block */ + settings = netdev_settings ( netdev ); + } /* Register settings */ if ( ( rc = register_settings ( &cache->dhcppkt->settings, settings, diff --git a/src/crypto/dhe.c b/src/crypto/dhe.c new file mode 100644 index 000000000..2da107d24 --- /dev/null +++ b/src/crypto/dhe.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + * + * 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 ); + +/** @file + * + * Ephemeral Diffie-Hellman key exchange + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <ipxe/bigint.h> +#include <ipxe/dhe.h> + +/** + * Calculate Diffie-Hellman key + * + * @v modulus Prime modulus + * @v len Length of prime modulus + * @v generator Generator + * @v generator_len Length of generator + * @v partner Partner public key + * @v partner_len Length of partner public key + * @v private Private key + * @v private_len Length of private key + * @ret public Public key (length equal to prime modulus) + * @ret shared Shared secret (length equal to prime modulus) + * @ret rc Return status code + */ +int dhe_key ( const void *modulus, size_t len, const void *generator, + size_t generator_len, const void *partner, size_t partner_len, + const void *private, size_t private_len, void *public, + void *shared ) { + unsigned int size = bigint_required_size ( len ); + unsigned int private_size = bigint_required_size ( private_len ); + bigint_t ( size ) *mod; + bigint_t ( private_size ) *exp; + size_t tmp_len = bigint_mod_exp_tmp_len ( mod, exp ); + struct { + bigint_t ( size ) modulus; + bigint_t ( size ) generator; + bigint_t ( size ) partner; + bigint_t ( private_size ) private; + bigint_t ( size ) result; + uint8_t tmp[tmp_len]; + } __attribute__ (( packed )) *ctx; + int rc; + + DBGC2 ( modulus, "DHE %p modulus:\n", modulus ); + DBGC2_HDA ( modulus, 0, modulus, len ); + DBGC2 ( modulus, "DHE %p generator:\n", modulus ); + DBGC2_HDA ( modulus, 0, generator, generator_len ); + DBGC2 ( modulus, "DHE %p partner public key:\n", modulus ); + DBGC2_HDA ( modulus, 0, partner, partner_len ); + DBGC2 ( modulus, "DHE %p private key:\n", modulus ); + DBGC2_HDA ( modulus, 0, private, private_len ); + + /* Sanity checks */ + if ( generator_len > len ) { + DBGC ( modulus, "DHE %p overlength generator\n", modulus ); + rc = -EINVAL; + goto err_sanity; + } + if ( partner_len > len ) { + DBGC ( modulus, "DHE %p overlength partner public key\n", + modulus ); + rc = -EINVAL; + goto err_sanity; + } + if ( private_len > len ) { + DBGC ( modulus, "DHE %p overlength private key\n", modulus ); + rc = -EINVAL; + goto err_sanity; + } + + /* Allocate context */ + ctx = malloc ( sizeof ( *ctx ) ); + if ( ! ctx ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Initialise context */ + bigint_init ( &ctx->modulus, modulus, len ); + bigint_init ( &ctx->generator, generator, generator_len ); + bigint_init ( &ctx->partner, partner, partner_len ); + bigint_init ( &ctx->private, private, private_len ); + + /* Calculate public key */ + bigint_mod_exp ( &ctx->generator, &ctx->modulus, &ctx->private, + &ctx->result, ctx->tmp ); + bigint_done ( &ctx->result, public, len ); + DBGC2 ( modulus, "DHE %p public key:\n", modulus ); + DBGC2_HDA ( modulus, 0, public, len ); + + /* Calculate shared secret */ + bigint_mod_exp ( &ctx->partner, &ctx->modulus, &ctx->private, + &ctx->result, ctx->tmp ); + bigint_done ( &ctx->result, shared, len ); + DBGC2 ( modulus, "DHE %p shared secret:\n", modulus ); + DBGC2_HDA ( modulus, 0, shared, len ); + + /* Success */ + rc = 0; + + free ( ctx ); + err_alloc: + err_sanity: + return rc; +} diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c index f898619c8..7109bbf6a 100644 --- a/src/crypto/hmac.c +++ b/src/crypto/hmac.c @@ -47,93 +47,61 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/hmac.h> /** - * Reduce HMAC key length - * - * @v digest Digest algorithm to use - * @v digest_ctx Digest context - * @v key Key - * @v key_len Length of key - */ -static void hmac_reduce_key ( struct digest_algorithm *digest, - void *key, size_t *key_len ) { - uint8_t digest_ctx[digest->ctxsize]; - - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, key, *key_len ); - digest_final ( digest, digest_ctx, key ); - *key_len = digest->digestsize; -} - -/** * Initialise HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v key Key * @v key_len Length of key - * - * The length of the key should be less than the block size of the - * digest algorithm being used. (If the key length is greater, it - * will be replaced with its own digest, and key_len will be updated - * accordingly). */ -void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len ) { - unsigned char k_ipad[digest->blocksize]; +void hmac_init ( struct digest_algorithm *digest, void *ctx, const void *key, + size_t key_len ) { + hmac_context_t ( digest ) *hctx = ctx; unsigned int i; - /* Reduce key if necessary */ - if ( *key_len > sizeof ( k_ipad ) ) - hmac_reduce_key ( digest, key, key_len ); - /* Construct input pad */ - memset ( k_ipad, 0, sizeof ( k_ipad ) ); - memcpy ( k_ipad, key, *key_len ); - for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { - k_ipad[i] ^= 0x36; + memset ( hctx->pad, 0, sizeof ( hctx->pad ) ); + if ( key_len <= sizeof ( hctx->pad ) ) { + memcpy ( hctx->pad, key, key_len ); + } else { + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, key, key_len ); + digest_final ( digest, hctx->ctx, hctx->pad ); + } + for ( i = 0 ; i < sizeof ( hctx->pad ) ; i++ ) { + hctx->pad[i] ^= 0x36; } - + /* Start inner hash */ - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, hctx->pad, sizeof ( hctx->pad ) ); } /** * Finalise HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context - * @v key Key - * @v key_len Length of key + * @v ctx HMAC context * @v hmac HMAC digest to fill in - * - * The length of the key should be less than the block size of the - * digest algorithm being used. (If the key length is greater, it - * will be replaced with its own digest, and key_len will be updated - * accordingly). */ -void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len, void *hmac ) { - unsigned char k_opad[digest->blocksize]; +void hmac_final ( struct digest_algorithm *digest, void *ctx, void *hmac ) { + hmac_context_t ( digest ) *hctx = ctx; unsigned int i; - /* Reduce key if necessary */ - if ( *key_len > sizeof ( k_opad ) ) - hmac_reduce_key ( digest, key, key_len ); - - /* Construct output pad */ - memset ( k_opad, 0, sizeof ( k_opad ) ); - memcpy ( k_opad, key, *key_len ); - for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { - k_opad[i] ^= 0x5c; + /* Construct output pad from input pad */ + for ( i = 0 ; i < sizeof ( hctx->pad ) ; i++ ) { + hctx->pad[i] ^= 0x6a; } - + /* Finish inner hash */ - digest_final ( digest, digest_ctx, hmac ); + digest_final ( digest, hctx->ctx, hmac ); /* Perform outer hash */ - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); - digest_update ( digest, digest_ctx, hmac, digest->digestsize ); - digest_final ( digest, digest_ctx, hmac ); + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, hctx->pad, sizeof ( hctx->pad ) ); + digest_update ( digest, hctx->ctx, hmac, digest->digestsize ); + digest_final ( digest, hctx->ctx, hmac ); + + /* Erase output pad (from which the key may be derivable) */ + memset ( hctx->pad, 0, sizeof ( hctx->pad ) ); } diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c index 098297716..57bde4d1d 100644 --- a/src/crypto/hmac_drbg.c +++ b/src/crypto/hmac_drbg.c @@ -79,7 +79,7 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, struct hmac_drbg_state *state, const void *data, size_t len, const uint8_t single ) { - uint8_t context[ hash->ctxsize ]; + uint8_t context[ hmac_ctxsize ( hash ) ]; size_t out_len = hash->digestsize; DBGC ( state, "HMAC_DRBG_%s %p provided data :\n", hash->name, state ); @@ -92,13 +92,11 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, assert ( ( single == 0x00 ) || ( single == 0x01 ) ); /* K = HMAC ( K, V || single || provided_data ) */ - hmac_init ( hash, context, state->key, &out_len ); - assert ( out_len == hash->digestsize ); + hmac_init ( hash, context, state->key, out_len ); hmac_update ( hash, context, state->value, out_len ); hmac_update ( hash, context, &single, sizeof ( single ) ); hmac_update ( hash, context, data, len ); - hmac_final ( hash, context, state->key, &out_len, state->key ); - assert ( out_len == hash->digestsize ); + hmac_final ( hash, context, state->key ); DBGC ( state, "HMAC_DRBG_%s %p K = HMAC ( K, V || %#02x || " "provided_data ) :\n", hash->name, state, single ); @@ -122,7 +120,7 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, */ static void hmac_drbg_update_value ( struct digest_algorithm *hash, struct hmac_drbg_state *state ) { - uint8_t context[ hash->ctxsize ]; + uint8_t context[ hmac_ctxsize ( hash ) ]; size_t out_len = hash->digestsize; /* Sanity checks */ @@ -130,11 +128,9 @@ static void hmac_drbg_update_value ( struct digest_algorithm *hash, assert ( state != NULL ); /* V = HMAC ( K, V ) */ - hmac_init ( hash, context, state->key, &out_len ); - assert ( out_len == hash->digestsize ); + hmac_init ( hash, context, state->key, out_len ); hmac_update ( hash, context, state->value, out_len ); - hmac_final ( hash, context, state->key, &out_len, state->value ); - assert ( out_len == hash->digestsize ); + hmac_final ( hash, context, state->value ); DBGC ( state, "HMAC_DRBG_%s %p V = HMAC ( K, V ) :\n", hash->name, state ); diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 06722c0e1..04b4ce2a7 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, @@ -42,6 +43,7 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite (04) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index c609eacea..1021f76f4 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, @@ -42,6 +43,7 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite(02)={ .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c index 870af2132..fb120f8db 100644 --- a/src/crypto/ntlm.c +++ b/src/crypto/ntlm.c @@ -117,10 +117,9 @@ void ntlm_key ( const char *domain, const char *username, struct digest_algorithm *md5 = &md5_algorithm; union { uint8_t md4[MD4_CTX_SIZE]; - uint8_t md5[MD5_CTX_SIZE]; + uint8_t md5[ MD5_CTX_SIZE + MD5_BLOCK_SIZE ]; } ctx; uint8_t digest[MD4_DIGEST_SIZE]; - size_t digest_len; uint8_t c; uint16_t wc; @@ -141,8 +140,7 @@ void ntlm_key ( const char *domain, const char *username, digest_final ( md4, ctx.md4, digest ); /* Construct HMAC-MD5 of (Unicode) upper-case username */ - digest_len = sizeof ( digest ); - hmac_init ( md5, ctx.md5, digest, &digest_len ); + hmac_init ( md5, ctx.md5, digest, sizeof ( digest ) ); while ( ( c = *(username++) ) ) { wc = cpu_to_le16 ( toupper ( c ) ); hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); @@ -151,7 +149,7 @@ void ntlm_key ( const char *domain, const char *username, wc = cpu_to_le16 ( c ); hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); } - hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw ); + hmac_final ( md5, ctx.md5, key->raw ); DBGC ( key, "NTLM key:\n" ); DBGC_HDA ( key, 0, key, sizeof ( *key ) ); } @@ -170,8 +168,7 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, struct ntlm_nt_response *nt ) { struct digest_algorithm *md5 = &md5_algorithm; struct ntlm_nonce tmp_nonce; - uint8_t ctx[MD5_CTX_SIZE]; - size_t key_len = sizeof ( *key ); + uint8_t ctx[ MD5_CTX_SIZE + MD5_BLOCK_SIZE ]; unsigned int i; /* Generate random nonce, if needed */ @@ -183,10 +180,10 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, /* Construct LAN Manager response */ memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) ); - hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_init ( md5, ctx, key->raw, sizeof ( *key ) ); hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); hmac_update ( md5, ctx, &lm->nonce, sizeof ( lm->nonce ) ); - hmac_final ( md5, ctx, key->raw, &key_len, lm->digest ); + hmac_final ( md5, ctx, lm->digest ); DBGC ( key, "NTLM LAN Manager response:\n" ); DBGC_HDA ( key, 0, lm, sizeof ( *lm ) ); @@ -195,14 +192,14 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, nt->version = NTLM_VERSION_NTLMV2; nt->high = NTLM_VERSION_NTLMV2; memcpy ( &nt->nonce, nonce, sizeof ( nt->nonce ) ); - hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_init ( md5, ctx, key->raw, sizeof ( *key ) ); hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); hmac_update ( md5, ctx, &nt->version, ( sizeof ( *nt ) - offsetof ( typeof ( *nt ), version ) ) ); hmac_update ( md5, ctx, info->target, info->len ); hmac_update ( md5, ctx, &nt->zero, sizeof ( nt->zero ) ); - hmac_final ( md5, ctx, key->raw, &key_len, nt->digest ); + hmac_final ( md5, ctx, nt->digest ); DBGC ( key, "NTLM NT response prefix:\n" ); DBGC_HDA ( key, 0, nt, sizeof ( *nt ) ); } diff --git a/src/crypto/sha1extra.c b/src/crypto/sha1extra.c index cec0d35e5..9e296eb2c 100644 --- a/src/crypto/sha1extra.c +++ b/src/crypto/sha1extra.c @@ -49,7 +49,7 @@ void prf_sha1 ( const void *key, size_t key_len, const char *label, u8 in[strlen ( label ) + 1 + data_len + 1]; /* message to HMAC */ u8 *in_blknr; /* pointer to last byte of in, block number */ u8 out[SHA1_DIGEST_SIZE]; /* HMAC-SHA1 result */ - u8 sha1_ctx[SHA1_CTX_SIZE]; /* SHA1 context */ + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; /* HMAC-SHA1 context */ const size_t label_len = strlen ( label ); /* The HMAC-SHA-1 is calculated using the given key on the @@ -65,9 +65,9 @@ void prf_sha1 ( const void *key, size_t key_len, const char *label, for ( blk = 0 ;; blk++ ) { *in_blknr = blk; - hmac_init ( &sha1_algorithm, sha1_ctx, keym, &key_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, in, sizeof ( in ) ); - hmac_final ( &sha1_algorithm, sha1_ctx, keym, &key_len, out ); + hmac_init ( &sha1_algorithm, ctx, keym, key_len ); + hmac_update ( &sha1_algorithm, ctx, in, sizeof ( in ) ); + hmac_final ( &sha1_algorithm, ctx, out ); if ( prf_len <= sizeof ( out ) ) { memcpy ( prf, out, prf_len ); @@ -100,7 +100,7 @@ static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len, u8 pass[pass_len]; /* modifiable passphrase */ u8 in[salt_len + 4]; /* input buffer to first round */ u8 last[SHA1_DIGEST_SIZE]; /* output of round N, input of N+1 */ - u8 sha1_ctx[SHA1_CTX_SIZE]; + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; u8 *next_in = in; /* changed to `last' after first round */ int next_size = sizeof ( in ); int i; @@ -114,9 +114,9 @@ static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len, memset ( block, 0, sizeof ( last ) ); for ( i = 0; i < iterations; i++ ) { - hmac_init ( &sha1_algorithm, sha1_ctx, pass, &pass_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, next_in, next_size ); - hmac_final ( &sha1_algorithm, sha1_ctx, pass, &pass_len, last ); + hmac_init ( &sha1_algorithm, ctx, pass, pass_len ); + hmac_update ( &sha1_algorithm, ctx, next_in, next_size ); + hmac_final ( &sha1_algorithm, ctx, last ); for ( j = 0; j < sizeof ( last ); j++ ) { block[j] ^= last[j]; diff --git a/src/drivers/bus/ecam.c b/src/drivers/bus/ecam.c new file mode 100644 index 000000000..1d57bd2a0 --- /dev/null +++ b/src/drivers/bus/ecam.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 <errno.h> +#include <ipxe/uaccess.h> +#include <ipxe/ecam.h> + +/** @file + * + * PCI Enhanced Configuration Access Mechanism (ECAM) + * + */ + +/** Cached mapped ECAM allocation */ +static struct ecam_mapping ecam; + +/** + * Find lowest ECAM allocation not below a given PCI bus:dev.fn address + * + * @v busdevfn PCI bus:dev.fn address + * @v range PCI device address range to fill in + * @v alloc ECAM allocation to fill in, or NULL + * @ret rc Return status code + */ +static int ecam_find ( uint32_t busdevfn, struct pci_range *range, + struct ecam_allocation *alloc ) { + struct ecam_allocation tmp; + unsigned int best = 0; + unsigned int offset; + unsigned int count; + unsigned int index; + userptr_t mcfg; + uint32_t length; + uint32_t start; + + /* Return empty range on error */ + range->count = 0; + + /* Locate MCFG table */ + mcfg = acpi_table ( ECAM_SIGNATURE, 0 ); + if ( ! mcfg ) { + DBGC ( &ecam, "ECAM found no MCFG table\n" ); + return -ENOTSUP; + } + + /* Get length of table */ + copy_from_user ( &length, mcfg, + offsetof ( struct ecam_table, acpi.length ), + sizeof ( length ) ); + + /* Iterate over allocations */ + for ( offset = offsetof ( struct ecam_table, alloc ) ; + ( offset + sizeof ( tmp ) ) <= le32_to_cpu ( length ) ; + offset += sizeof ( tmp ) ) { + + /* Read allocation */ + copy_from_user ( &tmp, mcfg, offset, sizeof ( tmp ) ); + DBGC2 ( &ecam, "ECAM %04x:[%02x-%02x] has base %08llx\n", + le16_to_cpu ( tmp.segment ), tmp.start, tmp.end, + ( ( unsigned long long ) le64_to_cpu ( tmp.base ) ) ); + start = PCI_BUSDEVFN ( le16_to_cpu ( tmp.segment ), + tmp.start, 0, 0 ); + count = PCI_BUSDEVFN ( 0, ( tmp.end - tmp.start + 1 ), 0, 0 ); + + /* Check for a matching or new closest allocation */ + index = ( busdevfn - start ); + if ( ( index < count ) || ( index > best ) ) { + if ( alloc ) + memcpy ( alloc, &tmp, sizeof ( *alloc ) ); + range->start = start; + range->count = count; + best = index; + } + + /* Stop if this range contains the target bus:dev.fn address */ + if ( index < count ) + return 0; + } + + return ( best ? 0 : -ENOENT ); +} + +/** + * Find next PCI bus:dev.fn address range in system + * + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in + */ +static void ecam_discover ( uint32_t busdevfn, struct pci_range *range ) { + + /* Find new range, if any */ + ecam_find ( busdevfn, range, NULL ); +} + +/** + * Access configuration space for PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ecam_access ( struct pci_device *pci ) { + uint64_t base; + size_t len; + int rc; + + /* Reuse mapping if possible */ + if ( ( pci->busdevfn - ecam.range.start ) < ecam.range.count ) + return 0; + + /* Clear any existing mapping */ + if ( ecam.regs ) { + iounmap ( ecam.regs ); + ecam.regs = NULL; + } + + /* Find allocation for this PCI device */ + if ( ( rc = ecam_find ( pci->busdevfn, &ecam.range, + &ecam.alloc ) ) != 0 ) { + DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT ": %s\n", + PCI_ARGS ( pci ), strerror ( rc ) ); + goto err_find; + } + if ( ecam.range.start > pci->busdevfn ) { + DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT "\n", + PCI_ARGS ( pci ) ); + goto err_find; + } + + /* Map configuration space for this allocation */ + base = le64_to_cpu ( ecam.alloc.base ); + len = ( ecam.range.count * ECAM_SIZE ); + ecam.regs = ioremap ( base, len ); + if ( ! ecam.regs ) { + DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map " + "[%08llx,%08llx)\n", le16_to_cpu ( ecam.alloc.segment ), + ecam.alloc.start, ecam.alloc.end, base, ( base + len ) ); + rc = -ENODEV; + goto err_ioremap; + } + + /* Populate cached mapping */ + DBGC ( &ecam, "ECAM %04x:[%02x-%02x] mapped [%08llx,%08llx) -> %p\n", + le16_to_cpu ( ecam.alloc.segment ), ecam.alloc.start, + ecam.alloc.end, base, ( base + len ), ecam.regs ); + return 0; + + iounmap ( ecam.regs ); + err_ioremap: + err_find: + ecam.range.count = 0; + return rc; +} + +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v location Offset and length within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +int ecam_read ( struct pci_device *pci, unsigned int location, void *value ) { + unsigned int where = ECAM_WHERE ( location ); + unsigned int len = ECAM_LEN ( location ); + unsigned int index; + void *addr; + int rc; + + /* Return all-ones on error */ + memset ( value, 0xff, len ); + + /* Access configuration space */ + if ( ( rc = ecam_access ( pci ) ) != 0 ) + return rc; + + /* Read from address */ + index = ( pci->busdevfn - ecam.range.start ); + addr = ( ecam.regs + ( index * ECAM_SIZE ) + where ); + switch ( len ) { + case 4: + *( ( uint32_t *) value ) = readl ( addr ); + break; + case 2: + *( ( uint16_t *) value ) = readw ( addr ); + break; + case 1: + *( ( uint8_t *) value ) = readb ( addr ); + break; + default: + assert ( 0 ); + } + + return 0; +} + +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v location Offset and length within PCI configuration space + * @v value Value to write + * @ret rc Return status code + */ +int ecam_write ( struct pci_device *pci, unsigned int location, + unsigned long value ) { + unsigned int where = ECAM_WHERE ( location ); + unsigned int len = ECAM_LEN ( location ); + unsigned int index; + void *addr; + int rc; + + /* Access configuration space */ + if ( ( rc = ecam_access ( pci ) ) != 0 ) + return rc; + + /* Read from address */ + index = ( pci->busdevfn - ecam.range.start ); + addr = ( ecam.regs + ( index * ECAM_SIZE ) + where ); + switch ( len ) { + case 4: + writel ( value, addr ); + break; + case 2: + writew ( value, addr ); + break; + case 1: + writeb ( value, addr ); + break; + default: + assert ( 0 ); + } + + return 0; +} + +PROVIDE_PCIAPI ( ecam, pci_discover, ecam_discover ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_write_config_dword ); +PROVIDE_PCIAPI_INLINE ( ecam, pci_ioremap ); + +struct pci_api ecam_api = PCIAPI_RUNTIME ( ecam ); diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 5891e42ff..7953aaedd 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -229,46 +229,55 @@ int pci_read_config ( struct pci_device *pci ) { * * @v pci PCI device to fill in * @v busdevfn Starting bus:dev.fn address - * @ret busdevfn Bus:dev.fn address of next PCI device, or negative error + * @ret busdevfn Bus:dev.fn address of next PCI device + * @ret rc Return status code */ -int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { - static unsigned int end; - unsigned int sub_end; +int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ) { + static struct pci_range range; uint8_t hdrtype; uint8_t sub; + uint32_t end; + unsigned int count; int rc; - /* Determine number of PCI buses */ - if ( ! end ) - end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 ); - /* Find next PCI device, if any */ - for ( ; busdevfn < end ; busdevfn++ ) { + do { + /* Find next PCI bus:dev.fn address range, if necessary */ + if ( ( *busdevfn - range.start ) >= range.count ) { + pci_discover ( *busdevfn, &range ); + if ( *busdevfn < range.start ) + *busdevfn = range.start; + if ( ( *busdevfn - range.start ) >= range.count ) + break; + } /* Check for PCI device existence */ memset ( pci, 0, sizeof ( *pci ) ); - pci_init ( pci, busdevfn ); + pci_init ( pci, *busdevfn ); if ( ( rc = pci_read_config ( pci ) ) != 0 ) continue; - /* If device is a bridge, expand the number of PCI - * buses as needed. + /* If device is a bridge, expand the PCI bus:dev.fn + * address range as needed. */ pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); hdrtype &= PCI_HEADER_TYPE_MASK; if ( hdrtype == PCI_HEADER_TYPE_BRIDGE ) { pci_read_config_byte ( pci, PCI_SUBORDINATE, &sub ); - sub_end = PCI_BUSDEVFN ( 0, ( sub + 1 ), 0, 0 ); - if ( end < sub_end ) { + end = PCI_BUSDEVFN ( PCI_SEG ( *busdevfn ), + ( sub + 1 ), 0, 0 ); + count = ( end - range.start ); + if ( count > range.count ) { DBGC ( pci, PCI_FMT " found subordinate bus " "%#02x\n", PCI_ARGS ( pci ), sub ); - end = sub_end; + range.count = count; } } /* Return this device */ - return busdevfn; - } + return 0; + + } while ( ++(*busdevfn) ); return -ENODEV; } @@ -348,11 +357,10 @@ void pci_remove ( struct pci_device *pci ) { */ static int pcibus_probe ( struct root_device *rootdev ) { struct pci_device *pci = NULL; - int busdevfn = 0; + uint32_t busdevfn = 0; int rc; - for ( busdevfn = 0 ; 1 ; busdevfn++ ) { - + do { /* Allocate struct pci_device */ if ( ! pci ) pci = malloc ( sizeof ( *pci ) ); @@ -362,8 +370,7 @@ static int pcibus_probe ( struct root_device *rootdev ) { } /* Find next PCI device, if any */ - busdevfn = pci_find_next ( pci, busdevfn ); - if ( busdevfn < 0 ) + if ( ( rc = pci_find_next ( pci, &busdevfn ) ) != 0 ) break; /* Look for a driver */ @@ -386,7 +393,8 @@ static int pcibus_probe ( struct root_device *rootdev ) { /* Not registered; re-use struct pci_device */ list_del ( &pci->dev.siblings ); } - } + + } while ( ++busdevfn ); free ( pci ); return 0; diff --git a/src/drivers/bus/pcibridge.c b/src/drivers/bus/pcibridge.c new file mode 100644 index 000000000..d2763faf9 --- /dev/null +++ b/src/drivers/bus/pcibridge.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 <stddef.h> +#include <stdint.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/pci.h> +#include <ipxe/pcibridge.h> + +/** @file + * + * PCI-to-PCI bridge + * + */ + +/** List of all PCI bridges */ +static LIST_HEAD ( pcibridges ); + +/** + * Find bridge attached to a PCI device + * + * @v pci PCI device + * @ret bridge PCI bridge, or NULL + */ +struct pci_bridge * pcibridge_find ( struct pci_device *pci ) { + unsigned int bus = PCI_BUS ( pci->busdevfn ); + struct pci_bridge *bridge; + + /* Find matching bridge */ + list_for_each_entry ( bridge, &pcibridges, list ) { + if ( bus == bridge->secondary ) + return bridge; + } + + return NULL; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int pcibridge_probe ( struct pci_device *pci ) { + struct pci_bridge *bridge; + uint16_t base; + uint16_t limit; + int rc; + + /* Allocate and initialise structure */ + bridge = zalloc ( sizeof ( *bridge ) ); + if ( ! bridge ) { + rc = -ENOMEM; + goto err_alloc; + } + bridge->pci = pci; + + /* Read configuration */ + pci_read_config_dword ( pci, PCI_PRIMARY, &bridge->buses ); + cpu_to_le32s ( &buses ); + pci_read_config_word ( pci, PCI_MEM_BASE, &base ); + bridge->membase = ( ( base & ~PCI_MEM_MASK ) << 16 ); + pci_read_config_word ( pci, PCI_MEM_LIMIT, &limit ); + bridge->memlimit = ( ( ( ( limit | PCI_MEM_MASK ) + 1 ) << 16 ) - 1 ); + DBGC ( bridge, "BRIDGE " PCI_FMT " bus %02x to [%02x,%02x) mem " + "[%08x,%08x)\n", PCI_ARGS ( pci ), bridge->primary, + bridge->secondary, bridge->subordinate, bridge->membase, + bridge->memlimit ); + + /* Add to list of PCI bridges */ + list_add ( &bridge->list, &pcibridges ); + + pci_set_drvdata ( pci, bridge ); + return 0; + + free ( bridge ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void pcibridge_remove ( struct pci_device *pci ) { + struct pci_bridge *bridge = pci_get_drvdata ( pci ); + + /* Remove from list of bridges */ + list_del ( &bridge->list ); + + /* Free device */ + free ( bridge ); +} + +/** Bridge PCI device IDs */ +static struct pci_device_id pcibridge_ids[] = { + PCI_ROM ( 0xffff, 0xffff, "bridge", "Bridge", 0 ), +}; + +/** Bridge PCI driver */ +struct pci_driver pcibridge_driver __pci_driver = { + .ids = pcibridge_ids, + .id_count = ( sizeof ( pcibridge_ids ) / sizeof ( pcibridge_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_PCI, + PCI_ANY_ID ), + .probe = pcibridge_probe, + .remove = pcibridge_remove, +}; diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 3082d8a3d..23617bc9a 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -1,6 +1,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> +#include <ipxe/timer.h> #include <ipxe/pci.h> static int pci_find_capability_common ( struct pci_device *pci, @@ -112,3 +113,24 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { size = size & ~( size - 1 ); return size; } + +/** + * Perform PCI Express function-level reset (FLR) + * + * @v pci PCI device + * @v exp PCI Express Capability address + */ +void pci_reset ( struct pci_device *pci, unsigned int exp ) { + uint16_t control; + + /* Perform a PCIe function-level reset */ + pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control ); + control |= PCI_EXP_DEVCTL_FLR; + pci_write_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), control ); + + /* Allow time for reset to complete */ + mdelay ( PCI_EXP_FLR_DELAY_MS ); + + /* Re-enable device */ + adjust_pci_device ( pci ); +} diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 826b3b16d..68ac962ab 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -84,24 +84,18 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, * * @v func USB function * @v desc Ethernet functional descriptor - * @v hw_addr Hardware address to fill in + * @v netdev Network device * @ret rc Return status code */ int ecm_fetch_mac ( struct usb_function *func, - struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) { + struct ecm_ethernet_descriptor *desc, + struct net_device *netdev ) { struct usb_device *usb = func->usb; char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + uint8_t amac[ETH_ALEN]; int len; int rc; - /* Use system-specific MAC address, if present and not already used */ - if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, hw_addr ) ) { - DBGC ( usb, "USB %s using system-specific MAC %s\n", - func->name, eth_ntoa ( hw_addr ) ); - return 0; - } - /* Fetch MAC address string */ len = usb_get_string_descriptor ( usb, desc->mac, 0, buf, sizeof ( buf ) ); @@ -118,7 +112,7 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Decode MAC address */ - len = base16_decode ( buf, hw_addr, ETH_ALEN ); + len = base16_decode ( buf, netdev->hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n", @@ -126,6 +120,16 @@ int ecm_fetch_mac ( struct usb_function *func, return rc; } + /* Apply system-specific MAC address as current link-layer + * address, if present and not already used. + */ + if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && + ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + memcpy ( netdev->ll_addr, amac, ETH_ALEN ); + DBGC ( usb, "USB %s using system-specific MAC %s\n", + func->name, eth_ntoa ( netdev->ll_addr ) ); + } + return 0; } @@ -474,7 +478,7 @@ static int ecm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n", ecm, strerror ( rc ) ); goto err_fetch_mac; diff --git a/src/drivers/net/ecm.h b/src/drivers/net/ecm.h index 0ad3ddb95..a7d03cf94 100644 --- a/src/drivers/net/ecm.h +++ b/src/drivers/net/ecm.h @@ -88,6 +88,6 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface ); extern int ecm_fetch_mac ( struct usb_function *func, struct ecm_ethernet_descriptor *desc, - uint8_t *hw_addr ); + struct net_device *netdev ); #endif /* _ECM_H */ diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 85da1c090..22e7e1e30 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> +#include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> @@ -34,6 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/iobuf.h> #include <ipxe/malloc.h> #include <ipxe/pci.h> +#include <ipxe/pcibridge.h> +#include <ipxe/version.h> #include "ena.h" /** @file @@ -359,6 +362,7 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, struct ena_cq *cq ) { union ena_aq_req *req; union ena_acq_rsp *rsp; + unsigned int i; int rc; /* Allocate submission queue entries */ @@ -391,11 +395,20 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, sq->prod = 0; sq->phase = ENA_SQE_PHASE; - DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) db +%04x CQ%d\n", + /* Calculate fill level */ + sq->fill = sq->max; + if ( sq->fill > cq->actual ) + sq->fill = cq->actual; + + /* Initialise buffer ID ring */ + for ( i = 0 ; i < sq->count ; i++ ) + sq->ids[i] = i; + + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) fill %d db +%04x CQ%d\n", ena, ena_direction ( sq->direction ), sq->id, virt_to_phys ( sq->sqe.raw ), ( virt_to_phys ( sq->sqe.raw ) + sq->len ), - sq->doorbell, cq->id ); + sq->fill, sq->doorbell, cq->id ); return 0; err_admin: @@ -459,6 +472,7 @@ static int ena_create_cq ( struct ena_nic *ena, struct ena_cq *cq ) { req->header.opcode = ENA_CREATE_CQ; req->create_cq.size = cq->size; req->create_cq.count = cpu_to_le16 ( cq->requested ); + req->create_cq.vector = cpu_to_le32 ( ENA_MSIX_NONE ); req->create_cq.address = cpu_to_le64 ( virt_to_bus ( cq->cqe.raw ) ); /* Issue request */ @@ -597,6 +611,32 @@ static int ena_get_device_attributes ( struct net_device *netdev ) { } /** + * Set host attributes + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_set_host_attributes ( struct ena_nic *ena ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_SET_FEATURE; + req->set_feature.id = ENA_HOST_ATTRIBUTES; + feature = &req->set_feature.feature; + feature->host.info = cpu_to_le64 ( virt_to_bus ( ena->info ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + return 0; +} + +/** * Get statistics (for debugging) * * @v ena ENA device @@ -652,13 +692,14 @@ static void ena_refill_rx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct io_buffer *iobuf; struct ena_rx_sqe *sqe; - unsigned int index; physaddr_t address; size_t len = netdev->max_pkt_len; unsigned int refilled = 0; + unsigned int index; + unsigned int id; /* Refill queue */ - while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ENA_RX_COUNT ) { + while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ena->rx.sq.fill ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( len ); @@ -667,14 +708,15 @@ static void ena_refill_rx ( struct net_device *netdev ) { break; } - /* Get next submission queue entry */ + /* Get next submission queue entry and buffer ID */ index = ( ena->rx.sq.prod % ENA_RX_COUNT ); sqe = &ena->rx.sq.sqe.rx[index]; + id = ena->rx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); sqe->len = cpu_to_le16 ( len ); - sqe->id = cpu_to_le16 ( ena->rx.sq.prod ); + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -686,10 +728,10 @@ static void ena_refill_rx ( struct net_device *netdev ) { ena->rx.sq.phase ^= ENA_SQE_PHASE; /* Record I/O buffer */ - assert ( ena->rx_iobuf[index] == NULL ); - ena->rx_iobuf[index] = iobuf; + assert ( ena->rx_iobuf[id] == NULL ); + ena->rx_iobuf[id] = iobuf; - DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); refilled++; @@ -778,23 +820,25 @@ static void ena_close ( struct net_device *netdev ) { static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct ena_nic *ena = netdev->priv; struct ena_tx_sqe *sqe; - unsigned int index; physaddr_t address; + unsigned int index; + unsigned int id; size_t len; /* Get next submission queue entry */ - if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ENA_TX_COUNT ) { + if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ena->tx.sq.fill ) { DBGC ( ena, "ENA %p out of transmit descriptors\n", ena ); return -ENOBUFS; } index = ( ena->tx.sq.prod % ENA_TX_COUNT ); sqe = &ena->tx.sq.sqe.tx[index]; + id = ena->tx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); len = iob_len ( iobuf ); sqe->len = cpu_to_le16 ( len ); - sqe->id = ena->tx.sq.prod; + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -806,10 +850,14 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { if ( ( ena->tx.sq.prod % ENA_TX_COUNT ) == 0 ) ena->tx.sq.phase ^= ENA_SQE_PHASE; + /* Record I/O buffer */ + assert ( ena->tx_iobuf[id] == NULL ); + ena->tx_iobuf[id] = iobuf; + /* Ring doorbell */ writel ( ena->tx.sq.prod, ( ena->regs + ena->tx.sq.doorbell ) ); - DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); return 0; @@ -823,7 +871,9 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { static void ena_poll_tx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct ena_tx_cqe *cqe; + struct io_buffer *iobuf; unsigned int index; + unsigned int id; /* Check for completed packets */ while ( ena->tx.cq.cons != ena->tx.sq.prod ) { @@ -835,16 +885,24 @@ static void ena_poll_tx ( struct net_device *netdev ) { /* Stop if completion queue entry is empty */ if ( ( cqe->flags ^ ena->tx.cq.phase ) & ENA_CQE_PHASE ) return; - DBGC2 ( ena, "ENA %p TX %d complete\n", ena, - ( le16_to_cpu ( cqe->id ) >> 2 /* Don't ask */ ) ); /* Increment consumer counter */ ena->tx.cq.cons++; if ( ! ( ena->tx.cq.cons & ena->tx.cq.mask ) ) ena->tx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = ENA_TX_CQE_ID ( le16_to_cpu ( cqe->id ) ); + ena->tx_ids[index] = id; + + /* Identify I/O buffer */ + iobuf = ena->tx_iobuf[id]; + assert ( iobuf != NULL ); + ena->tx_iobuf[id] = NULL; + /* Complete transmit */ - netdev_tx_complete_next ( netdev ); + DBGC2 ( ena, "ENA %p TX %d complete\n", ena, id ); + netdev_tx_complete ( netdev, iobuf ); } } @@ -858,13 +916,14 @@ static void ena_poll_rx ( struct net_device *netdev ) { struct ena_rx_cqe *cqe; struct io_buffer *iobuf; unsigned int index; + unsigned int id; size_t len; /* Check for received packets */ while ( ena->rx.cq.cons != ena->rx.sq.prod ) { /* Get next completion queue entry */ - index = ( ena->rx.cq.cons % ENA_RX_COUNT ); + index = ( ena->rx.cq.cons & ena->rx.cq.mask ); cqe = &ena->rx.cq.cqe.rx[index]; /* Stop if completion queue entry is empty */ @@ -876,15 +935,20 @@ static void ena_poll_rx ( struct net_device *netdev ) { if ( ! ( ena->rx.cq.cons & ena->rx.cq.mask ) ) ena->rx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = le16_to_cpu ( cqe->id ); + ena->rx_ids[index] = id; + /* Populate I/O buffer */ - iobuf = ena->rx_iobuf[index]; - ena->rx_iobuf[index] = NULL; + iobuf = ena->rx_iobuf[id]; + assert ( iobuf != NULL ); + ena->rx_iobuf[id] = NULL; len = le16_to_cpu ( cqe->len ); iob_put ( iobuf, len ); /* Hand off to network stack */ DBGC2 ( ena, "ENA %p RX %d complete (length %zd)\n", - ena, le16_to_cpu ( cqe->id ), len ); + ena, id, len ); netdev_rx ( netdev, iobuf ); } } @@ -922,6 +986,45 @@ static struct net_device_operations ena_operations = { */ /** + * Assign memory BAR + * + * @v ena ENA device + * @v pci PCI device + * @ret rc Return status code + * + * Some BIOSes in AWS EC2 are observed to fail to assign a base + * address to the ENA device. The device is the only device behind + * its bridge, and the BIOS does assign a memory window to the bridge. + * We therefore place the device at the start of the memory window. + */ +static int ena_membase ( struct ena_nic *ena, struct pci_device *pci ) { + struct pci_bridge *bridge; + + /* Locate PCI bridge */ + bridge = pcibridge_find ( pci ); + if ( ! bridge ) { + DBGC ( ena, "ENA %p found no PCI bridge\n", ena ); + return -ENOTCONN; + } + + /* Sanity check */ + if ( PCI_SLOT ( pci->busdevfn ) || PCI_FUNC ( pci->busdevfn ) ) { + DBGC ( ena, "ENA %p at " PCI_FMT " may not be only device " + "on bus\n", ena, PCI_ARGS ( pci ) ); + return -ENOTSUP; + } + + /* Place device at start of memory window */ + pci_write_config_dword ( pci, PCI_BASE_ADDRESS_0, bridge->membase ); + pci->membase = bridge->membase; + DBGC ( ena, "ENA %p at " PCI_FMT " claiming bridge " PCI_FMT " mem " + "%08x\n", ena, PCI_ARGS ( pci ), PCI_ARGS ( bridge->pci ), + bridge->membase ); + + return 0; +} + +/** * Probe PCI device * * @v pci PCI device @@ -930,6 +1033,7 @@ static struct net_device_operations ena_operations = { static int ena_probe ( struct pci_device *pci ) { struct net_device *netdev; struct ena_nic *ena; + struct ena_host_info *info; int rc; /* Allocate and initialise net device */ @@ -946,16 +1050,20 @@ static int ena_probe ( struct pci_device *pci ) { ena->acq.phase = ENA_ACQ_PHASE; ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, sizeof ( ena->tx.cq.cqe.tx[0] ) ); - ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, - sizeof ( ena->tx.sq.sqe.tx[0] ) ); + ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, ENA_TX_COUNT, + sizeof ( ena->tx.sq.sqe.tx[0] ), ena->tx_ids ); ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, sizeof ( ena->rx.cq.cqe.rx[0] ) ); - ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, - sizeof ( ena->rx.sq.sqe.rx[0] ) ); + ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, ENA_RX_FILL, + sizeof ( ena->rx.sq.sqe.rx[0] ), ena->rx_ids ); /* Fix up PCI device */ adjust_pci_device ( pci ); + /* Fix up PCI BAR if left unassigned by BIOS */ + if ( ( ! pci->membase ) && ( ( rc = ena_membase ( ena, pci ) ) != 0 ) ) + goto err_membase; + /* Map registers */ ena->regs = pci_ioremap ( pci, pci->membase, ENA_BAR_SIZE ); if ( ! ena->regs ) { @@ -963,6 +1071,25 @@ static int ena_probe ( struct pci_device *pci ) { goto err_ioremap; } + /* Allocate and initialise host info */ + info = malloc_phys ( PAGE_SIZE, PAGE_SIZE ); + if ( ! info ) { + rc = -ENOMEM; + goto err_info; + } + ena->info = info; + memset ( info, 0, PAGE_SIZE ); + info->type = cpu_to_le32 ( ENA_HOST_INFO_TYPE_LINUX ); + snprintf ( info->dist_str, sizeof ( info->dist_str ), "%s", + ( product_name[0] ? product_name : product_short_name ) ); + snprintf ( info->kernel_str, sizeof ( info->kernel_str ), "%s", + product_version ); + info->version = cpu_to_le32 ( ENA_HOST_INFO_VERSION_WTF ); + info->spec = cpu_to_le16 ( ENA_HOST_INFO_SPEC_2_0 ); + info->busdevfn = cpu_to_le16 ( pci->busdevfn ); + DBGC2 ( ena, "ENA %p host info:\n", ena ); + DBGC2_HDA ( ena, virt_to_phys ( info ), info, sizeof ( *info ) ); + /* Reset the NIC */ if ( ( rc = ena_reset ( ena ) ) != 0 ) goto err_reset; @@ -971,6 +1098,10 @@ static int ena_probe ( struct pci_device *pci ) { if ( ( rc = ena_create_admin ( ena ) ) != 0 ) goto err_create_admin; + /* Set host attributes */ + if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 ) + goto err_set_host_attributes; + /* Fetch MAC address */ if ( ( rc = ena_get_device_attributes ( netdev ) ) != 0 ) goto err_get_device_attributes; @@ -989,12 +1120,16 @@ static int ena_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: err_get_device_attributes: + err_set_host_attributes: ena_destroy_admin ( ena ); err_create_admin: ena_reset ( ena ); err_reset: + free_phys ( ena->info, PAGE_SIZE ); + err_info: iounmap ( ena->regs ); err_ioremap: + err_membase: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: @@ -1019,6 +1154,9 @@ static void ena_remove ( struct pci_device *pci ) { /* Reset card */ ena_reset ( ena ); + /* Free host info */ + free_phys ( ena->info, PAGE_SIZE ); + /* Free network device */ iounmap ( ena->regs ); netdev_nullify ( netdev ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 676c5b878..4e1896e86 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -28,7 +28,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENA_TX_COUNT 16 /** Number of receive queue entries */ -#define ENA_RX_COUNT 16 +#define ENA_RX_COUNT 128 + +/** Receive queue maximum fill level */ +#define ENA_RX_FILL 16 /** Base address low register offset */ #define ENA_BASE_LO 0x0 @@ -127,10 +130,86 @@ struct ena_device_attributes { uint32_t mtu; } __attribute__ (( packed )); +/** Host attributes */ +#define ENA_HOST_ATTRIBUTES 28 + +/** Host attributes */ +struct ena_host_attributes { + /** Host info base address */ + uint64_t info; + /** Debug area base address */ + uint64_t debug; + /** Debug area size */ + uint32_t debug_len; +} __attribute__ (( packed )); + +/** Host information */ +struct ena_host_info { + /** Operating system type */ + uint32_t type; + /** Operating system distribution (string) */ + char dist_str[128]; + /** Operating system distribution (numeric) */ + uint32_t dist; + /** Kernel version (string) */ + char kernel_str[32]; + /** Kernel version (numeric) */ + uint32_t kernel; + /** Driver version */ + uint32_t version; + /** Linux network device features */ + uint64_t linux_features; + /** ENA specification version */ + uint16_t spec; + /** PCI bus:dev.fn address */ + uint16_t busdevfn; + /** Number of CPUs */ + uint16_t cpus; + /** Reserved */ + uint8_t reserved_a[2]; + /** Supported features */ + uint32_t features; +} __attribute__ (( packed )); + +/** Linux operating system type + * + * There is a defined "iPXE" operating system type (with value 5). + * However, some very broken versions of the ENA firmware will refuse + * to allow a completion queue to be created if the "iPXE" type is + * used. + */ +#define ENA_HOST_INFO_TYPE_LINUX 1 + +/** Driver version + * + * The driver version field is nominally used to report a version + * number outside of the VM for consumption by humans (and potentially + * by automated monitoring tools that could e.g. check for outdated + * versions with known security flaws). + * + * However, at some point in the development of the ENA firmware, some + * unknown person at AWS thought it would be sensible to apply a + * machine interpretation to this field and adjust the behaviour of + * the firmware based on its value, thereby creating a maintenance and + * debugging nightmare for all existing and future drivers. + * + * Hint to engineers: if you ever find yourself writing code of the + * form "if (version == SOME_MAGIC_NUMBER)" then something has gone + * very, very wrong. This *always* indicates that something is + * broken, either in your own code or in the code with which you are + * forced to interact. + */ +#define ENA_HOST_INFO_VERSION_WTF 0x00000002UL + +/** ENA specification version */ +#define ENA_HOST_INFO_SPEC_2_0 0x0200 + /** Feature */ union ena_feature { /** Device attributes */ struct ena_device_attributes device; + /** Host attributes */ + struct ena_host_attributes host; }; /** Submission queue direction */ @@ -230,6 +309,14 @@ struct ena_create_cq_req { uint64_t address; } __attribute__ (( packed )); +/** Empty MSI-X vector + * + * Some versions of the ENA firmware will complain if the completion + * queue's MSI-X vector field is left empty, even though the queue + * configuration specifies that interrupts are not used. + */ +#define ENA_MSIX_NONE 0xffffffffUL + /** Create completion queue response */ struct ena_create_cq_rsp { /** Header */ @@ -292,6 +379,27 @@ struct ena_get_feature_rsp { union ena_feature feature; } __attribute__ (( packed )); +/** Set feature */ +#define ENA_SET_FEATURE 9 + +/** Set feature request */ +struct ena_set_feature_req { + /** Header */ + struct ena_aq_header header; + /** Length */ + uint32_t len; + /** Address */ + uint64_t address; + /** Flags */ + uint8_t flags; + /** Feature identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved[2]; + /** Feature */ + union ena_feature feature; +} __attribute__ (( packed )); + /** Get statistics */ #define ENA_GET_STATS 11 @@ -352,6 +460,8 @@ union ena_aq_req { struct ena_destroy_cq_req destroy_cq; /** Get feature */ struct ena_get_feature_req get_feature; + /** Set feature */ + struct ena_set_feature_req set_feature; /** Get statistics */ struct ena_get_stats_req get_stats; /** Padding */ @@ -454,6 +564,9 @@ struct ena_tx_cqe { uint16_t cons; } __attribute__ (( packed )); +/** Transmit completion request identifier */ +#define ENA_TX_CQE_ID(id) ( (id) >> 2 ) + /** Receive completion queue entry */ struct ena_rx_cqe { /** Reserved */ @@ -482,6 +595,8 @@ struct ena_sq { /** Raw data */ void *raw; } sqe; + /** Buffer IDs */ + uint8_t *ids; /** Doorbell register offset */ unsigned int doorbell; /** Total length of entries */ @@ -496,6 +611,10 @@ struct ena_sq { uint8_t direction; /** Number of entries */ uint8_t count; + /** Maximum fill level */ + uint8_t max; + /** Fill level (limited to completion queue size) */ + uint8_t fill; }; /** @@ -504,15 +623,19 @@ struct ena_sq { * @v sq Submission queue * @v direction Direction * @v count Number of entries + * @v max Maximum fill level * @v size Size of each entry + * @v ids Buffer IDs */ static inline __attribute__ (( always_inline )) void ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, - size_t size ) { + unsigned int max, size_t size, uint8_t *ids ) { sq->len = ( count * size ); sq->direction = direction; sq->count = count; + sq->max = max; + sq->ids = ids; } /** Completion queue */ @@ -573,6 +696,8 @@ struct ena_qp { struct ena_nic { /** Registers */ void *regs; + /** Host info */ + struct ena_host_info *info; /** Admin queue */ struct ena_aq aq; /** Admin completion queue */ @@ -581,7 +706,13 @@ struct ena_nic { struct ena_qp tx; /** Receive queue */ struct ena_qp rx; - /** Receive I/O buffers */ + /** Transmit buffer IDs */ + uint8_t tx_ids[ENA_TX_COUNT]; + /** Transmit I/O buffers, indexed by buffer ID */ + struct io_buffer *tx_iobuf[ENA_TX_COUNT]; + /** Receive buffer IDs */ + uint8_t rx_ids[ENA_RX_COUNT]; + /** Receive I/O buffers, indexed by buffer ID */ struct io_buffer *rx_iobuf[ENA_RX_COUNT]; }; diff --git a/src/drivers/net/ice.c b/src/drivers/net/ice.c new file mode 100644 index 000000000..b5d66f1bb --- /dev/null +++ b/src/drivers/net/ice.c @@ -0,0 +1,986 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/iobuf.h> +#include <ipxe/pci.h> +#include "ice.h" + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +/** + * Magic MAC address + * + * Used as the source address and promiscuous unicast destination + * address in the "add switch rules" command. + */ +static uint8_t ice_magic_mac[ETH_HLEN] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Get firmware version + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_version ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_version_params *version; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + version = &cmd->params.version; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + api = version->api.major; + DBGC ( intelxl, "ICE %p firmware v%d/%d.%d.%d API v%d/%d.%d.%d\n", + intelxl, version->firmware.branch, version->firmware.major, + version->firmware.minor, version->firmware.patch, + version->api.branch, version->api.major, version->api.minor, + version->api.patch ); + + /* Check for API compatibility */ + if ( api > INTELXL_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "ICE %p unsupported API v%d\n", intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_read_params *read; + struct ice_admin_mac_read_address *mac; + union ice_admin_buffer *buf; + unsigned int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "ICE %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Identify MAC address */ + for ( i = 0 ; i < read->count ; i++ ) { + + /* Check for a LAN MAC address */ + mac = &buf->mac_read.mac[i]; + if ( mac->type != ICE_ADMIN_MAC_READ_TYPE_LAN ) + continue; + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac->mac ) ) { + DBGC ( intelxl, "ICE %p has invalid MAC address " + "(%s)\n", intelxl, eth_ntoa ( mac->mac ) ); + return -EINVAL; + } + + /* Copy MAC address */ + DBGC ( intelxl, "ICE %p has MAC address %s\n", + intelxl, eth_ntoa ( mac->mac ) ); + memcpy ( netdev->hw_addr, mac->mac, ETH_ALEN ); + + return 0; + } + + /* Missing LAN MAC address */ + DBGC ( intelxl, "ICE %p has no LAN MAC address\n", + intelxl ); + return -ENOENT; +} + +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_write_params *write; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( write->mac, netdev->ll_addr, ETH_ALEN ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get switch configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_switch ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_switch_params *sw; + union ice_admin_buffer *buf; + uint16_t next = 0; + uint16_t seid; + uint16_t type; + int rc; + + /* Get each configuration in turn */ + do { + /* Populate descriptor */ + cmd = ice_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; + sw->next = next; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + seid = le16_to_cpu ( buf->sw.cfg[0].seid ); + + /* Dump raw configuration */ + DBGC2 ( intelxl, "ICE %p SEID %#04x:\n", intelxl, seid ); + DBGC2_HDA ( intelxl, 0, &buf->sw.cfg[0], + sizeof ( buf->sw.cfg[0] ) ); + + /* Parse response */ + type = ( seid & ICE_ADMIN_SWITCH_TYPE_MASK ); + if ( type == ICE_ADMIN_SWITCH_TYPE_VSI ) { + intelxl->vsi = ( seid & ~ICE_ADMIN_SWITCH_TYPE_MASK ); + DBGC ( intelxl, "ICE %p VSI %#04x uplink %#04x func " + "%#04x\n", intelxl, intelxl->vsi, + le16_to_cpu ( buf->sw.cfg[0].uplink ), + le16_to_cpu ( buf->sw.cfg[0].func ) ); + } + + } while ( ( next = sw->next ) ); + + /* Check that we found a VSI */ + if ( ! intelxl->vsi ) { + DBGC ( intelxl, "ICE %p has no VSI\n", intelxl ); + return -ENOENT; + } + + return 0; +} + +/** + * Add switch rules + * + * @v intelxl Intel device + * @v mac MAC address + * @ret rc Return status code + */ +static int ice_admin_rules ( struct intelxl_nic *intelxl, uint8_t *mac ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_rules_params *rules; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_RULES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF | INTELXL_ADMIN_FL_RD ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rules ) ); + rules = &cmd->params.rules; + rules->count = cpu_to_le16 ( 1 ); + buf = ice_admin_command_buffer ( intelxl ); + buf->rules.recipe = cpu_to_le16 ( ICE_ADMIN_RULES_RECIPE_PROMISC ); + buf->rules.port = cpu_to_le16 ( intelxl->port ); + buf->rules.action = + cpu_to_le32 ( ICE_ADMIN_RULES_ACTION_VALID | + ICE_ADMIN_RULES_ACTION_VSI ( intelxl->vsi ) ); + buf->rules.len = cpu_to_le16 ( sizeof ( buf->rules.hdr ) ); + memcpy ( buf->rules.hdr.eth.h_dest, mac, ETH_ALEN ); + memcpy ( buf->rules.hdr.eth.h_source, ice_magic_mac, ETH_ALEN ); + buf->rules.hdr.eth.h_protocol = htons ( ETH_P_8021Q ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Check if scheduler node is a parent (i.e. non-leaf) node + * + * @v branch Scheduler topology branch + * @v node Scheduler topology node + * @ret child Any child node, or NULL if not found + */ +static struct ice_admin_schedule_node * +ice_admin_schedule_is_parent ( struct ice_admin_schedule_branch *branch, + struct ice_admin_schedule_node *node ) { + unsigned int count = le16_to_cpu ( branch->count ); + struct ice_admin_schedule_node *child; + unsigned int i; + + /* Find a child element, if any */ + for ( i = 0 ; i < count ; i++ ) { + child = &branch->node[i]; + if ( child->parent == node->teid ) + return child; + } + + return NULL; +} + +/** + * Query default scheduling tree topology + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_schedule ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_schedule_params *sched; + struct ice_admin_schedule_branch *branch; + struct ice_admin_schedule_node *node; + union ice_admin_buffer *buf; + int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_SCHEDULE ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sched ) ); + sched = &cmd->params.sched; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Sanity checks */ + if ( ! sched->branches ) { + DBGC ( intelxl, "ICE %p topology has no branches\n", intelxl ); + return -EINVAL; + } + branch = buf->sched.branch; + + /* Identify leaf node */ + for ( i = ( le16_to_cpu ( branch->count ) - 1 ) ; i >= 0 ; i-- ) { + node = &branch->node[i]; + if ( ! ice_admin_schedule_is_parent ( branch, node ) ) { + intelxl->teid = le32_to_cpu ( node->teid ); + DBGC2 ( intelxl, "ICE %p TEID %#08x type %d\n", + intelxl, intelxl->teid, node->config.type ); + break; + } + } + if ( ! intelxl->teid ) { + DBGC ( intelxl, "ICE %p found no leaf TEID\n", intelxl ); + return -EINVAL; + } + + return 0; +} + +/** + * Restart autonegotiation + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_autoneg ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_autoneg_params *autoneg; + int rc; + + /* Populate descriptor */ + cmd = ice_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 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get link status + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_link ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_link_params *link; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->link ) ); + link = &cmd->params.link; + link->notify = INTELXL_ADMIN_LINK_NOTIFY; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p speed %#02x status %#02x\n", + intelxl, le16_to_cpu ( buf->link.speed ), buf->link.status ); + + /* Update network device */ + if ( buf->link.status & INTELXL_ADMIN_LINK_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} + +/** + * Handle admin event + * + * @v netdev Network device + * @v xlevt Event descriptor + * @v xlbuf Data buffer + */ +static void ice_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *evt = + container_of ( xlevt, struct ice_admin_descriptor, xl ); + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + ice_admin_link ( netdev ); +} + +/** + * Add transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_add_txq ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_add_txq_params *add_txq; + union ice_admin_buffer *buf; + struct ice_context_tx *ctx; + struct ice_schedule_tx *sched; + physaddr_t address; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->add_txq ) ); + add_txq = &cmd->params.add_txq; + add_txq->count = 1; + buf = ice_admin_command_buffer ( intelxl ); + buf->add_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->add_txq.count = 1; + ctx = &buf->add_txq.ctx; + address = dma ( &ring->map, ring->desc.raw ); + ctx->base_port = + cpu_to_le64 ( ICE_TXQ_BASE_PORT ( address, intelxl->port ) ); + ctx->pf_type = cpu_to_le16 ( ICE_TXQ_PF_TYPE ( intelxl->pf ) ); + ctx->vsi = cpu_to_le16 ( intelxl->vsi ); + ctx->len = cpu_to_le16 ( ICE_TXQ_LEN ( INTELXL_TX_NUM_DESC ) ); + ctx->flags = cpu_to_le16 ( ICE_TXQ_FL_TSO | ICE_TXQ_FL_LEGACY ); + sched = &buf->add_txq.sched; + sched->sections = ( ICE_SCHEDULE_GENERIC | ICE_SCHEDULE_COMMIT | + ICE_SCHEDULE_EXCESS ); + sched->commit_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + sched->excess_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p added TEID %#04x\n", + intelxl, le32_to_cpu ( buf->add_txq.teid ) ); + + return 0; +} + +/** + * Disable transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_disable_txq ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_disable_txq_params *disable_txq; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_DISABLE_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->disable_txq ) ); + disable_txq = &cmd->params.disable_txq; + disable_txq->flags = ICE_TXQ_FL_FLUSH; + disable_txq->count = 1; + disable_txq->timeout = ICE_TXQ_TIMEOUT; + buf = ice_admin_command_buffer ( intelxl ); + buf->disable_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->disable_txq.count = 1; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Dump transmit queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_tx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct ice_context_tx ) / sizeof ( uint32_t ) ]; + uint32_t stat; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Trigger reading of transmit context */ + writel ( ( ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ | + ICE_GLCOMM_QTX_CNTX_CTL_EXEC ), + intelxl->regs + ICE_GLCOMM_QTX_CNTX_CTL ); + + /* Wait for operation to complete */ + for ( i = 0 ; i < INTELXL_CTX_MAX_WAIT_MS ; i++ ) { + + /* Check if operation is complete */ + stat = readl ( intelxl->regs + ICE_GLCOMM_QTX_CNTX_STAT ); + if ( ! ( stat & ICE_GLCOMM_QTX_CNTX_BUSY ) ) + break; + + /* Delay */ + mdelay ( 1 ); + } + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_GLCOMM_QTX_CNTX_DATA ( i ))); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p TX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Dump receive queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_rx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_QRX_CONTEXT ( i ) ) ); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p RX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Create transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_create_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Allocate descriptor ring */ + if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 ) + goto err_alloc; + + /* Add transmit queue */ + if ( ( rc = ice_admin_add_txq ( intelxl, ring ) ) != 0 ) + goto err_add_txq; + + return 0; + + err_add_txq: + intelxl_free_ring ( intelxl, ring ); + err_alloc: + return rc; +} + +/** + * Destroy transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static void ice_destroy_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Disable transmit queue */ + if ( ( rc = ice_admin_disable_txq ( intelxl ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Free descriptor ring */ + intelxl_free_ring ( intelxl, ring ); +} + +/** + * Program receive queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + * @ret rc Return status code + */ +static int ice_context_rx ( struct intelxl_nic *intelxl, + physaddr_t address ) { + union { + struct intelxl_context_rx rx; + uint32_t raw[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + } ctx; + uint64_t base_count; + unsigned int i; + + /* Initialise context */ + memset ( &ctx, 0, sizeof ( ctx ) ); + 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_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP ); + ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) ); + + /* Write context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx.raw[0] ) ) ; i++ ) { + writel ( le32_to_cpu ( ctx.raw[i] ), + ( intelxl->regs + ICE_QRX_CONTEXT ( i ) ) ); + } + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_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 ) ); + + /* Set MAC address */ + if ( ( rc = ice_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; + + /* Create receive descriptor ring */ + if ( ( rc = intelxl_create_ring ( intelxl, &intelxl->rx ) ) != 0 ) + goto err_create_rx; + + /* Create transmit descriptor ring */ + if ( ( rc = ice_create_tx ( intelxl, &intelxl->tx ) ) != 0 ) + goto err_create_tx; + + /* Restart autonegotiation */ + ice_admin_autoneg ( intelxl ); + + /* Update link state */ + ice_admin_link ( netdev ); + + return 0; + + ice_destroy_tx ( intelxl, &intelxl->tx ); + err_create_tx: + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + err_create_rx: + err_mac_config: + err_mac_write: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ice_close ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + + /* Dump contexts (for debugging) */ + ice_dump_tx ( intelxl ); + ice_dump_rx ( intelxl ); + + /* Destroy transmit descriptor ring */ + ice_destroy_tx ( intelxl, &intelxl->tx ); + + /* Destroy receive descriptor ring */ + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + + /* Discard any unused receive buffers */ + intelxl_empty_rx ( intelxl ); +} + +/** Network device operations */ +static struct net_device_operations ice_operations = { + .open = ice_open, + .close = ice_close, + .transmit = intelxl_transmit, + .poll = intelxl_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ice_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intelxl_nic *intelxl; + uint32_t pffunc_rid; + uint32_t pfgen_portnum; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intelxl ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ice_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; + intelxl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intelxl, 0, sizeof ( *intelxl ) ); + intelxl->intr = ICE_GLINT_DYN_CTL; + intelxl->handle = ice_admin_event; + 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, + sizeof ( intelxl->tx.desc.tx[0] ), NULL ); + intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + sizeof ( intelxl->rx.desc.rx[0] ), + ice_context_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intelxl->regs = pci_ioremap ( pci, pci->membase, ICE_BAR_SIZE ); + if ( ! intelxl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Configure DMA */ + intelxl->dma = &pci->dma; + dma_set_mask_64bit ( intelxl->dma ); + netdev->dma = intelxl->dma; + + /* Locate PCI Express capability */ + intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP ); + if ( ! intelxl->exp ) { + DBGC ( intelxl, "ICE %p missing PCIe capability\n", + intelxl ); + rc = -ENXIO; + goto err_exp; + } + + /* Reset the function via PCIe FLR */ + pci_reset ( pci, intelxl->exp ); + + /* Get function and port number */ + pffunc_rid = readl ( intelxl->regs + ICE_PFFUNC_RID ); + intelxl->pf = ICE_PFFUNC_RID_FUNC_NUM ( pffunc_rid ); + pfgen_portnum = readl ( intelxl->regs + ICE_PFGEN_PORTNUM ); + intelxl->port = ICE_PFGEN_PORTNUM_PORT_NUM ( pfgen_portnum ); + DBGC ( intelxl, "ICE %p PF %d using port %d\n", + intelxl, intelxl->pf, intelxl->port ); + + /* Enable MSI-X dummy interrupt */ + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) + goto err_msix; + + /* Open admin queues */ + if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) + goto err_open_admin; + + /* Get firmware version */ + if ( ( rc = ice_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Clear PXE mode */ + if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) + goto err_admin_clear_pxe; + + /* Get switch configuration */ + if ( ( rc = ice_admin_switch ( intelxl ) ) != 0 ) + goto err_admin_switch; + + /* Add broadcast address */ + if ( ( rc = ice_admin_rules ( intelxl, eth_broadcast ) ) != 0 ) + goto err_admin_rules_broadcast; + + /* Add promiscuous unicast address */ + if ( ( rc = ice_admin_rules ( intelxl, ice_magic_mac ) ) != 0 ) + goto err_admin_rules_magic; + + /* Query scheduler topology */ + if ( ( rc = ice_admin_schedule ( intelxl ) ) != 0 ) + goto err_admin_schedule; + + /* Get MAC address */ + if ( ( rc = ice_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; + + /* Configure queue register addresses */ + intelxl->tx.tail = ICE_QTX_COMM_DBELL; + intelxl->rx.reg = ICE_QRX_CTRL; + intelxl->rx.tail = ICE_QRX_TAIL; + + /* Configure interrupt causes */ + writel ( ( ICE_QINT_TQCTL_ITR_INDX_NONE | ICE_QINT_TQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_TQCTL ); + writel ( ( ICE_QINT_RQCTL_ITR_INDX_NONE | ICE_QINT_RQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_RQCTL ); + + /* Set a default value for the queue context flex extension, + * since this register erroneously retains its value across at + * least a PCIe FLR. + */ + writel ( ( ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 | + ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX ), + intelxl->regs + ICE_QRX_FLXP_CNTXT ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + ice_admin_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_admin_mac_read: + err_admin_schedule: + err_admin_rules_magic: + err_admin_rules_broadcast: + err_admin_switch: + err_admin_clear_pxe: + err_admin_version: + intelxl_close_admin ( intelxl ); + err_open_admin: + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + err_msix: + pci_reset ( pci, intelxl->exp ); + 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 ice_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 ); + + /* Close admin queues */ + intelxl_close_admin ( intelxl ); + + /* Disable MSI-X dummy interrupt */ + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + + /* Reset the NIC */ + pci_reset ( pci, intelxl->exp ); + + /* Free network device */ + iounmap ( intelxl->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id ice_nics[] = { + PCI_ROM ( 0x8086, 0x124c, "e823l-bp", "E823-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x124d, "e823l-sfp", "E823-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x124e, "e823l-10gt", "E823-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x124f, "e823l-1g", "E823-L 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x151d, "e823l-qsfp", "E823-L QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1591, "e810c-bp", "E810-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1592, "e810c-qsfp", "E810-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1593, "e810c-sfp", "E810-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1599, "e810-xxv-bp", "E810-XXV backplane", 0 ), + PCI_ROM ( 0x8086, 0x159a, "e810-xxv-qsfp", "E810-XXV QSFP", 0 ), + PCI_ROM ( 0x8086, 0x159b, "e810-xxv-sfp", "E810-XXV SFP", 0 ), + PCI_ROM ( 0x8086, 0x188a, "e823c-bp", "E823-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x188b, "e823c-qsfp", "E823-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x188c, "e823c-sfp", "E823-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x188d, "e823c-10gt", "E823-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x188e, "e823c-1g", "E823-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1890, "e822c-bp", "E822-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1891, "e822c-qsfp", "E822-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1892, "e822c-sfp", "E822-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1893, "e822c-10gt", "E822-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x1894, "e822c-1g", "E822-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1897, "e822l-bp", "E822-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x1898, "e822l-sfp", "E822-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x1899, "e822l-10gt", "E822-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x189a, "e822l-1g", "E822-L 1GbE", 0 ), +}; + +/** PCI driver */ +struct pci_driver ice_driver __pci_driver = { + .ids = ice_nics, + .id_count = ( sizeof ( ice_nics ) / sizeof ( ice_nics[0] ) ), + .probe = ice_probe, + .remove = ice_remove, +}; diff --git a/src/drivers/net/ice.h b/src/drivers/net/ice.h new file mode 100644 index 000000000..26291a7a1 --- /dev/null +++ b/src/drivers/net/ice.h @@ -0,0 +1,565 @@ +#ifndef _ICE_H +#define _ICE_H + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/if_ether.h> +#include "intelxl.h" + +/** BAR size */ +#define ICE_BAR_SIZE 0x800000 + +/****************************************************************************** + * + * Transmit and receive datapaths + * + ****************************************************************************** + */ + +/** Transmit queue context */ +struct ice_context_tx { + /** Base address */ + uint64_t base_port; + /** PF number and queue type */ + uint16_t pf_type; + /** Source VSI */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_c[5]; + /** Queue length */ + uint16_t len; + /** Flags */ + uint16_t flags; + /** Reserved */ + uint8_t reserved_d[3]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration */ +struct ice_schedule_tx { + /** Node type */ + uint8_t type; + /** Valid sections */ + uint8_t sections; + /** Generic information */ + uint8_t generic; + /** Flags */ + uint8_t flags; + /** Committed bandwidth profile ID */ + uint16_t commit_id; + /** Committeed bandwidth weight */ + uint16_t commit_weight; + /** Excess bandwidth profile ID */ + uint16_t excess_id; + /** Excess bandwidth weight */ + uint16_t excess_weight; + /** Shared rate limit profile ID */ + uint16_t shared; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** Global Receive Queue Control Register */ +#define ICE_QRX_CTRL 0x120000 + +/** Receive Queue Context Registers */ +#define ICE_QRX_CONTEXT(x) ( 0x280000 + ( 0x2000 * (x) ) ) + +/** Receive Queue Tail Register */ +#define ICE_QRX_TAIL 0x290000 + +/** Transmit Comm Scheduler Queue Doorbell Register */ +#define ICE_QTX_COMM_DBELL 0x2c0000 + +/** Transmit Comm Scheduler Queue Context Data Registers */ +#define ICE_GLCOMM_QTX_CNTX_DATA(x) ( 0x2d2d40 + ( 0x4 * (x) ) ) + +/** Transmit Comm Scheduler Queue Context Control Register */ +#define ICE_GLCOMM_QTX_CNTX_CTL 0x2d2dc8 +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD(x) ( (x) << 16 ) /**< Command */ +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ \ + ICE_GLCOMM_QTX_CNTX_CTL_CMD ( 0 ) /**< Read context */ +#define ICE_GLCOMM_QTX_CNTX_CTL_EXEC 0x00080000UL /**< Execute */ + +/** Transmit Comm Scheduler Queue Context Status Register */ +#define ICE_GLCOMM_QTX_CNTX_STAT 0x2d2dcc +#define ICE_GLCOMM_QTX_CNTX_BUSY 0x00000001UL /**< In progress */ + +/** Queue Context Flex Extension Register */ +#define ICE_QRX_FLXP_CNTXT 0x480000 +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX(x) ( (x) << 0 ) /**< RX profile */ +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 \ + ICE_QRX_FLXP_CNTXT_RXDID_IDX ( 1 ) /**< 32-byte legacy */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO(x) ( (x) << 8 ) /**< Priority */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX \ + ICE_QRX_FLXP_CNTXT_RXDID_PRIO ( 7 ) /**< Maximum priority */ + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** Admin queue version number */ +struct ice_admin_version { + /** Branch identifier */ + uint8_t branch; + /** Major version number */ + uint8_t major; + /** Minor version number */ + uint8_t minor; + /** Patch level */ + uint8_t patch; +} __attribute__ (( packed )); + +/** Admin queue Get Version command parameters */ +struct ice_admin_version_params { + /** ROM version */ + uint32_t rom; + /** Firmware build ID */ + uint32_t build; + /** Firmware version */ + struct ice_admin_version firmware; + /** API version */ + struct ice_admin_version api; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Read command parameters */ +struct ice_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved_a[3]; + /** Number of addresses in response */ + uint8_t count; + /** Reserved */ + uint8_t reserved_b[11]; +} __attribute__ (( packed )); + +/** MAC Address description */ +struct ice_admin_mac_read_address { + /** Port number */ + uint8_t port; + /** Address type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** LAN MAC address type */ +#define ICE_ADMIN_MAC_READ_TYPE_LAN 0 + +/** Admin queue Manage MAC Address Read data buffer */ +struct ice_admin_mac_read_buffer { + /** MAC addresses */ + struct ice_admin_mac_read_address mac[4]; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Write command parameters */ +struct ice_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Admin queue Get Switch Configuration command parameters */ +struct ice_admin_switch_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Starting switching element identifier */ + uint16_t next; + /** Reserved */ + uint8_t reserved[4]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Switching element configuration */ +struct ice_admin_switch_config { + /** Switching element ID and flags */ + uint16_t seid; + /** Uplink switching element ID */ + uint16_t uplink; + /** PF/VF number */ + uint16_t func; +} __attribute__ (( packed )); + +/** Switching element ID type mask */ +#define ICE_ADMIN_SWITCH_TYPE_MASK 0xc000 + +/** Virtual Station Interface element type */ +#define ICE_ADMIN_SWITCH_TYPE_VSI 0x8000 + +/** Admin queue Get Switch Configuration data buffer */ +struct ice_admin_switch_buffer { + /** Switch configuration */ + struct ice_admin_switch_config cfg[1]; +}; + +/** Admin queue Add Switch Rules command */ +#define ICE_ADMIN_ADD_RULES 0x02a0 + +/** Admin queue Add Switch Rules command parameters */ +struct ice_admin_rules_params { + /** Number of rules */ + uint16_t count; + /** Reserved */ + uint8_t reserved[6]; +} __attribute__ (( packed )); + +/** Admin queue Add Switch Rules data buffer */ +struct ice_admin_rules_buffer { + /** Type */ + uint16_t type; + /** Return status */ + uint16_t status; + /** Receipt ID */ + uint16_t recipe; + /** Source port */ + uint16_t port; + /** Action */ + uint32_t action; + /** Lookup table index */ + uint16_t index; + /** Header length */ + uint16_t len; + /** Header data */ + union { + /** Ethernet header */ + struct ethhdr eth; + /** Raw data */ + uint8_t raw[16]; + } __attribute__ (( packed )) hdr; +} __attribute__ (( packed )); + +/** Switch rule promiscuous recipe ID */ +#define ICE_ADMIN_RULES_RECIPE_PROMISC 0x0003 + +/** Switch rule action valid */ +#define ICE_ADMIN_RULES_ACTION_VALID 0x00020000UL + +/** Switch rule VSI number */ +#define ICE_ADMIN_RULES_ACTION_VSI(x) ( (x) << 4 ) + +/** Admin queue Query Default Scheduling Tree Topology command */ +#define ICE_ADMIN_SCHEDULE 0x0400 + +/** Admin queue Query Default Scheduling Tree Topology command parameters */ +struct ice_admin_schedule_params { + /** Reserved */ + uint8_t reserved_a; + /** Total branches */ + uint8_t branches; + /** Reserved */ + uint8_t reserved_b[6]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration generic section is valid */ +#define ICE_SCHEDULE_GENERIC 0x01 + +/** Transmit scheduler configuration committed bandwidth section is valid */ +#define ICE_SCHEDULE_COMMIT 0x02 + +/** Transmit scheduler configuration excess bandwidth section is valid */ +#define ICE_SCHEDULE_EXCESS 0x04 + +/** Transmit scheduler configuration default weight */ +#define ICE_SCHEDULE_WEIGHT 0x0004 + +/** Admin queue Query Default Scheduling Tree Topology node */ +struct ice_admin_schedule_node { + /** Parent TEID */ + uint32_t parent; + /** Node TEID */ + uint32_t teid; + /** Scheduler configuration */ + struct ice_schedule_tx config; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology branch */ +struct ice_admin_schedule_branch { + /** Reserved */ + uint8_t reserved_a[4]; + /** Number of nodes */ + uint16_t count; + /** Reserved */ + uint8_t reserved_b[2]; + /** Nodes */ + struct ice_admin_schedule_node node[0]; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology data buffer */ +union ice_admin_schedule_buffer { + /** Branches */ + struct ice_admin_schedule_branch branch[0]; + /** Padding */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; +} __attribute__ (( packed )); + +/** Admin queue Restart Autonegotiation command parameters */ +struct ice_admin_autoneg_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status command parameters */ +struct ice_admin_link_params { + /** Logical port number */ + uint8_t port; + /** Reserved */ + uint8_t reserved_a; + /** Link status notification */ + uint8_t notify; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status data buffer */ +struct ice_admin_link_buffer { + /** Topology conflicts */ + uint8_t conflict; + /** Configuration errors */ + uint8_t error; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved_a[7]; + /** Link speed */ + uint16_t speed; + /** Reserved */ + uint8_t reserved_b[20]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues command */ +#define ICE_ADMIN_ADD_TXQ 0x0c30 + +/** Admin queue Add Transmit Queues command parameters */ +struct ice_admin_add_txq_params { + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved[7]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues data buffer */ +struct ice_admin_add_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a[3]; + /** Transmit queue ID */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[2]; + /** Queue TEID */ + uint32_t teid; + /** Transmit queue context */ + struct ice_context_tx ctx; + /** Scheduler configuration */ + struct ice_schedule_tx sched; +} __attribute__ (( packed )); + +/** Transmit queue base address and port number */ +#define ICE_TXQ_BASE_PORT( addr, port ) \ + ( ( (addr) >> 7 ) | ( ( ( uint64_t ) (port) ) << 57 ) ) + +/** Transmit queue PF number */ +#define ICE_TXQ_PF_TYPE( pf ) ( ( (pf) << 1 ) | ( 0x2 << 14 ) ) + +/** Transmit queue length */ +#define ICE_TXQ_LEN( count ) ( (count) >> 1 ) + +/** Transmit queue uses TSO */ +#define ICE_TXQ_FL_TSO 0x0001 + +/** Transmit queue uses legacy mode*/ +#define ICE_TXQ_FL_LEGACY 0x1000 + +/** Admin queue Disable Transmit Queues command */ +#define ICE_ADMIN_DISABLE_TXQ 0x0c31 + +/** Admin queue Disable Transmit Queues command parameters */ +struct ice_admin_disable_txq_params { + /** Flags */ + uint8_t flags; + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a; + /** Timeout */ + uint8_t timeout; + /** Reserved */ + uint8_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Disable queue and flush pipe */ +#define ICE_TXQ_FL_FLUSH 0x08 + +/** Disable queue timeout */ +#define ICE_TXQ_TIMEOUT 0xc8 + +/** Admin queue Disable Transmit Queues data buffer */ +struct ice_admin_disable_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved; + /** Transmit queue ID */ + uint16_t id; +} __attribute__ (( packed )); + +/** Admin queue command parameters */ +union ice_admin_params { + /** Additional data buffer command parameters */ + struct intelxl_admin_buffer_params buffer; + /** Get Version command parameters */ + struct ice_admin_version_params version; + /** Manage MAC Address Read command parameters */ + struct ice_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct ice_admin_mac_write_params mac_write; + /** Get Switch Configuration command parameters */ + struct ice_admin_switch_params sw; + /** Add Switch Rules command parameters */ + struct ice_admin_rules_params rules; + /** Query Default Scheduling Tree Topology command parameters */ + struct ice_admin_schedule_params sched; + /** Restart Autonegotiation command parameters */ + struct ice_admin_autoneg_params autoneg; + /** Get Link Status command parameters */ + struct ice_admin_link_params link; + /** Add Transmit Queue command parameters */ + struct ice_admin_add_txq_params add_txq; + /** Disable Transmit Queue command parameters */ + struct ice_admin_disable_txq_params disable_txq; +} __attribute__ (( packed )); + + +/** Admin queue data buffer */ +union ice_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** Manage MAC Address Read data buffer */ + struct ice_admin_mac_read_buffer mac_read; + /** Get Switch Configuration data buffer */ + struct ice_admin_switch_buffer sw; + /** Add Switch Rules data buffer */ + struct ice_admin_rules_buffer rules; + /** Query Default Scheduling Tree Topology data buffer */ + union ice_admin_schedule_buffer sched; + /** Get Link Status data buffer */ + struct ice_admin_link_buffer link; + /** Add Transmit Queue data buffer */ + struct ice_admin_add_txq_buffer add_txq; + /** Disable Transmit Queue data buffer */ + struct ice_admin_disable_txq_buffer disable_txq; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct ice_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; + /** Parameters */ + union ice_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct ice_admin_descriptor * +ice_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct ice_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union ice_admin_buffer * +ice_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union ice_admin_buffer, xl ); +} + +/****************************************************************************** + * + * Top level + * + ****************************************************************************** + */ + +/** Function Requester ID Information Register */ +#define ICE_PFFUNC_RID 0x09e880 +#define ICE_PFFUNC_RID_FUNC_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ + +/** PF LAN Port Number Register */ +#define ICE_PFGEN_PORTNUM 0x1d2400 +#define ICE_PFGEN_PORTNUM_PORT_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Port number */ + +/** Transmit Queue Interrupt Cause Control Register */ +#define ICE_QINT_TQCTL 0x140000 +#define ICE_QINT_TQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_TQCTL_ITR_INDX_NONE \ + ICE_QINT_TQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Receive Queue Interrupt Cause Control Register */ +#define ICE_QINT_RQCTL 0x150000 +#define ICE_QINT_RQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_RQCTL_ITR_INDX_NONE \ + ICE_QINT_RQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_RQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Global Interrupt Dynamic Control Register */ +#define ICE_GLINT_DYN_CTL 0x160000 + +#endif /* _ICE_H */ diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index ac9e37c5a..82b07833c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -46,79 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /****************************************************************************** * - * Device reset - * - ****************************************************************************** - */ - -/** - * Reset hardware - * - * @v intelxl Intel device - * @ret rc Return status code - */ -static int intelxl_reset ( struct intelxl_nic *intelxl ) { - uint32_t pfgen_ctrl; - - /* Perform a global software reset */ - pfgen_ctrl = readl ( intelxl->regs + INTELXL_PFGEN_CTRL ); - writel ( ( pfgen_ctrl | INTELXL_PFGEN_CTRL_PFSWR ), - intelxl->regs + INTELXL_PFGEN_CTRL ); - mdelay ( INTELXL_RESET_DELAY_MS ); - - return 0; -} - -/****************************************************************************** - * - * MAC address - * - ****************************************************************************** - */ - -/** - * Fetch initial MAC address and maximum frame size - * - * @v intelxl Intel device - * @v netdev Network device - * @ret rc Return status code - */ -static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, - struct net_device *netdev ) { - union intelxl_receive_address mac; - uint32_t prtpm_sal; - uint32_t prtpm_sah; - uint32_t prtgl_sah; - size_t mfs; - - /* Read NVM-loaded address */ - prtpm_sal = readl ( intelxl->regs + INTELXL_PRTPM_SAL ); - prtpm_sah = readl ( intelxl->regs + INTELXL_PRTPM_SAH ); - mac.reg.low = cpu_to_le32 ( prtpm_sal ); - mac.reg.high = cpu_to_le32 ( prtpm_sah ); - - /* Check that address is valid */ - if ( ! is_valid_ether_addr ( mac.raw ) ) { - DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", - intelxl, eth_ntoa ( mac.raw ) ); - return -ENOENT; - } - - /* Copy MAC address */ - DBGC ( intelxl, "INTELXL %p has autoloaded MAC address %s\n", - intelxl, eth_ntoa ( mac.raw ) ); - memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); - - /* Get maximum frame size */ - prtgl_sah = readl ( intelxl->regs + INTELXL_PRTGL_SAH ); - mfs = INTELXL_PRTGL_SAH_MFS_GET ( prtgl_sah ); - netdev->max_pkt_len = ( mfs - 4 /* CRC */ ); - - return 0; -} - -/****************************************************************************** - * * MSI-X interrupts * ****************************************************************************** @@ -129,10 +56,11 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector * @ret rc Return status code */ int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { int rc; /* Map dummy target location */ @@ -151,12 +79,12 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, goto err_enable; } - /* Configure interrupt zero to write to dummy location */ - pci_msix_map ( &intelxl->msix.cap, 0, + /* Configure interrupt to write to dummy location */ + pci_msix_map ( &intelxl->msix.cap, vector, dma ( &intelxl->msix.map, &intelxl->msix.msg ), 0 ); - /* Enable dummy interrupt zero */ - pci_msix_unmask ( &intelxl->msix.cap, 0 ); + /* Enable dummy interrupt */ + pci_msix_unmask ( &intelxl->msix.cap, vector ); return 0; @@ -172,12 +100,13 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector */ void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { - /* Disable dummy interrupt zero */ - pci_msix_mask ( &intelxl->msix.cap, 0 ); + /* Disable dummy interrupts */ + pci_msix_mask ( &intelxl->msix.cap, vector ); /* Disable MSI-X capability */ pci_msix_disable ( pci, &intelxl->msix.cap ); @@ -194,7 +123,7 @@ void intelxl_msix_disable ( struct intelxl_nic *intelxl, */ /** Admin queue register offsets */ -static const struct intelxl_admin_offsets intelxl_admin_offsets = { +const struct intelxl_admin_offsets intelxl_admin_offsets = { .bal = INTELXL_ADMIN_BAL, .bah = INTELXL_ADMIN_BAH, .len = INTELXL_ADMIN_LEN, @@ -371,6 +300,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { union intelxl_admin_buffer *buf; uint64_t address; uint32_t cookie; + uint16_t silence; unsigned int index; unsigned int tail; unsigned int i; @@ -383,15 +313,18 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ]; 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 ) ); + if ( cmd->cookie ) + DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->cookie ) ); DBGC2 ( intelxl, ":\n" ); + /* Allow expected errors to be silenced */ + silence = cmd->ret; + cmd->ret = 0; + /* 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 ) ) { @@ -401,7 +334,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { } /* Populate cookie, if not being (ab)used for VF opcode */ - if ( ! cmd->vopcode ) + if ( ! cmd->cookie ) cmd->cookie = cpu_to_le32 ( index ); /* Record cookie */ @@ -409,7 +342,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { /* Post command descriptor */ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); - if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_RD ) ) { DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, le16_to_cpu ( cmd->len ) ); } @@ -428,6 +361,10 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { intelxl, index ); 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 ) ); + } /* Check for cookie mismatch */ if ( cmd->cookie != cookie ) { @@ -438,8 +375,8 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { goto err; } - /* Check for errors */ - if ( cmd->ret != 0 ) { + /* Check for unexpected errors */ + if ( ( cmd->ret != 0 ) && ( cmd->ret != silence ) ) { DBGC ( intelxl, "INTELXL %p admin command %#x error " "%d\n", intelxl, index, le16_to_cpu ( cmd->ret ) ); @@ -551,28 +488,102 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { } /** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_read_params *read; + union intelxl_admin_buffer *buf; + uint8_t *mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = intelxl_admin_command_buffer ( intelxl ); + mac = buf->mac_read.pf; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "INTELXL %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac ) ) { + DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", + intelxl, eth_ntoa ( mac ) ); + return -ENOENT; + } + + /* Copy MAC address */ + DBGC ( intelxl, "INTELXL %p has MAC address %s\n", + intelxl, eth_ntoa ( mac ) ); + memcpy ( netdev->hw_addr, mac, ETH_ALEN ); + + return 0; +} + +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_write_params *write; + union { + uint8_t raw[ETH_ALEN]; + struct { + uint16_t high; + uint32_t low; + } __attribute__ (( packed )); + } mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + write->high = bswap_16 ( mac.high ); + write->low = bswap_32 ( mac.low ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** * Clear PXE mode * * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { +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 ); + cmd->ret = cpu_to_le16 ( INTELXL_ADMIN_EEXIST ); pxe = &cmd->params.pxe; pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC; @@ -580,6 +591,13 @@ static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; + /* Check for expected errors */ + if ( cmd->ret == cpu_to_le16 ( INTELXL_ADMIN_EEXIST ) ) { + DBGC ( intelxl, "INTELXL %p already in non-PXE mode\n", + intelxl ); + return 0; + } + return 0; } @@ -593,18 +611,20 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_switch_params *sw; union intelxl_admin_buffer *buf; + uint16_t next = 0; int rc; - /* Populate descriptor */ - 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 { + /* Populate descriptor */ + 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; + sw->next = next; + buf = intelxl_admin_command_buffer ( intelxl ); + /* Issue command */ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; @@ -624,7 +644,7 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { buf->sw.cfg.connection ); } - } while ( sw->next ); + } while ( ( next = sw->next ) ); /* Check that we found a VSI */ if ( ! intelxl->vsi ) { @@ -701,6 +721,31 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { } /** + * Set MAC configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_config_params *config; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_CONFIG ); + config = &cmd->params.mac_config; + config->mfs = cpu_to_le16 ( intelxl->mfs ); + config->flags = INTELXL_ADMIN_MAC_CONFIG_FL_CRC; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** * Restart autonegotiation * * @v intelxl Intel device @@ -760,18 +805,26 @@ static int intelxl_admin_link ( struct net_device *netdev ) { } /** - * Handle virtual function event (when VF driver is not present) + * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v evt Event descriptor + * @v buf Data buffer */ -__weak void -intelxlvf_admin_event ( struct net_device *netdev __unused, - struct intelxl_admin_descriptor *evt __unused, - union intelxl_admin_buffer *buf __unused ) { +static void intelxl_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; - /* Nothing to do */ + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + intelxl_admin_link ( netdev ); } /** @@ -823,19 +876,7 @@ void intelxl_poll_admin ( struct net_device *netdev ) { } /* Handle event */ - switch ( evt->opcode ) { - 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, - admin->index, le16_to_cpu ( evt->opcode ) ); - break; - } + intelxl->handle ( netdev, evt, buf ); /* Reset descriptor and refill queue */ intelxl_admin_event_init ( intelxl, admin->index ); @@ -864,18 +905,8 @@ int intelxl_open_admin ( struct intelxl_nic *intelxl ) { /* (Re)open admin queues */ intelxl_reopen_admin ( intelxl ); - /* Get firmware version */ - if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) - goto err_version; - - /* Report driver version */ - if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) - goto err_driver; - return 0; - err_driver: - err_version: intelxl_disable_admin ( intelxl, &intelxl->command ); intelxl_disable_admin ( intelxl, &intelxl->event ); intelxl_free_admin ( intelxl, &intelxl->command ); @@ -963,8 +994,7 @@ int intelxl_alloc_ring ( struct intelxl_nic *intelxl, ring->cons = 0; DBGC ( intelxl, "INTELXL %p ring %06x is at [%08lx,%08lx)\n", - intelxl, ( ring->reg + ring->tail ), - virt_to_phys ( ring->desc.raw ), + intelxl, ring->tail, virt_to_phys ( ring->desc.raw ), ( virt_to_phys ( ring->desc.raw ) + ring->len ) ); return 0; @@ -1203,7 +1233,7 @@ static int intelxl_enable_ring ( struct intelxl_nic *intelxl, qxx_ena = readl ( ring_regs + INTELXL_QXX_ENA ); if ( ! ( qxx_ena & INTELXL_QXX_ENA_STAT ) ) { DBGC ( intelxl, "INTELXL %p ring %06x failed to enable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -EIO; } @@ -1239,7 +1269,7 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, } DBGC ( intelxl, "INTELXL %p ring %06x timed out waiting for disable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -ETIMEDOUT; } @@ -1250,8 +1280,8 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, * @v ring Descriptor ring * @ret rc Return status code */ -static int intelxl_create_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { physaddr_t address; int rc; @@ -1284,8 +1314,8 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, * @v intelxl Intel device * @v ring Descriptor ring */ -static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +void intelxl_destroy_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { int rc; /* Disable ring */ @@ -1377,24 +1407,20 @@ void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { */ static int intelxl_open ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - union intelxl_receive_address mac; unsigned int queue; - uint32_t prtgl_sal; - uint32_t prtgl_sah; int rc; /* Calculate maximum frame size */ intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); - /* Program MAC address and maximum frame size */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, sizeof ( mac.raw ) ); - prtgl_sal = le32_to_cpu ( mac.reg.low ); - prtgl_sah = ( le32_to_cpu ( mac.reg.high ) | - INTELXL_PRTGL_SAH_MFS_SET ( intelxl->mfs ) ); - writel ( prtgl_sal, intelxl->regs + INTELXL_PRTGL_SAL ); - writel ( prtgl_sah, intelxl->regs + INTELXL_PRTGL_SAH ); + /* Set MAC address */ + if ( ( rc = intelxl_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; /* Associate transmit queue to PF */ writel ( ( INTELXL_QXX_CTL_PFVF_Q_PF | @@ -1437,6 +1463,8 @@ static int intelxl_open ( struct net_device *netdev ) { err_create_tx: intelxl_destroy_ring ( intelxl, &intelxl->rx ); err_create_rx: + err_mac_config: + err_mac_write: return rc; } @@ -1668,11 +1696,13 @@ static int intelxl_probe ( struct pci_device *pci ) { goto err_alloc; } netdev_init ( netdev, &intelxl_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; intelxl = netdev->priv; pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->intr = INTELXL_PFINT_DYN_CTL0; + intelxl->handle = intelxl_admin_event; intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, &intelxl_admin_offsets ); intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, @@ -1699,9 +1729,17 @@ static int intelxl_probe ( struct pci_device *pci ) { dma_set_mask_64bit ( intelxl->dma ); netdev->dma = intelxl->dma; - /* Reset the NIC */ - if ( ( rc = intelxl_reset ( intelxl ) ) != 0 ) - goto err_reset; + /* 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 */ + pci_reset ( pci, intelxl->exp ); /* Get function number, port number and base queue number */ pffunc_rid = readl ( intelxl->regs + INTELXL_PFFUNC_RID ); @@ -1714,18 +1752,23 @@ static int intelxl_probe ( struct pci_device *pci ) { intelxl, intelxl->pf, intelxl->port, intelxl->base, INTELXL_PFLAN_QALLOC_LASTQ ( pflan_qalloc ) ); - /* Fetch MAC address and maximum frame size */ - 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 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) goto err_open_admin; + /* Get firmware version */ + if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Report driver version */ + if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) + goto err_admin_driver; + /* Clear PXE mode */ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) goto err_admin_clear_pxe; @@ -1742,6 +1785,10 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_admin_promisc ( intelxl ) ) != 0 ) goto err_admin_promisc; + /* Get MAC address */ + if ( ( rc = intelxl_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; + /* Configure queue register addresses */ intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL ); @@ -1773,17 +1820,19 @@ static int intelxl_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_admin_mac_read: err_admin_promisc: err_admin_vsi: err_admin_switch: err_admin_clear_pxe: + err_admin_driver: + err_admin_version: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); err_msix: - err_fetch_mac: - intelxl_reset ( intelxl ); - err_reset: + pci_reset ( pci, intelxl->exp ); + err_exp: iounmap ( intelxl->regs ); err_ioremap: netdev_nullify ( netdev ); @@ -1808,10 +1857,10 @@ static void intelxl_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); /* Reset the NIC */ - intelxl_reset ( intelxl ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); @@ -1821,6 +1870,10 @@ static void intelxl_remove ( struct pci_device *pci ) { /** PCI device IDs */ static struct pci_device_id intelxl_nics[] = { + PCI_ROM ( 0x8086, 0x0cf8, "x710-n3000", "X710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x0d58, "xxv710-n3000", "XXV710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x104e, "x710-sfp-b", "X710 10GbE SFP+", 0 ), + PCI_ROM ( 0x8086, 0x104f, "x710-kx-b", "X710 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x1572, "x710-sfp", "X710 10GbE SFP+", 0 ), PCI_ROM ( 0x8086, 0x1574, "xl710-qemu", "Virtual XL710", 0 ), PCI_ROM ( 0x8086, 0x1580, "xl710-kx-b", "XL710 40GbE backplane", 0 ), @@ -1834,6 +1887,7 @@ static struct pci_device_id intelxl_nics[] = { PCI_ROM ( 0x8086, 0x1589, "x710-10gt4", "X710 10GBASE-T4", 0 ), PCI_ROM ( 0x8086, 0x158a, "xxv710", "XXV710 25GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x158b, "xxv710-sfp28", "XXV710 25GbE SFP28", 0 ), + PCI_ROM ( 0x8086, 0x15ff, "x710-10gt-b", "X710 10GBASE-T", 0 ), PCI_ROM ( 0x8086, 0x37ce, "x722-kx", "X722 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x37cf, "x722-qsfp", "X722 10GbE QSFP+", 0 ), PCI_ROM ( 0x8086, 0x37d0, "x722-sfp", "X722 10GbE SFP+", 0 ), diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index a4a776d28..6c7865aa2 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -145,6 +145,49 @@ struct intelxl_admin_shutdown_params { /** Driver is unloading */ #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01 +/** Admin queue Manage MAC Address Read command */ +#define INTELXL_ADMIN_MAC_READ 0x0107 + +/** Admin queue Manage MAC Address Read command parameters */ +struct intelxl_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** LAN MAC address is valid */ +#define INTELXL_ADMIN_MAC_READ_VALID_LAN 0x10 + +/** Admin queue Manage MAC Address Read data buffer */ +struct intelxl_admin_mac_read_buffer { + /** Physical function MAC address */ + uint8_t pf[ETH_ALEN]; + /** Reserved */ + uint8_t reserved[ETH_ALEN]; + /** Port MAC address */ + uint8_t port[ETH_ALEN]; + /** Physical function wake-on-LAN MAC address */ + uint8_t wol[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Write command */ +#define INTELXL_ADMIN_MAC_WRITE 0x0108 + +/** Admin queue Manage MAC Address Write command parameters */ +struct intelxl_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address first 16 bits, byte-swapped */ + uint16_t high; + /** MAC address last 32 bits, byte-swapped */ + uint32_t low; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + /** Admin queue Clear PXE Mode command */ #define INTELXL_ADMIN_CLEAR_PXE 0x0110 @@ -263,6 +306,22 @@ struct intelxl_admin_promisc_params { /** Promiscuous VLAN mode */ #define INTELXL_ADMIN_PROMISC_FL_VLAN 0x0010 +/** Admin queue Set MAC Configuration command */ +#define INTELXL_ADMIN_MAC_CONFIG 0x0603 + +/** Admin queue Set MAC Configuration command parameters */ +struct intelxl_admin_mac_config_params { + /** Maximum frame size */ + uint16_t mfs; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[13]; +} __attribute__ (( packed )); + +/** Append CRC on transmit */ +#define INTELXL_ADMIN_MAC_CONFIG_FL_CRC 0x04 + /** Admin queue Restart Autonegotiation command */ #define INTELXL_ADMIN_AUTONEG 0x0605 @@ -305,172 +364,6 @@ 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 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 */ @@ -481,6 +374,10 @@ union intelxl_admin_params { struct intelxl_admin_driver_params driver; /** Shutdown command parameters */ struct intelxl_admin_shutdown_params shutdown; + /** Manage MAC Address Read command parameters */ + struct intelxl_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct intelxl_admin_mac_write_params mac_write; /** Clear PXE Mode command parameters */ struct intelxl_admin_clear_pxe_params pxe; /** Get Switch Configuration command parameters */ @@ -489,34 +386,29 @@ union intelxl_admin_params { struct intelxl_admin_vsi_params vsi; /** Set VSI Promiscuous Modes command parameters */ struct intelxl_admin_promisc_params promisc; + /** Set MAC Configuration command parameters */ + struct intelxl_admin_mac_config_params mac_config; /** Restart Autonegotiation command parameters */ struct intelxl_admin_autoneg_params autoneg; /** Get Link Status command parameters */ struct intelxl_admin_link_params link; } __attribute__ (( packed )); +/** Maximum size of a data buffer */ +#define INTELXL_ADMIN_BUFFER_SIZE 0x1000 + /** Admin queue data buffer */ union intelxl_admin_buffer { /** Driver Version data buffer */ struct intelxl_admin_driver_buffer driver; + /** Manage MAC Address Read data buffer */ + struct intelxl_admin_mac_read_buffer mac_read; /** Get Switch Configuration data 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]; + /** Maximum buffer size */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; } __attribute__ (( packed )); /** Admin queue descriptor */ @@ -529,15 +421,10 @@ struct intelxl_admin_descriptor { uint16_t len; /** Return value */ uint16_t ret; - /** Opaque cookie / VF opcode */ - union { - /** Cookie */ - uint32_t cookie; - /** VF opcode */ - uint32_t vopcode; - }; - /** VF return value */ - int32_t vret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; /** Parameters */ union intelxl_admin_params params; } __attribute__ (( packed )); @@ -557,6 +444,9 @@ struct intelxl_admin_descriptor { /** Admin descriptor uses data buffer */ #define INTELXL_ADMIN_FL_BUF 0x1000 +/** Error: attempt to create something that already exists */ +#define INTELXL_ADMIN_EEXIST 13 + /** Admin queue */ struct intelxl_admin { /** Descriptors */ @@ -685,6 +575,8 @@ struct intelxl_context_rx { uint8_t reserved_b[7]; /** Maximum frame size */ uint16_t mfs; + /** Reserved */ + uint8_t reserved_c[8]; } __attribute__ (( packed )); /** Receive queue base address and queue count */ @@ -755,10 +647,6 @@ 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 */ @@ -919,9 +807,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** Number of receive descriptors * - * Must be a multiple of 32. + * Must be a multiple of 32 and greater than or equal to 64. */ -#define INTELXL_RX_NUM_DESC 32 +#define INTELXL_RX_NUM_DESC 64 /** Receive descriptor ring fill level * @@ -929,6 +817,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, */ #define INTELXL_RX_FILL 16 +/** Maximum packet length (excluding CRC) */ +#define INTELXL_MAX_PKT_LEN ( 9728 - 4 /* CRC */ ) + /****************************************************************************** * * Top level @@ -983,17 +874,10 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, INTELXL_QINT_TQCTL_NEXTQ_TYPE ( 0x1 ) /**< Transmit queue */ #define INTELXL_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ -/** PF Control Register */ -#define INTELXL_PFGEN_CTRL 0x092400 -#define INTELXL_PFGEN_CTRL_PFSWR 0x00000001UL /**< Software Reset */ - -/** Time to delay for device reset, in milliseconds */ -#define INTELXL_RESET_DELAY_MS 100 - /** Function Requester ID Information Register */ #define INTELXL_PFFUNC_RID 0x09c000 #define INTELXL_PFFUNC_RID_FUNC_NUM(x) \ - ( ( (x) >> 0 ) & 0x3 ) /**< Function number */ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ /** PF Queue Allocation Register */ #define INTELXL_PFLAN_QALLOC 0x1c0400 @@ -1007,29 +891,6 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, #define INTELXL_PFGEN_PORTNUM_PORT_NUM(x) \ ( ( (x) >> 0 ) & 0x3 ) /**< Port number */ -/** Port MAC Address Low Register */ -#define INTELXL_PRTGL_SAL 0x1e2120 - -/** Port MAC Address High Register */ -#define INTELXL_PRTGL_SAH 0x1e2140 -#define INTELXL_PRTGL_SAH_MFS_GET(x) ( (x) >> 16 ) /**< Max frame size */ -#define INTELXL_PRTGL_SAH_MFS_SET(x) ( (x) << 16 ) /**< Max frame size */ - -/** Physical Function MAC Address Low Register */ -#define INTELXL_PRTPM_SAL 0x1e4440 - -/** Physical Function MAC Address High Register */ -#define INTELXL_PRTPM_SAH 0x1e44c0 - -/** Receive address */ -union intelxl_receive_address { - struct { - uint32_t low; - uint32_t high; - } __attribute__ (( packed )) reg; - uint8_t raw[ETH_ALEN]; -}; - /** MSI-X interrupt */ struct intelxl_msix { /** PCI capability */ @@ -1040,7 +901,10 @@ struct intelxl_msix { struct dma_mapping map; }; -/** An Intel 40Gigabit network card */ +/** MSI-X interrupt vector */ +#define INTELXL_MSIX_VECTOR 0 + +/** An Intel 40 Gigabit network card */ struct intelxl_nic { /** Registers */ void *regs; @@ -1061,6 +925,10 @@ struct intelxl_nic { unsigned int vsi; /** Queue set handle */ unsigned int qset; + /** Transmit element ID */ + uint32_t teid; + /** Device capabilities */ + uint32_t caps; /** Interrupt control register */ unsigned int intr; /** PCI Express capability offset */ @@ -1075,10 +943,6 @@ struct intelxl_nic { /** 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; @@ -1086,17 +950,34 @@ struct intelxl_nic { struct intelxl_ring rx; /** Receive I/O buffers */ struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; + + /** + * Handle admin event + * + * @v netdev Network device + * @v evt Event descriptor + * @v buf Data buffer + */ + void ( * handle ) ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ); }; +extern const struct intelxl_admin_offsets intelxl_admin_offsets; + extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); extern void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); 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 int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ); +extern int intelxl_admin_mac_config ( 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 ); @@ -1105,13 +986,13 @@ 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 int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); +extern void intelxl_destroy_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 ); - #endif /* _INTELXL_H */ diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c index 752de7815..083195513 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -46,23 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * 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 @@ -120,26 +103,16 @@ static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) { } /** - * Reset hardware via admin queue + * Wait for reset to complete * * @v intelxl Intel device * @ret rc Return status code */ -static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor *cmd; +static int intelxlvf_reset_wait ( struct intelxl_nic *intelxl ) { 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 ); + mdelay ( INTELXLVF_RESET_DELAY_MS ); /* Wait for reset to take effect */ if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 ) @@ -152,10 +125,35 @@ static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { err_active: err_teardown: intelxl_reopen_admin ( intelxl ); - err_command: return rc; } +/** + * Reset hardware via admin queue + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { + struct intelxlvf_admin_descriptor *cmd; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /****************************************************************************** * * Admin queue @@ -190,13 +188,15 @@ static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = { 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; + struct intelxl_admin_descriptor *xlcmd; + struct intelxlvf_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 ); + xlcmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + cmd = container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); /* Record opcode */ intelxl->vopcode = le32_to_cpu ( cmd->vopcode ); @@ -218,7 +218,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { } /* Check for errors */ - if ( intelxl->vret != 0 ) + if ( cmd->vret != 0 ) return -EIO; return 0; @@ -239,7 +239,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { * @v link Link status */ static void intelxlvf_admin_link ( struct net_device *netdev, - struct intelxl_admin_vf_status_link *link ) { + struct intelxlvf_admin_status_link *link ) { struct intelxl_nic *intelxl = netdev->priv; DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl, @@ -261,12 +261,12 @@ static void intelxlvf_admin_link ( struct net_device *netdev, */ static void intelxlvf_admin_status ( struct net_device *netdev, - struct intelxl_admin_vf_status_buffer *stat ) { + struct intelxlvf_admin_status_buffer *stat ) { struct intelxl_nic *intelxl = netdev->priv; /* Handle event */ switch ( stat->event ) { - case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ): + case cpu_to_le32 ( INTELXLVF_ADMIN_STATUS_LINK ): intelxlvf_admin_link ( netdev, &stat->data.link ); break; default: @@ -278,26 +278,42 @@ intelxlvf_admin_status ( struct net_device *netdev, } /** - * Handle virtual function event + * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v xlevt Admin queue event descriptor + * @v xlbuf Admin queue event data buffer */ -void intelxlvf_admin_event ( struct net_device *netdev, - struct intelxl_admin_descriptor *evt, - union intelxl_admin_buffer *buf ) { +static void intelxlvf_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf ) { struct intelxl_nic *intelxl = netdev->priv; - unsigned int vopcode = le32_to_cpu ( evt->vopcode ); + struct intelxl_admin *admin = &intelxl->command; + struct intelxlvf_admin_descriptor *evt = + container_of ( xlevt, struct intelxlvf_admin_descriptor, xl ); + union intelxlvf_admin_buffer *buf = + container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); + unsigned int vopcode; + unsigned int index; + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_VF ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } /* Record command response if applicable */ + vopcode = le32_to_cpu ( evt->vopcode ); if ( vopcode == intelxl->vopcode ) { - memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) ); + index = ( ( admin->index - 1 ) % INTELXL_ADMIN_NUM_DESC ); + memcpy ( &admin->desc[index], evt, sizeof ( *evt ) ); + memcpy ( &admin->buf[index], buf, sizeof ( *buf ) ); intelxl->vopcode = 0; - intelxl->vret = le32_to_cpu ( evt->vret ); - if ( intelxl->vret != 0 ) { + if ( evt->vret != 0 ) { DBGC ( intelxl, "INTELXL %p admin VF command %#x " - "error %d\n", intelxl, vopcode, intelxl->vret ); + "error %d\n", intelxl, vopcode, + le32_to_cpu ( evt->vret ) ); DBGC_HDA ( intelxl, virt_to_phys ( evt ), evt, sizeof ( *evt ) ); DBGC_HDA ( intelxl, virt_to_phys ( buf ), buf, @@ -308,7 +324,7 @@ void intelxlvf_admin_event ( struct net_device *netdev, /* Handle unsolicited events */ switch ( vopcode ) { - case INTELXL_ADMIN_VF_STATUS: + case INTELXLVF_ADMIN_STATUS: intelxlvf_admin_status ( netdev, &buf->stat ); break; default: @@ -323,6 +339,45 @@ void intelxlvf_admin_event ( struct net_device *netdev, } /** + * Negotiate API version + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_version ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_VERSION ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->ver ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->ver.major = cpu_to_le32 ( INTELXLVF_ADMIN_API_MAJOR ); + buf->ver.minor = cpu_to_le32 ( INTELXLVF_ADMIN_API_MINOR ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + api = le32_to_cpu ( buf->ver.major ); + DBGC ( intelxl, "INTELXL %p API v%d.%d\n", + intelxl, api, le32_to_cpu ( buf->ver.minor ) ); + + /* Check for API compatibility */ + if ( api > INTELXLVF_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "INTELXL %p unsupported API v%d\n", + intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** * Get resources * * @v netdev Network device @@ -330,23 +385,116 @@ void intelxlvf_admin_event ( struct net_device *netdev, */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_RESOURCES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->caps ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 | + INTELXLVF_ADMIN_CAP_RQPS ); /* 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 ); + intelxl->caps = le32_to_cpu ( buf->res.caps ); + intelxl->vsi = le16_to_cpu ( buf->res.vsi ); + memcpy ( netdev->hw_addr, buf->res.mac, ETH_ALEN ); + DBGC ( intelxl, "INTELXL %p capabilities %#08x VSI %#04x\n", + intelxl, intelxl->caps, intelxl->vsi ); + + return 0; +} + +/** + * Get statistics (for debugging) + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_stats ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + struct intelxlvf_admin_stats *tx; + struct intelxlvf_admin_stats *rx; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_STATS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->queues.vsi = cpu_to_le16 ( intelxl->vsi ); + tx = &buf->stats.tx; + rx = &buf->stats.rx; + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + DBGC ( intelxl, "INTELXL %p TX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p TX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->broadcasts ) ) ); + DBGC ( intelxl, "INTELXL %p RX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p RX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->broadcasts ) ) ); + + return 0; +} + +/** + * Configure number of queue pairs + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_request_qps ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_REQUEST_QPS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rqps ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->rqps.count = cpu_to_le16 ( 1 ); + + /* Issue command (which will trigger a reset) */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + /* Reestablish capabilities to reactivate VF after reset */ + if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) + return rc; return 0; } @@ -366,16 +514,16 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -404,18 +552,19 @@ static int intelxlvf_admin_configure ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_admin_command_buffer ( intelxl ); buf->irq.count = cpu_to_le16 ( 1 ); buf->irq.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->irq.vec = cpu_to_le16 ( INTELXLVF_MSIX_VECTOR ); buf->irq.rxmap = cpu_to_le16 ( 0x0001 ); buf->irq.txmap = cpu_to_le16 ( 0x0001 ); @@ -435,17 +584,17 @@ static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_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 = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXLVF_ADMIN_ENABLE ) : + cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -465,16 +614,16 @@ static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -524,10 +673,14 @@ static int intelxlvf_open ( struct net_device *netdev ) { if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 ) goto err_promisc; + /* Reset statistics counters (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + return 0; err_promisc: - intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE ); + intelxlvf_admin_queues ( netdev, 0 ); err_enable: err_irq_map: err_configure: @@ -547,6 +700,10 @@ static void intelxlvf_close ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; int rc; + /* Show statistics (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + /* Disable queues */ if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) { /* Leak memory; there's nothing else we can do */ @@ -600,7 +757,8 @@ static int intelxlvf_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); - intelxl->intr = INTELXLVF_VFINT_DYN_CTL0; + intelxl->intr = INTELXLVF_VFINT_DYN_CTLN ( INTELXLVF_MSIX_VECTOR ); + intelxl->handle = intelxlvf_admin_event; intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN, &intelxlvf_admin_command_offsets ); intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN, @@ -637,10 +795,11 @@ static int intelxlvf_probe ( struct pci_device *pci ) { } /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Enable MSI-X dummy interrupt */ - if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXLVF_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ @@ -651,10 +810,19 @@ static int intelxlvf_probe ( struct pci_device *pci ) { if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 ) goto err_reset_admin; + /* Negotiate API version */ + if ( ( rc = intelxlvf_admin_version ( netdev ) ) != 0 ) + goto err_version; + /* Get MAC address */ if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) goto err_get_resources; + /* Configure number of queue pairs, if applicable */ + if ( ( intelxl->caps & INTELXLVF_ADMIN_CAP_RQPS ) && + ( ( rc = intelxlvf_admin_request_qps ( netdev ) ) != 0 ) ) + goto err_rqps; + /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; @@ -663,13 +831,15 @@ static int intelxlvf_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_rqps: err_get_resources: + err_version: err_reset_admin: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); err_msix: - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); err_exp: iounmap ( intelxl->regs ); err_ioremap: @@ -698,10 +868,10 @@ static void intelxlvf_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); @@ -713,7 +883,7 @@ static void intelxlvf_remove ( struct pci_device *pci ) { 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, 0x1889, "iavf", "Intel adaptive VF", 0 ), PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ), PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ), }; diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index ffcae5674..95ddf9474 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -14,12 +14,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** BAR size */ #define INTELXLVF_BAR_SIZE 0x10000 +/** MSI-X vector + * + * The 100 Gigabit physical function driver requires a virtual + * function driver to request that transmit and receive queues are + * mapped to MSI-X vector 1 or higher. + */ +#define INTELXLVF_MSIX_VECTOR 1 + /** Transmit Queue Tail Register */ #define INTELXLVF_QTX_TAIL 0x00000 /** Receive Queue Tail Register */ #define INTELXLVF_QRX_TAIL 0x02000 +/** VF Interrupt N Dynamic Control Register */ +#define INTELXLVF_VFINT_DYN_CTLN( x ) ( 0x3800 + ( 0x4 * ( (x) - 1 ) ) ) + /** VF Interrupt Zero Dynamic Control Register */ #define INTELXLVF_VFINT_DYN_CTL0 0x5c00 @@ -59,11 +70,335 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for a VF admin request to complete */ #define INTELXLVF_ADMIN_MAX_WAIT_MS 2000 +/** Admin queue Send Message to PF command */ +#define INTELXLVF_ADMIN_SEND_TO_PF 0x0801 + +/** Admin queue Send Message to VF command */ +#define INTELXLVF_ADMIN_SEND_TO_VF 0x0802 + +/** Admin Queue VF Version opcode */ +#define INTELXLVF_ADMIN_VERSION 0x00000001 + +/** Admin Queue VF Version data buffer */ +struct intelxlvf_admin_version_buffer { + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; +} __attribute__ (( packed )); + +/** Admin queue VF API major version */ +#define INTELXLVF_ADMIN_API_MAJOR 1 + +/** Admin queue VF API minor version */ +#define INTELXLVF_ADMIN_API_MINOR 1 + +/** Admin Queue VF Reset opcode */ +#define INTELXLVF_ADMIN_RESET 0x00000002 + +/** Admin Queue VF Get Resources opcode */ +#define INTELXLVF_ADMIN_GET_RESOURCES 0x00000003 + +/** Admin Queue VF Capabilities data buffer */ +struct intelxlvf_admin_capabilities_buffer { + /** Capabilities */ + uint32_t caps; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Resources data buffer */ +struct intelxlvf_admin_get_resources_buffer { + /** Number of VSIs */ + uint16_t vsis; + /** Number of queue pairs */ + uint16_t qps; + /** Number of MSI-X vectors */ + uint16_t vectors; + /** Maximum MTU */ + uint16_t mtu; + /** Capabilities */ + uint32_t caps; + /** Reserved */ + uint8_t reserved_a[8]; + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_b[8]; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Layer 2 capabilities (add/remove MAC, configure promiscuous mode) */ +#define INTELXLVF_ADMIN_CAP_L2 0x00000001 + +/** Request Queues capabilities */ +#define INTELXLVF_ADMIN_CAP_RQPS 0x00000040 + +/** Admin Queue VF Status Change Event opcode */ +#define INTELXLVF_ADMIN_STATUS 0x00000011 + +/** Link status change event type */ +#define INTELXLVF_ADMIN_STATUS_LINK 0x00000001 + +/** Link status change event data */ +struct intelxlvf_admin_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 intelxlvf_admin_status_buffer { + /** Event type */ + uint32_t event; + /** Event data */ + union { + /** Link change event data */ + struct intelxlvf_admin_status_link link; + } data; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Queues opcode */ +#define INTELXLVF_ADMIN_CONFIGURE 0x00000006 + +/** Admin Queue VF Configure Queues data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_IRQ_MAP 0x00000007 + +/** Admin Queue VF IRQ Map data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_ENABLE 0x00000008 + +/** Admin Queue VF Disable Queues opcode */ +#define INTELXLVF_ADMIN_DISABLE 0x00000009 + +/** Admin Queue VF Enable/Disable Queues data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_PROMISC 0x0000000e + +/** Admin Queue VF Configure Promiscuous Mode data buffer */ +struct intelxlvf_admin_promisc_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Flags */ + uint16_t flags; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Statistics opcode */ +#define INTELXLVF_ADMIN_GET_STATS 0x0000000f + +/** VF statistics */ +struct intelxlvf_admin_stats { + /** Bytes */ + uint64_t bytes; + /** Unicast packets */ + uint64_t unicasts; + /** Multicast packets */ + uint64_t multicasts; + /** Broadcast packets */ + uint64_t broadcasts; + /** Discarded packets */ + uint64_t discards; + /** Errors */ + uint64_t errors; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Statistics data buffer */ +struct intelxlvf_admin_stats_buffer { + /** Receive statistics */ + struct intelxlvf_admin_stats rx; + /** Transmit statistics */ + struct intelxlvf_admin_stats tx; +} __attribute__ (( packed )); + +/** Admin Queue VF Request Queues opcode */ +#define INTELXLVF_ADMIN_REQUEST_QPS 0x0000001d + +/** Admin Queue VF Request Queues data buffer */ +struct intelxlvf_admin_request_qps_buffer { + /** Number of queue pairs */ + uint16_t count; +} __attribute__ (( packed )); + +/** Admin queue data buffer */ +union intelxlvf_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** VF Version data buffer */ + struct intelxlvf_admin_version_buffer ver; + /** VF Capabilities data buffer */ + struct intelxlvf_admin_capabilities_buffer caps; + /** VF Get Resources data buffer */ + struct intelxlvf_admin_get_resources_buffer res; + /** VF Status Change Event data buffer */ + struct intelxlvf_admin_status_buffer stat; + /** VF Configure Queues data buffer */ + struct intelxlvf_admin_configure_buffer cfg; + /** VF Enable/Disable Queues data buffer */ + struct intelxlvf_admin_queues_buffer queues; + /** VF Configure Promiscuous Mode data buffer */ + struct intelxlvf_admin_promisc_buffer promisc; + /** VF IRQ Map data buffer */ + struct intelxlvf_admin_irq_map_buffer irq; + /** VF Get Statistics data buffer */ + struct intelxlvf_admin_stats_buffer stats; + /** VF Request Queues data buffer */ + struct intelxlvf_admin_request_qps_buffer rqps; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct intelxlvf_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** VF opcode */ + uint32_t vopcode; + /** VF return value */ + int32_t vret; + /** Parameters */ + union intelxl_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct intelxlvf_admin_descriptor * +intelxlvf_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union intelxlvf_admin_buffer * +intelxlvf_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); +} + /** 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 +/** Minimum time to wait for reset to complete */ +#define INTELXLVF_RESET_DELAY_MS 100 + /** Maximum time to wait for reset to complete */ #define INTELXLVF_RESET_MAX_WAIT_MS 1000 diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c index 1e8088d76..2c0f91e21 100644 --- a/src/drivers/net/ncm.c +++ b/src/drivers/net/ncm.c @@ -598,7 +598,7 @@ static int ncm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n", ncm, strerror ( rc ) ); goto err_fetch_mac; diff --git a/src/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c index a2a811aa0..5bae66fbe 100644 --- a/src/hci/commands/pci_cmd.c +++ b/src/hci/commands/pci_cmd.c @@ -22,6 +22,7 @@ */ #include <stdio.h> +#include <errno.h> #include <getopt.h> #include <ipxe/pci.h> #include <ipxe/command.h> @@ -58,7 +59,7 @@ static int pciscan_exec ( int argc, char **argv ) { struct named_setting setting; struct pci_device pci; unsigned long prev; - int next; + uint32_t busdevfn; int len; int rc; @@ -75,17 +76,19 @@ static int pciscan_exec ( int argc, char **argv ) { if ( ( len = fetchn_setting ( setting.settings, &setting.setting, NULL, &setting.setting, &prev ) ) < 0 ) { /* Setting not yet defined: start searching from 00:00.0 */ - prev = 0; + busdevfn = 0; } else { /* Setting is defined: start searching from next location */ - prev++; + busdevfn = ( prev + 1 ); + if ( ! busdevfn ) { + rc = -ENOENT; + goto err_end; + } } /* Find next existent PCI device */ - if ( ( next = pci_find_next ( &pci, prev ) ) < 0 ) { - rc = next; + if ( ( rc = pci_find_next ( &pci, &busdevfn ) ) != 0 ) goto err_find_next; - } /* Apply default type if necessary. Use ":uint16" rather than * ":busdevfn" to allow for easy inclusion within a @@ -96,13 +99,14 @@ static int pciscan_exec ( int argc, char **argv ) { /* Store setting */ if ( ( rc = storen_setting ( setting.settings, &setting.setting, - next ) ) != 0 ) { + busdevfn ) ) != 0 ) { printf ( "Could not store \"%s\": %s\n", setting.setting.name, strerror ( rc ) ); goto err_store; } err_store: + err_end: err_find_next: err_parse_setting: err_parse_options: diff --git a/src/include/ipxe/dhe.h b/src/include/ipxe/dhe.h new file mode 100644 index 000000000..3cd24a880 --- /dev/null +++ b/src/include/ipxe/dhe.h @@ -0,0 +1,19 @@ +#ifndef _IPXE_DHE_H +#define _IPXE_DHE_H + +/** @file + * + * Ephemeral Diffie-Hellman key exchange + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +extern int dhe_key ( const void *modulus, size_t len, const void *generator, + size_t generator_len, const void *partner, + size_t partner_len, const void *private, + size_t private_len, void *public, void *shared ); + +#endif /* _IPXE_DHE_H */ diff --git a/src/include/ipxe/ecam.h b/src/include/ipxe/ecam.h new file mode 100644 index 000000000..683d613a0 --- /dev/null +++ b/src/include/ipxe/ecam.h @@ -0,0 +1,57 @@ +#ifndef _IPXE_ECAM_H +#define _IPXE_ECAM_H + +/** @file + * + * PCI I/O API for Enhanced Configuration Access Mechanism (ECAM) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/acpi.h> +#include <ipxe/pci.h> + +/** Enhanced Configuration Access Mechanism per-device size */ +#define ECAM_SIZE 4096 + +/** Enhanced Configuration Access Mechanism table signature */ +#define ECAM_SIGNATURE ACPI_SIGNATURE ( 'M', 'C', 'F', 'G' ) + +/** An Enhanced Configuration Access Mechanism allocation */ +struct ecam_allocation { + /** Base address */ + uint64_t base; + /** PCI segment number */ + uint16_t segment; + /** Start PCI bus number */ + uint8_t start; + /** End PCI bus number */ + uint8_t end; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** An Enhanced Configuration Access Mechanism table */ +struct ecam_table { + /** ACPI header */ + struct acpi_header acpi; + /** Reserved */ + uint8_t reserved[8]; + /** Allocation structures */ + struct ecam_allocation alloc[0]; +} __attribute__ (( packed )); + +/** A mapped Enhanced Configuration Access Mechanism allocation */ +struct ecam_mapping { + /** Allocation */ + struct ecam_allocation alloc; + /** PCI bus:dev.fn address range */ + struct pci_range range; + /** MMIO base address */ + void *regs; +}; + +extern struct pci_api ecam_api; + +#endif /* _IPXE_ECAM_H */ diff --git a/src/include/ipxe/ecam_io.h b/src/include/ipxe/ecam_io.h new file mode 100644 index 000000000..4fb24db33 --- /dev/null +++ b/src/include/ipxe/ecam_io.h @@ -0,0 +1,139 @@ +#ifndef _IPXE_ECAM_IO_H +#define _IPXE_ECAM_IO_H + +/** @file + * + * PCI I/O API for Enhanced Configuration Access Mechanism (ECAM) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +#ifdef PCIAPI_ECAM +#define PCIAPI_PREFIX_ecam +#else +#define PCIAPI_PREFIX_ecam __ecam_ +#endif + +struct pci_device; + +/** Construct ECAM location */ +#define ECAM_LOC( where, len ) ( ( (len) << 16 ) | where ) + +/** Extract offset from ECAM location */ +#define ECAM_WHERE( location ) ( (location) & 0xffff ) + +/** Extract length from ECAM location */ +#define ECAM_LEN( location ) ( (location) >> 16 ) + +extern int ecam_read ( struct pci_device *pci, unsigned int location, + void *value ); +extern int ecam_write ( struct pci_device *pci, unsigned int location, + unsigned long value ); + +/** + * Read byte from PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + return ecam_read ( pci, ECAM_LOC ( where, sizeof ( *value ) ), value ); +} + +/** + * Read word from PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + return ecam_read ( pci, ECAM_LOC ( where, sizeof ( *value ) ), value ); +} + +/** + * Read dword from PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + return ecam_read ( pci, ECAM_LOC ( where, sizeof ( *value ) ), value ); +} + +/** + * Write byte to PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + return ecam_write ( pci, ECAM_LOC ( where, sizeof ( value ) ), value ); +} + +/** + * Write word to PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + return ecam_write ( pci, ECAM_LOC ( where, sizeof ( value ) ), value ); +} + +/** + * Write dword to PCI configuration space via ECAM + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( ecam, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + return ecam_write ( pci, ECAM_LOC ( where, sizeof ( value ) ), value ); +} + +/** + * Map PCI bus address as an I/O address + * + * @v bus_addr PCI bus address + * @v len Length of region + * @ret io_addr I/O address, or NULL on error + */ +static inline __always_inline void * +PCIAPI_INLINE ( ecam, pci_ioremap ) ( struct pci_device *pci __unused, + unsigned long bus_addr, size_t len ) { + return ioremap ( bus_addr, len ); +} + +#endif /* _IPXE_ECAM_IO_H */ diff --git a/src/include/ipxe/efi/efi_pci_api.h b/src/include/ipxe/efi/efi_pci_api.h index 887d5ee14..cf5e1d020 100644 --- a/src/include/ipxe/efi/efi_pci_api.h +++ b/src/include/ipxe/efi/efi_pci_api.h @@ -33,14 +33,17 @@ extern int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ); /** - * Determine number of PCI buses within system + * Find next PCI bus:dev.fn address range in system * - * @ret num_bus Number of buses + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -static inline __always_inline int -PCIAPI_INLINE ( efi, pci_num_bus ) ( void ) { +static inline __always_inline void +PCIAPI_INLINE ( efi, pci_discover ) ( uint32_t busdevfn __unused, + struct pci_range *range ) { + /* EFI does not want us to scan the PCI bus ourselves */ - return 0; + range->count = 0; } /** diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 81f555725..c3541e8a0 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -215,6 +215,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 ) #define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 ) #define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 ) +#define ERRFILE_ice ( ERRFILE_DRIVER | 0x00d20000 ) +#define ERRFILE_ecam ( ERRFILE_DRIVER | 0x00d30000 ) +#define ERRFILE_pcibridge ( ERRFILE_DRIVER | 0x00d40000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -396,6 +399,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_linux_sysfs ( ERRFILE_OTHER | 0x00560000 ) #define ERRFILE_linux_acpi ( ERRFILE_OTHER | 0x00570000 ) #define ERRFILE_dynkeymap ( ERRFILE_OTHER | 0x00580000 ) +#define ERRFILE_pci_cmd ( ERRFILE_OTHER | 0x00590000 ) +#define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 ) /** @} */ diff --git a/src/include/ipxe/hmac.h b/src/include/ipxe/hmac.h index 09d3e273d..cf9d08677 100644 --- a/src/include/ipxe/hmac.h +++ b/src/include/ipxe/hmac.h @@ -10,23 +10,45 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/crypto.h> +/** HMAC context type */ +#define hmac_context_t( digest ) struct { \ + /** Digest context */ \ + uint8_t ctx[ digest->ctxsize ]; \ + /** HMAC input/output padding */ \ + uint8_t pad[ digest->blocksize ]; \ + } __attribute__ (( packed )) + +/** + * Calculate HMAC context size + * + * @v digest Digest algorithm to use + * @ret len HMAC context size + */ +static inline __attribute__ (( always_inline )) size_t +hmac_ctxsize ( struct digest_algorithm *digest ) { + hmac_context_t ( digest ) *hctx; + + return sizeof ( *hctx ); +} + /** * Update HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v data Data * @v len Length of data */ -static inline void hmac_update ( struct digest_algorithm *digest, - void *digest_ctx, const void *data, - size_t len ) { - digest_update ( digest, digest_ctx, data, len ); +static inline void hmac_update ( struct digest_algorithm *digest, void *ctx, + const void *data, size_t len ) { + hmac_context_t ( digest ) *hctx = ctx; + + digest_update ( digest, hctx->ctx, data, len ); } -extern void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len ); -extern void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len, void *hmac ); +extern void hmac_init ( struct digest_algorithm *digest, void *ctx, + const void *key, size_t key_len ); +extern void hmac_final ( struct digest_algorithm *digest, void *ctx, + void *hmac ); #endif /* _IPXE_HMAC_H */ diff --git a/src/include/ipxe/linux/linux_pci.h b/src/include/ipxe/linux/linux_pci.h index de42f766b..ec6ff8b1c 100644 --- a/src/include/ipxe/linux/linux_pci.h +++ b/src/include/ipxe/linux/linux_pci.h @@ -23,14 +23,18 @@ extern int linux_pci_write ( struct pci_device *pci, unsigned long where, unsigned long value, size_t len ); /** - * Determine number of PCI buses within system + * Find next PCI bus:dev.fn address range in system * - * @ret num_bus Number of buses + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -static inline __always_inline int -PCIAPI_INLINE ( linux, pci_num_bus ) ( void ) { - /* Assume all buses may exist */ - return 0x100; +static inline __always_inline void +PCIAPI_INLINE ( linux, pci_discover ) ( uint32_t busdevfn __unused, + struct pci_range *range ) { + + /* Assume all buses in segment 0 may exist */ + range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 ); + range->count = PCI_BUSDEVFN ( 1, 0, 0, 0 ); } /** diff --git a/src/include/ipxe/md4.h b/src/include/ipxe/md4.h index 8f172e626..9f6cb8a5f 100644 --- a/src/include/ipxe/md4.h +++ b/src/include/ipxe/md4.h @@ -65,6 +65,9 @@ struct md4_context { /** MD4 context size */ #define MD4_CTX_SIZE sizeof ( struct md4_context ) +/** MD4 block size */ +#define MD4_BLOCK_SIZE sizeof ( union md4_block ) + /** MD4 digest size */ #define MD4_DIGEST_SIZE sizeof ( struct md4_digest ) diff --git a/src/include/ipxe/md5.h b/src/include/ipxe/md5.h index 05c3974c8..527ad3658 100644 --- a/src/include/ipxe/md5.h +++ b/src/include/ipxe/md5.h @@ -65,6 +65,9 @@ struct md5_context { /** MD5 context size */ #define MD5_CTX_SIZE sizeof ( struct md5_context ) +/** MD5 block size */ +#define MD5_BLOCK_SIZE sizeof ( union md5_block ) + /** MD5 digest size */ #define MD5_DIGEST_SIZE sizeof ( struct md5_digest ) diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 933f48530..637b20d60 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -127,6 +127,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Network controller */ #define PCI_CLASS_NETWORK 0x02 +/** Bridge device */ +#define PCI_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_PCI 0x04 /**< PCI-to-PCI bridge */ + /** Serial bus controller */ #define PCI_CLASS_SERIAL 0x0c #define PCI_CLASS_SERIAL_USB 0x03 /**< USB controller */ @@ -135,9 +139,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_CLASS_SERIAL_USB_EHCI 0x20 /**< ECHI USB controller */ #define PCI_CLASS_SERIAL_USB_XHCI 0x30 /**< xHCI USB controller */ +/** Primary bus number */ +#define PCI_PRIMARY 0x18 + +/** Secondary bus number */ +#define PCI_SECONDARY 0x19 + /** Subordinate bus number */ #define PCI_SUBORDINATE 0x1a +/** Memory base and limit */ +#define PCI_MEM_BASE 0x20 +#define PCI_MEM_LIMIT 0x22 +#define PCI_MEM_MASK 0x000f + /** Construct PCI class * * @v base Base class (or PCI_ANY_ID) @@ -262,9 +277,6 @@ struct pci_driver { #define PCI_BUS( busdevfn ) ( ( (busdevfn) >> 8 ) & 0xff ) #define PCI_SLOT( busdevfn ) ( ( (busdevfn) >> 3 ) & 0x1f ) #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) -#define PCI_BUSDEVFN( segment, bus, slot, func ) \ - ( ( (segment) << 16 ) | ( (bus) << 8 ) | \ - ( (slot) << 3 ) | ( (func) << 0 ) ) #define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) #define PCI_LAST_FUNC( busdevfn ) ( (busdevfn) | 0x07 ) @@ -301,7 +313,7 @@ extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ); extern int pci_read_config ( struct pci_device *pci ); -extern int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ); +extern int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ); extern int pci_find_driver ( struct pci_device *pci ); extern int pci_probe ( struct pci_device *pci ); extern void pci_remove ( struct pci_device *pci ); @@ -309,6 +321,7 @@ extern int pci_find_capability ( struct pci_device *pci, int capability ); extern int pci_find_next_capability ( struct pci_device *pci, int pos, int capability ); extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); +extern void pci_reset ( struct pci_device *pci, unsigned int exp ); /** * Initialise PCI device diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 2dcdd9b28..4c035b18b 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -14,6 +14,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/iomap.h> #include <config/ioapi.h> +struct pci_device; +struct pci_api; + +/** A PCI bus:dev.fn address range */ +struct pci_range { + /** Starting bus:dev.fn address */ + uint32_t start; + /** Number of bus:dev.fn addresses within this range */ + unsigned int count; +}; + +#define PCI_BUSDEVFN( segment, bus, slot, func ) \ + ( ( (segment) << 16 ) | ( (bus) << 8 ) | \ + ( (slot) << 3 ) | ( (func) << 0 ) ) + /** * Calculate static inline PCI I/O API function name * @@ -44,6 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); PROVIDE_SINGLE_API_INLINE ( PCIAPI_PREFIX_ ## _subsys, _api_func ) /* Include all architecture-independent I/O API headers */ +#include <ipxe/ecam_io.h> #include <ipxe/efi/efi_pci_api.h> #include <ipxe/linux/linux_pci.h> @@ -51,11 +67,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <bits/pci_io.h> /** - * Determine number of PCI buses within system + * Find next PCI bus:dev.fn address range in system * - * @ret num_bus Number of buses + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -int pci_num_bus ( void ); +void pci_discover ( uint32_t busdevfn, struct pci_range *range ); /** * Read byte from PCI configuration space @@ -133,4 +150,36 @@ int pci_write_config_dword ( struct pci_device *pci, unsigned int where, void * pci_ioremap ( struct pci_device *pci, unsigned long bus_addr, size_t len ); +/** A runtime selectable PCI I/O API */ +struct pci_api { + const char *name; + typeof ( pci_discover ) ( * pci_discover ); + typeof ( pci_read_config_byte ) ( * pci_read_config_byte ); + typeof ( pci_read_config_word ) ( * pci_read_config_word ); + typeof ( pci_read_config_dword ) ( * pci_read_config_dword ); + typeof ( pci_write_config_byte ) ( * pci_write_config_byte ); + typeof ( pci_write_config_word ) ( * pci_write_config_word ); + typeof ( pci_write_config_dword ) ( * pci_write_config_dword ); + typeof ( pci_ioremap ) ( * pci_ioremap ); +}; + +/** Provide a runtime selectable PCI I/O API */ +#define PCIAPI_RUNTIME( _subsys ) { \ + .name = #_subsys, \ + .pci_discover = PCIAPI_INLINE ( _subsys, pci_discover ), \ + .pci_read_config_byte = \ + PCIAPI_INLINE ( _subsys, pci_read_config_byte ), \ + .pci_read_config_word = \ + PCIAPI_INLINE ( _subsys, pci_read_config_word ), \ + .pci_read_config_dword = \ + PCIAPI_INLINE ( _subsys, pci_read_config_dword ), \ + .pci_write_config_byte = \ + PCIAPI_INLINE ( _subsys, pci_write_config_byte ), \ + .pci_write_config_word = \ + PCIAPI_INLINE ( _subsys, pci_write_config_word ), \ + .pci_write_config_dword = \ + PCIAPI_INLINE ( _subsys, pci_write_config_dword ), \ + .pci_ioremap = PCIAPI_INLINE ( _subsys, pci_ioremap ), \ + } + #endif /* _IPXE_PCI_IO_H */ diff --git a/src/include/ipxe/pcibridge.h b/src/include/ipxe/pcibridge.h new file mode 100644 index 000000000..c57a81067 --- /dev/null +++ b/src/include/ipxe/pcibridge.h @@ -0,0 +1,43 @@ +#ifndef _IPXE_PCIBRIDGE_H +#define _IPXE_PCIBRIDGE_H + +/** @file + * + * PCI-to-PCI bridge + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/list.h> +#include <ipxe/pci.h> + +/** A PCI-to-PCI bridge */ +struct pci_bridge { + /** PCI device */ + struct pci_device *pci; + /** Bridge numbers */ + union { + /** Raw dword */ + uint32_t buses; + struct { + /** Primary bus */ + uint8_t primary; + /** Secondary bus */ + uint8_t secondary; + /** Subordinate bus */ + uint8_t subordinate; + } __attribute__ (( packed )); + }; + /** Memory base */ + uint32_t membase; + /** Memory limit */ + uint32_t memlimit; + /** List of bridges */ + struct list_head list; +}; + +extern struct pci_bridge * pcibridge_find ( struct pci_device *pci ); + +#endif /* _IPXE_PCIBRIDGE_H */ diff --git a/src/include/ipxe/sha1.h b/src/include/ipxe/sha1.h index a97035ec7..9cbbebdee 100644 --- a/src/include/ipxe/sha1.h +++ b/src/include/ipxe/sha1.h @@ -65,6 +65,9 @@ struct sha1_context { /** SHA-1 context size */ #define SHA1_CTX_SIZE sizeof ( struct sha1_context ) +/** SHA-1 block size */ +#define SHA1_BLOCK_SIZE sizeof ( union sha1_block ) + /** SHA-1 digest size */ #define SHA1_DIGEST_SIZE sizeof ( struct sha1_digest ) diff --git a/src/include/ipxe/sha256.h b/src/include/ipxe/sha256.h index e234cce33..f226ad07b 100644 --- a/src/include/ipxe/sha256.h +++ b/src/include/ipxe/sha256.h @@ -70,6 +70,9 @@ struct sha256_context { /** SHA-256 context size */ #define SHA256_CTX_SIZE sizeof ( struct sha256_context ) +/** SHA-256 block size */ +#define SHA256_BLOCK_SIZE sizeof ( union sha256_block ) + /** SHA-256 digest size */ #define SHA256_DIGEST_SIZE sizeof ( struct sha256_digest ) diff --git a/src/include/ipxe/sha512.h b/src/include/ipxe/sha512.h index 8e22d8357..82a9e4e69 100644 --- a/src/include/ipxe/sha512.h +++ b/src/include/ipxe/sha512.h @@ -72,6 +72,9 @@ struct sha512_context { /** SHA-512 context size */ #define SHA512_CTX_SIZE sizeof ( struct sha512_context ) +/** SHA-512 block size */ +#define SHA512_BLOCK_SIZE sizeof ( union sha512_block ) + /** SHA-512 digest size */ #define SHA512_DIGEST_SIZE sizeof ( struct sha512_digest ) diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8b03579cc..6d6c82de0 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/iobuf.h> #include <ipxe/tables.h> +struct tls_connection; + /** A TLS header */ struct tls_header { /** Content type @@ -48,6 +50,9 @@ struct tls_header { /** TLS version 1.2 */ #define TLS_VERSION_TLS_1_2 0x0303 +/** Maximum supported TLS version */ +#define TLS_VERSION_MAX TLS_VERSION_TLS_1_2 + /** Change cipher content type */ #define TLS_TYPE_CHANGE_CIPHER 20 @@ -140,8 +145,23 @@ enum tls_tx_pending { TLS_TX_FINISHED = 0x0020, }; +/** A TLS key exchange algorithm */ +struct tls_key_exchange_algorithm { + /** Algorithm name */ + const char *name; + /** + * Transmit Client Key Exchange record + * + * @v tls TLS connection + * @ret rc Return status code + */ + int ( * exchange ) ( struct tls_connection *tls ); +}; + /** A TLS cipher suite */ struct tls_cipher_suite { + /** Key exchange algorithm */ + struct tls_key_exchange_algorithm *exchange; /** Public-key encryption algorithm */ struct pubkey_algorithm *pubkey; /** Bulk encryption cipher algorithm */ @@ -209,14 +229,6 @@ struct tls_signature_hash_algorithm { #define __tls_sig_hash_algorithm \ __table_entry ( TLS_SIG_HASH_ALGORITHMS, 01 ) -/** TLS pre-master secret */ -struct tls_pre_master_secret { - /** TLS version */ - uint16_t version; - /** Random data */ - uint8_t random[46]; -} __attribute__ (( packed )); - /** TLS client random data */ struct tls_client_random { /** GMT Unix time */ @@ -309,14 +321,16 @@ struct tls_connection { struct tls_cipherspec rx_cipherspec; /** Next RX cipher specification */ struct tls_cipherspec rx_cipherspec_pending; - /** Premaster secret */ - struct tls_pre_master_secret pre_master_secret; /** Master secret */ uint8_t master_secret[48]; /** Server random bytes */ uint8_t server_random[32]; /** Client random bytes */ struct tls_client_random client_random; + /** Server Key Exchange record (if any) */ + void *server_key; + /** Server Key Exchange record length */ + size_t server_key_len; /** MD5+SHA1 context for handshake verification */ uint8_t handshake_md5_sha1_ctx[MD5_SHA1_CTX_SIZE]; /** SHA256 context for handshake verification */ @@ -388,6 +402,9 @@ struct tls_connection { /** RX I/O buffer alignment */ #define TLS_RX_ALIGN 16 +extern struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm; +extern struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm; + extern int add_tls ( struct interface *xfer, const char *name, struct x509_root *root, struct private_key *key ); diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index fda4aba0e..19e341707 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -362,7 +362,7 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, return ioremap ( bus_addr, len ); } -PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); +PROVIDE_PCIAPI_INLINE ( efi, pci_discover ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c index 99c629c19..300844737 100644 --- a/src/interface/linux/linux_pci.c +++ b/src/interface/linux/linux_pci.c @@ -187,3 +187,12 @@ int linux_pci_write ( struct pci_device *pci, unsigned long where, err_open: return rc; } + +PROVIDE_PCIAPI_INLINE ( linux, pci_discover ); +PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( linux, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( linux, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( linux, pci_write_config_dword ); +PROVIDE_PCIAPI_INLINE ( linux, pci_ioremap ); diff --git a/src/net/80211/wpa_ccmp.c b/src/net/80211/wpa_ccmp.c index a073c6a3c..0abd217e7 100644 --- a/src/net/80211/wpa_ccmp.c +++ b/src/net/80211/wpa_ccmp.c @@ -478,16 +478,15 @@ struct net80211_crypto ccmp_crypto __net80211_crypto = { static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len, void *mic ) { - u8 sha1_ctx[SHA1_CTX_SIZE]; + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; u8 kckb[16]; u8 hash[SHA1_DIGEST_SIZE]; - size_t kck_len = 16; - memcpy ( kckb, kck, kck_len ); + memcpy ( kckb, kck, sizeof ( kckb ) ); - hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, msg, len ); - hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash ); + hmac_init ( &sha1_algorithm, ctx, kckb, sizeof ( kckb ) ); + hmac_update ( &sha1_algorithm, ctx, msg, len ); + hmac_final ( &sha1_algorithm, ctx, hash ); memcpy ( mic, hash, 16 ); } diff --git a/src/net/80211/wpa_tkip.c b/src/net/80211/wpa_tkip.c index 3b1934b59..3bd651512 100644 --- a/src/net/80211/wpa_tkip.c +++ b/src/net/80211/wpa_tkip.c @@ -545,15 +545,14 @@ struct net80211_crypto tkip_crypto __net80211_crypto = { static void tkip_kie_mic ( const void *kck, const void *msg, size_t len, void *mic ) { - uint8_t ctx[MD5_CTX_SIZE]; + uint8_t ctx[MD5_CTX_SIZE + MD5_BLOCK_SIZE]; u8 kckb[16]; - size_t kck_len = 16; - memcpy ( kckb, kck, kck_len ); + memcpy ( kckb, kck, sizeof ( kckb ) ); - hmac_init ( &md5_algorithm, ctx, kckb, &kck_len ); + hmac_init ( &md5_algorithm, ctx, kckb, sizeof ( kckb ) ); hmac_update ( &md5_algorithm, ctx, msg, len ); - hmac_final ( &md5_algorithm, ctx, kckb, &kck_len, mic ); + hmac_final ( &md5_algorithm, ctx, mic ); } /** diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 4b2c33eb4..901203c40 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -251,7 +251,7 @@ int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address, *prefix_mask = 0xff; } if ( remaining ) - *prefix_mask <<= ( 8 - remaining ); + *prefix_mask = ( 0xff << ( 8 - remaining ) ); } /* Add to start of routing table */ diff --git a/src/net/ndp.c b/src/net/ndp.c index 75e531648..c8e8ebad3 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -789,29 +789,43 @@ static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data, container_of ( ndpset->settings.parent, struct net_device, settings.settings ); struct ndp_prefix_information_option *prefix = prefset->prefix; - struct in6_addr ip6; + struct in6_addr *ip6 = &prefix->prefix; + struct in6_addr slaac; int prefix_len; + int rc; /* Skip dead prefixes */ if ( ! prefix->valid ) return -ENOENT; /* Construct IPv6 address via SLAAC, if applicable */ - memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) ); if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) { - prefix_len = ipv6_eui64 ( &ip6, netdev ); - if ( prefix_len < 0 ) - return prefix_len; - if ( prefix_len != prefix->prefix_len ) - return -EINVAL; + memcpy ( &slaac, ip6, sizeof ( slaac ) ); + prefix_len = ipv6_eui64 ( &slaac, netdev ); + if ( prefix_len == prefix->prefix_len ) { + /* Correctly configured prefix: use SLAAC address */ + ip6 = &slaac; + } else if ( prefix_len < 0 ) { + /* Link layer does not support SLAAC */ + rc = prefix_len; + DBGC ( netdev, "NDP %s does not support SLAAC: %s\n", + netdev->name, strerror ( rc ) ); + } else { + /* Prefix length incorrect: assume a badly + * configured router and ignore SLAAC address. + */ + DBGC ( netdev, "NDP %s ignoring misconfigured SLAAC " + "on prefix %s/%d\n", netdev->name, + inet6_ntoa ( ip6 ), prefix->prefix_len ); + } } /* Fill in IPv6 address */ - if ( len > sizeof ( ip6 ) ) - len = sizeof ( ip6 ); - memcpy ( data, &ip6, len ); + if ( len > sizeof ( *ip6 ) ) + len = sizeof ( *ip6 ); + memcpy ( data, ip6, len ); - return sizeof ( ip6 ); + return sizeof ( *ip6 ); } /** diff --git a/src/net/pccrc.c b/src/net/pccrc.c index 4cd82cd1c..a94bc0e11 100644 --- a/src/net/pccrc.c +++ b/src/net/pccrc.c @@ -104,9 +104,8 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, const void *hash, const void *secret ){ const struct peerdist_info *info = segment->info; struct digest_algorithm *digest = info->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; size_t digestsize = info->digestsize; - size_t secretsize = digestsize; static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; /* Sanity check */ @@ -121,12 +120,10 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, memcpy ( segment->secret, secret, digestsize ); /* Calculate segment identifier */ - hmac_init ( digest, ctx, segment->secret, &secretsize ); - assert ( secretsize == digestsize ); + hmac_init ( digest, ctx, segment->secret, digestsize ); hmac_update ( digest, ctx, segment->hash, digestsize ); hmac_update ( digest, ctx, magic, sizeof ( magic ) ); - hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id ); - assert ( secretsize == digestsize ); + hmac_final ( digest, ctx, segment->id ); } /****************************************************************************** diff --git a/src/net/tls.c b/src/net/tls.c index 3c4144450..4aa4d9e29 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/rbg.h> #include <ipxe/validator.h> #include <ipxe/job.h> +#include <ipxe/dhe.h> #include <ipxe/tls.h> #include <config/crypto.h> @@ -109,6 +110,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EINVAL_TICKET \ __einfo_uniqify ( EINFO_EINVAL, 0x0e, \ "Invalid New Session Ticket record") +#define EINVAL_KEY_EXCHANGE __einfo_error ( EINFO_EINVAL_KEY_EXCHANGE ) +#define EINFO_EINVAL_KEY_EXCHANGE \ + __einfo_uniqify ( EINFO_EINVAL, 0x0f, \ + "Invalid Server Key Exchange record" ) #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ __einfo_uniqify ( EINFO_EIO, 0x01, \ @@ -177,6 +182,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_RENEG_VERIFY \ __einfo_uniqify ( EINFO_EPERM, 0x05, \ "Secure renegotiation verification failed" ) +#define EPERM_KEY_EXCHANGE __einfo_error ( EINFO_EPERM_KEY_EXCHANGE ) +#define EINFO_EPERM_KEY_EXCHANGE \ + __einfo_uniqify ( EINFO_EPERM, 0x06, \ + "ServerKeyExchange verification failed" ) #define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) #define EINFO_EPROTO_VERSION \ __einfo_uniqify ( EINFO_EPROTO, 0x01, \ @@ -377,6 +386,7 @@ static void free_tls ( struct refcnt *refcnt ) { tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); + free ( tls->server_key ); list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); @@ -458,17 +468,17 @@ static int tls_generate_random ( struct tls_connection *tls, * Update HMAC with a list of ( data, len ) pairs * * @v digest Hash function to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v args ( data, len ) pairs of data, terminated by NULL */ static void tls_hmac_update_va ( struct digest_algorithm *digest, - void *digest_ctx, va_list args ) { + void *ctx, va_list args ) { void *data; size_t len; while ( ( data = va_arg ( args, void * ) ) ) { len = va_arg ( args, size_t ); - hmac_update ( digest, digest_ctx, data, len ); + hmac_update ( digest, ctx, data, len ); } } @@ -485,43 +495,38 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, */ static void tls_p_hash_va ( struct tls_connection *tls, struct digest_algorithm *digest, - void *secret, size_t secret_len, + const void *secret, size_t secret_len, void *out, size_t out_len, va_list seeds ) { - uint8_t secret_copy[secret_len]; - uint8_t digest_ctx[digest->ctxsize]; - uint8_t digest_ctx_partial[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; + uint8_t ctx_partial[ sizeof ( ctx ) ]; uint8_t a[digest->digestsize]; uint8_t out_tmp[digest->digestsize]; size_t frag_len = digest->digestsize; va_list tmp; - /* Copy the secret, in case HMAC modifies it */ - memcpy ( secret_copy, secret, secret_len ); - secret = secret_copy; DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name ); DBGC2_HD ( tls, secret, secret_len ); /* Calculate A(1) */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); + hmac_init ( digest, ctx, secret, secret_len ); va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); + tls_hmac_update_va ( digest, ctx, tmp ); va_end ( tmp ); - hmac_final ( digest, digest_ctx, secret, &secret_len, a ); + hmac_final ( digest, ctx, a ); DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name ); DBGC2_HD ( tls, &a, sizeof ( a ) ); /* Generate as much data as required */ while ( out_len ) { /* Calculate output portion */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); - hmac_update ( digest, digest_ctx, a, sizeof ( a ) ); - memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize ); + hmac_init ( digest, ctx, secret, secret_len ); + hmac_update ( digest, ctx, a, sizeof ( a ) ); + memcpy ( ctx_partial, ctx, sizeof ( ctx_partial ) ); va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); + tls_hmac_update_va ( digest, ctx, tmp ); va_end ( tmp ); - hmac_final ( digest, digest_ctx, - secret, &secret_len, out_tmp ); + hmac_final ( digest, ctx, out_tmp ); /* Copy output */ if ( frag_len > out_len ) @@ -531,8 +536,7 @@ static void tls_p_hash_va ( struct tls_connection *tls, DBGC2_HD ( tls, out, frag_len ); /* Calculate A(i) */ - hmac_final ( digest, digest_ctx_partial, - secret, &secret_len, a ); + hmac_final ( digest, ctx_partial, a ); DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name ); DBGC2_HD ( tls, &a, sizeof ( a ) ); @@ -551,13 +555,13 @@ static void tls_p_hash_va ( struct tls_connection *tls, * @v out_len Length of output buffer * @v ... ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_prf ( struct tls_connection *tls, void *secret, +static void tls_prf ( struct tls_connection *tls, const void *secret, size_t secret_len, void *out, size_t out_len, ... ) { va_list seeds; va_list tmp; size_t subsecret_len; - void *md5_secret; - void *sha1_secret; + const void *md5_secret; + const void *sha1_secret; uint8_t buf[out_len]; unsigned int i; @@ -622,21 +626,23 @@ static void tls_prf ( struct tls_connection *tls, void *secret, * Generate master secret * * @v tls TLS connection + * @v pre_master_secret Pre-master secret + * @v pre_master_secret_len Length of pre-master secret * - * The pre-master secret and the client and server random values must - * already be known. + * The client and server random values must already be known. */ -static void tls_generate_master_secret ( struct tls_connection *tls ) { +static void tls_generate_master_secret ( struct tls_connection *tls, + const void *pre_master_secret, + size_t pre_master_secret_len ) { + DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); - DBGC_HD ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ) ); + DBGC_HD ( tls, pre_master_secret, pre_master_secret_len ); DBGC ( tls, "TLS %p client random bytes:\n", tls ); DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) ); DBGC ( tls, "TLS %p server random bytes:\n", tls ); DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) ); - tls_prf_label ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), + tls_prf_label ( tls, pre_master_secret, pre_master_secret_len, &tls->master_secret, sizeof ( tls->master_secret ), "master secret", &tls->client_random, sizeof ( tls->client_random ), @@ -737,6 +743,7 @@ static int tls_generate_keys ( struct tls_connection *tls ) { /** Null cipher suite */ struct tls_cipher_suite tls_cipher_suite_null = { + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &pubkey_null, .cipher = &cipher_null, .digest = &digest_null, @@ -852,7 +859,8 @@ static int tls_select_cipher ( struct tls_connection *tls, suite ) ) != 0 ) return rc; - DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, suite->pubkey->name, + DBGC ( tls, "TLS %p selected %s-%s-%s-%d-%s\n", tls, + suite->exchange->name, suite->pubkey->name, suite->cipher->name, ( suite->key_len * 8 ), suite->digest->name ); @@ -916,6 +924,44 @@ tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, return NULL; } +/** + * Find TLS signature algorithm + * + * @v code Signature and hash algorithm identifier + * @ret pubkey Public key algorithm, or NULL + */ +static struct pubkey_algorithm * +tls_signature_hash_pubkey ( struct tls_signature_hash_id code ) { + struct tls_signature_hash_algorithm *sig_hash; + + /* Identify signature and hash algorithm */ + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { + if ( sig_hash->code.signature == code.signature ) + return sig_hash->pubkey; + } + + return NULL; +} + +/** + * Find TLS hash algorithm + * + * @v code Signature and hash algorithm identifier + * @ret digest Digest algorithm, or NULL + */ +static struct digest_algorithm * +tls_signature_hash_digest ( struct tls_signature_hash_id code ) { + struct tls_signature_hash_algorithm *sig_hash; + + /* Identify signature and hash algorithm */ + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { + if ( sig_hash->code.hash == code.hash ) + return sig_hash->digest; + } + + return NULL; +} + /****************************************************************************** * * Handshake verification @@ -1208,16 +1254,20 @@ static int tls_send_certificate ( struct tls_connection *tls ) { } /** - * Transmit Client Key Exchange record + * Transmit Client Key Exchange record using public key exchange * * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_key_exchange ( struct tls_connection *tls ) { +static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); struct { + uint16_t version; + uint8_t random[46]; + } __attribute__ (( packed )) pre_master_secret; + struct { uint32_t type_length; uint16_t encrypted_pre_master_secret_len; uint8_t encrypted_pre_master_secret[max_len]; @@ -1226,11 +1276,28 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { int len; int rc; + /* Generate pre-master secret */ + pre_master_secret.version = htons ( TLS_VERSION_MAX ); + if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random, + ( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) { + return rc; + } + + /* Generate master secret */ + tls_generate_master_secret ( tls, &pre_master_secret, + sizeof ( pre_master_secret ) ); + + /* Generate keys */ + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate keys: %s\n", + tls, strerror ( rc ) ); + return rc; + } + /* Encrypt pre-master secret using server's public key */ memset ( &key_xchg, 0, sizeof ( key_xchg ) ); len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, - &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), + &pre_master_secret, sizeof ( pre_master_secret ), key_xchg.encrypted_pre_master_secret ); if ( len < 0 ) { rc = len; @@ -1251,6 +1318,225 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { ( sizeof ( key_xchg ) - unused ) ); } +/** Public key exchange algorithm */ +struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { + .name = "pubkey", + .exchange = tls_send_client_key_exchange_pubkey, +}; + +/** + * Transmit Client Key Exchange record using DHE key exchange + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { + struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct pubkey_algorithm *pubkey; + struct digest_algorithm *digest; + int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 ); + uint8_t private[ sizeof ( tls->client_random.random ) ]; + const struct { + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *dh_val[3]; + const struct { + struct tls_signature_hash_id sig_hash[use_sig_hash]; + uint16_t signature_len; + uint8_t signature[0]; + } __attribute__ (( packed )) *sig; + const void *data; + size_t remaining; + size_t frag_len; + unsigned int i; + int rc; + + /* Parse ServerKeyExchange */ + data = tls->server_key; + remaining = tls->server_key_len; + for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){ + dh_val[i] = data; + if ( ( sizeof ( *dh_val[i] ) > remaining ) || + ( ntohs ( dh_val[i]->len ) > ( remaining - + sizeof ( *dh_val[i] ) ) )){ + DBGC ( tls, "TLS %p received underlength " + "ServerKeyExchange\n", tls ); + DBGC_HDA ( tls, 0, tls->server_key, + tls->server_key_len ); + rc = -EINVAL_KEY_EXCHANGE; + goto err_header; + } + frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len )); + data += frag_len; + remaining -= frag_len; + } + sig = data; + if ( ( sizeof ( *sig ) > remaining ) || + ( ntohs ( sig->signature_len ) > ( remaining - + sizeof ( *sig ) ) ) ) { + DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", + tls ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + rc = -EINVAL_KEY_EXCHANGE; + goto err_header; + } + + /* Identify signature and hash algorithm */ + if ( use_sig_hash ) { + pubkey = tls_signature_hash_pubkey ( sig->sig_hash[0] ); + digest = tls_signature_hash_digest ( sig->sig_hash[0] ); + if ( ( ! pubkey ) || ( ! digest ) ) { + DBGC ( tls, "TLS %p ServerKeyExchange unsupported " + "signature and hash algorithm\n", tls ); + rc = -ENOTSUP_SIG_HASH; + goto err_sig_hash; + } + if ( pubkey != cipherspec->suite->pubkey ) { + DBGC ( tls, "TLS %p ServerKeyExchange incorrect " + "signature algorithm %s (expected %s)\n", tls, + pubkey->name, cipherspec->suite->pubkey->name ); + rc = -EPERM_KEY_EXCHANGE; + goto err_sig_hash; + } + } else { + pubkey = cipherspec->suite->pubkey; + digest = &md5_sha1_algorithm; + } + + /* Verify signature */ + { + const void *signature = sig->signature; + size_t signature_len = ntohs ( sig->signature_len ); + uint8_t ctx[digest->ctxsize]; + uint8_t hash[digest->digestsize]; + + /* Calculate digest */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, &tls->client_random, + sizeof ( tls->client_random ) ); + digest_update ( digest, ctx, tls->server_random, + sizeof ( tls->server_random ) ); + digest_update ( digest, ctx, tls->server_key, + ( tls->server_key_len - remaining ) ); + digest_final ( digest, ctx, hash ); + + /* Verify signature */ + if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx, + digest, hash, signature, + signature_len ) ) != 0 ) { + DBGC ( tls, "TLS %p ServerKeyExchange failed " + "verification\n", tls ); + DBGC_HDA ( tls, 0, tls->server_key, + tls->server_key_len ); + rc = -EPERM_KEY_EXCHANGE; + goto err_verify; + } + } + + /* Generate Diffie-Hellman private key */ + if ( ( rc = tls_generate_random ( tls, private, + sizeof ( private ) ) ) != 0 ) { + goto err_random; + } + + /* Construct pre-master secret and ClientKeyExchange record */ + { + typeof ( dh_val[0] ) dh_p = dh_val[0]; + typeof ( dh_val[1] ) dh_g = dh_val[1]; + typeof ( dh_val[2] ) dh_ys = dh_val[2]; + size_t len = ntohs ( dh_p->len ); + struct { + uint32_t type_length; + uint16_t dh_xs_len; + uint8_t dh_xs[len]; + } __attribute__ (( packed )) *key_xchg; + struct { + uint8_t pre_master_secret[len]; + typeof ( *key_xchg ) key_xchg; + } *dynamic; + uint8_t *pre_master_secret; + + /* Allocate space */ + dynamic = malloc ( sizeof ( *dynamic ) ); + if ( ! dynamic ) { + rc = -ENOMEM; + goto err_alloc; + } + pre_master_secret = dynamic->pre_master_secret; + key_xchg = &dynamic->key_xchg; + key_xchg->type_length = + ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | + htonl ( sizeof ( *key_xchg ) - + sizeof ( key_xchg->type_length ) ) ); + key_xchg->dh_xs_len = htons ( len ); + + /* Calculate pre-master secret and client public value */ + if ( ( rc = dhe_key ( dh_p->data, len, + dh_g->data, ntohs ( dh_g->len ), + dh_ys->data, ntohs ( dh_ys->len ), + private, sizeof ( private ), + key_xchg->dh_xs, + pre_master_secret ) ) != 0 ) { + DBGC ( tls, "TLS %p could not calculate DHE key: %s\n", + tls, strerror ( rc ) ); + goto err_dhe_key; + } + + /* Strip leading zeroes from pre-master secret */ + while ( len && ( ! *pre_master_secret ) ) { + pre_master_secret++; + len--; + } + + /* Generate master secret */ + tls_generate_master_secret ( tls, pre_master_secret, len ); + + /* Generate keys */ + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate keys: %s\n", + tls, strerror ( rc ) ); + goto err_generate_keys; + } + + /* Transmit Client Key Exchange record */ + if ( ( rc = tls_send_handshake ( tls, key_xchg, + sizeof ( *key_xchg ) ) ) !=0){ + goto err_send_handshake; + } + + err_send_handshake: + err_generate_keys: + err_dhe_key: + free ( dynamic ); + } + err_alloc: + err_random: + err_verify: + err_sig_hash: + err_header: + return rc; +} + +/** Ephemeral Diffie-Hellman key exchange algorithm */ +struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = { + .name = "dhe", + .exchange = tls_send_client_key_exchange_dhe, +}; + +/** + * Transmit Client Key Exchange record + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange ( struct tls_connection *tls ) { + struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipher_suite *suite = cipherspec->suite; + + /* Transmit Client Key Exchange record via key exchange algorithm */ + return suite->exchange->exchange ( tls ); +} + /** * Transmit Certificate Verify record * @@ -1628,7 +1914,7 @@ static int tls_new_server_hello ( struct tls_connection *tls, if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) return rc; - /* Reuse or generate master secret */ + /* Check session ID */ if ( hello_a->session_id_len && ( hello_a->session_id_len == tls->session_id_len ) && ( memcmp ( session_id, tls->session_id, @@ -1637,12 +1923,11 @@ static int tls_new_server_hello ( struct tls_connection *tls, /* 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 ); + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) + return rc; } 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 ))){ @@ -1655,10 +1940,6 @@ static int tls_new_server_hello ( struct tls_connection *tls, } } - /* Generate keys */ - if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) - return rc; - /* Handle secure renegotiation */ if ( tls->secure_renegotiation ) { @@ -1856,6 +2137,37 @@ static int tls_new_certificate ( struct tls_connection *tls, } /** + * Receive new Server Key Exchange 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_server_key_exchange ( struct tls_connection *tls, + const void *data, size_t len ) { + + /* Free any existing server key exchange record */ + free ( tls->server_key ); + tls->server_key_len = 0; + + /* Allocate copy of server key exchange record */ + tls->server_key = malloc ( len ); + if ( ! tls->server_key ) + return -ENOMEM; + + /* Store copy of server key exchange record for later + * processing. We cannot verify the signature at this point + * since the certificate validation will not yet have + * completed. + */ + memcpy ( tls->server_key, data, len ); + tls->server_key_len = len; + + return 0; +} + +/** * Receive new Certificate Request handshake record * * @v tls TLS connection @@ -2088,6 +2400,10 @@ static int tls_new_handshake ( struct tls_connection *tls, case TLS_CERTIFICATE: rc = tls_new_certificate ( tls, payload, payload_len ); break; + case TLS_SERVER_KEY_EXCHANGE: + rc = tls_new_server_key_exchange ( tls, payload, + payload_len ); + break; case TLS_CERTIFICATE_REQUEST: rc = tls_new_certificate_request ( tls, payload, payload_len ); @@ -2213,7 +2529,7 @@ static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx, uint64_t seq, struct tls_header *tlshdr ) { struct digest_algorithm *digest = cipherspec->suite->digest; - hmac_init ( digest, ctx, cipherspec->mac_secret, &digest->digestsize ); + hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize ); seq = cpu_to_be64 ( seq ); hmac_update ( digest, ctx, &seq, sizeof ( seq ) ); hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) ); @@ -2245,8 +2561,7 @@ static void tls_hmac_final ( struct tls_cipherspec *cipherspec, void *ctx, void *hmac ) { struct digest_algorithm *digest = cipherspec->suite->digest; - hmac_final ( digest, ctx, cipherspec->mac_secret, - &digest->digestsize, hmac ); + hmac_final ( digest, ctx, hmac ); } /** @@ -2263,7 +2578,7 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, uint64_t seq, struct tls_header *tlshdr, const void *data, size_t len, void *hmac ) { struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; tls_hmac_init ( cipherspec, ctx, seq, tlshdr ); tls_hmac_update ( cipherspec, ctx, data, len ); @@ -2545,7 +2860,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; struct cipher_algorithm *cipher = cipherspec->suite->cipher; struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; struct io_buffer *iobuf; void *mac; @@ -3175,7 +3490,7 @@ int add_tls ( struct interface *xfer, const char *name, &tls->refcnt ); tls->key = privkey_get ( key ? key : &private_key ); tls->root = x509_root_get ( root ? root : &root_certificates ); - tls->version = TLS_VERSION_TLS_1_2; + tls->version = TLS_VERSION_MAX; tls_clear_cipher ( tls, &tls->tx_cipherspec ); tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); @@ -3188,11 +3503,6 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { goto err_random; } - tls->pre_master_secret.version = htons ( tls->version ); - if ( ( rc = tls_generate_random ( tls, &tls->pre_master_secret.random, - ( 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 ); diff --git a/src/tests/dhe_test.c b/src/tests/dhe_test.c new file mode 100644 index 000000000..ea97ec7f4 --- /dev/null +++ b/src/tests/dhe_test.c @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + * + * 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 ); + +/** @file + * + * Ephemeral Diffie-Hellman self-tests + * + * NIST test vectors are taken from + * + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/keymgmt/KASTestVectorsFFC2014.zip + * + * using the files in the "KASTestVectorsFFC2014/Test of 800-56A + * excluding KDF/FFC Ephemeral Scheme" subdirectory + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stdint.h> +#include <string.h> +#include <ipxe/dhe.h> +#include <ipxe/test.h> + +/** Define inline prime modulus data */ +#define MODULUS(...) { __VA_ARGS__ } + +/** Define inline generator data */ +#define GENERATOR(...) { __VA_ARGS__ } + +/** Define inline partner public key data */ +#define PARTNER(...) { __VA_ARGS__ } + +/** Define inline private key data */ +#define PRIVATE(...) { __VA_ARGS__ } + +/** Define inline public key data */ +#define PUBLIC(...) { __VA_ARGS__ } + +/** Define inline shared secret data */ +#define SHARED(...) { __VA_ARGS__ } + +/** An Ephemeral Diffie-Hellman self-test */ +struct dhe_test { + /** Prime modulus */ + const void *modulus; + /** Length of prime modulus */ + size_t len; + /** Generator */ + const void *generator; + /** Length of generator */ + size_t generator_len; + /** Partner public key */ + const void *partner; + /** Length of partner public key */ + size_t partner_len; + /** Private key */ + const void *private; + /** Length of private key */ + size_t private_len; + /** Expected public key */ + const void *public; + /** Length of expected public key */ + size_t public_len; + /** Expected shared secret */ + const void *shared; + /** Length of shared secret */ + size_t shared_len; +}; + +/** + * Define an Ephemeral Diffie-Hellman test + * + * @v name Test name + * @v MODULUS Prime modulus + * @v GENERATOR Generator + * @v PARTNER Partner public key + * @v PRIVATE Private key + * @v PUBLIC Expected public key + * @v SHARED Expected shared secret + * @ret test Ephemeral Diffie-Hellman test + */ +#define DHE_TEST( name, MODULUS, GENERATOR, PARTNER, PRIVATE, PUBLIC, \ + SHARED ) \ + static const uint8_t name ## _modulus[] = MODULUS; \ + static const uint8_t name ## _generator[] = GENERATOR; \ + static const uint8_t name ## _partner[] = PARTNER; \ + static const uint8_t name ## _private[] = PRIVATE; \ + static const uint8_t name ## _public[] = PUBLIC; \ + static const uint8_t name ## _shared[] = SHARED; \ + static struct dhe_test name = { \ + .modulus = name ## _modulus, \ + .len = sizeof ( name ## _modulus ), \ + .generator = name ## _generator, \ + .generator_len = sizeof ( name ## _generator ), \ + .partner = name ## _partner, \ + .partner_len = sizeof ( name ## _partner ), \ + .private = name ## _private, \ + .private_len = sizeof ( name ## _private ), \ + .public = name ## _public, \ + .public_len = sizeof ( name ## _public ), \ + .shared = name ## _shared, \ + .shared_len = sizeof ( name ## _shared ), \ + } + +/** + * Report an Ephemeral Diffie-Hellman test result + * + * @v test Ephemeral Diffie-Hellman test + * @v file Test code file + * @v line Test code line + */ +static void dhe_key_okx ( struct dhe_test *test, const char *file, + unsigned int line ) { + uint8_t public[test->len]; + uint8_t shared[test->len]; + + /* Sanity checks */ + okx ( test->generator_len <= test->len, file, line ); + okx ( test->partner_len <= test->len, file, line ); + okx ( test->private_len <= test->len, file, line ); + okx ( test->public_len == test->len, file, line ); + okx ( test->shared_len == test->len, file, line ); + + /* Calculate Diffie-Hellman key */ + okx ( dhe_key ( test->modulus, test->len, test->generator, + test->generator_len, test->partner, test->partner_len, + test->private, test->private_len, public, + shared ) == 0, file, line ); + + /* Compare against expected result */ + okx ( memcmp ( public, test->public, test->len ) == 0, file, line ); + okx ( memcmp ( shared, test->shared, test->len ) == 0, file, line ); +} +#define dhe_key_ok( test ) dhe_key_okx ( test, __FILE__, __LINE__ ) + +/* KASValidityTest_FFCEphem_NOKC_ZZOnly_init.fax test 0 */ +DHE_TEST ( kasvaliditytest_ffcephem_nokc_zzonly_init_fb_0, + MODULUS ( 0xc5, 0x7c, 0xa2, 0x4f, 0x4b, 0xd6, 0x8c, 0x3c, 0xda, + 0xc7, 0xba, 0xaa, 0xea, 0x2e, 0x5c, 0x1e, 0x18, 0xb2, + 0x7b, 0x8c, 0x55, 0x65, 0x9f, 0xea, 0xe0, 0xa1, 0x36, + 0x53, 0x2b, 0x36, 0xe0, 0x4e, 0x3e, 0x64, 0xa9, 0xe4, + 0xfc, 0x8f, 0x32, 0x62, 0x97, 0xe4, 0xbe, 0xf7, 0xc1, + 0xde, 0x07, 0x5a, 0x89, 0x28, 0xf3, 0xfe, 0x4f, 0xfe, + 0x68, 0xbc, 0xfb, 0x0a, 0x7c, 0xa4, 0xb3, 0x14, 0x48, + 0x89, 0x9f, 0xaf, 0xb8, 0x43, 0xe2, 0xa0, 0x62, 0x5c, + 0xb4, 0x88, 0x3f, 0x06, 0x50, 0x11, 0xfe, 0x65, 0x8d, + 0x49, 0xd2, 0xf5, 0x4b, 0x74, 0x79, 0xdb, 0x06, 0x62, + 0x92, 0x89, 0xed, 0xda, 0xcb, 0x87, 0x37, 0x16, 0xd2, + 0xa1, 0x7a, 0xe8, 0xde, 0x92, 0xee, 0x3e, 0x41, 0x4a, + 0x91, 0x5e, 0xed, 0xf3, 0x6c, 0x6b, 0x7e, 0xfd, 0x15, + 0x92, 0x18, 0xfc, 0xa7, 0xac, 0x42, 0x85, 0x57, 0xe9, + 0xdc, 0xda, 0x55, 0xc9, 0x8b, 0x28, 0x9e, 0xc1, 0xc4, + 0x46, 0x4d, 0x88, 0xed, 0x62, 0x8e, 0xdb, 0x3f, 0xb9, + 0xd7, 0xc8, 0xe3, 0xcf, 0xb8, 0x34, 0x2c, 0xd2, 0x6f, + 0x28, 0x06, 0x41, 0xe3, 0x66, 0x8c, 0xfc, 0x72, 0xff, + 0x26, 0x3b, 0x6b, 0x6c, 0x6f, 0x73, 0xde, 0xf2, 0x90, + 0x29, 0xe0, 0x61, 0x32, 0xc4, 0x12, 0x74, 0x09, 0x52, + 0xec, 0xf3, 0x1b, 0xa6, 0x45, 0x98, 0xac, 0xf9, 0x1c, + 0x65, 0x8e, 0x3a, 0x91, 0x84, 0x4b, 0x23, 0x8a, 0xb2, + 0x3c, 0xc9, 0xfa, 0xea, 0xf1, 0x38, 0xce, 0xd8, 0x05, + 0xe0, 0xfa, 0x44, 0x68, 0x1f, 0xeb, 0xd9, 0x57, 0xb8, + 0x4a, 0x97, 0x5b, 0x88, 0xc5, 0xf1, 0xbb, 0xb0, 0x49, + 0xc3, 0x91, 0x7c, 0xd3, 0x13, 0xb9, 0x47, 0xbb, 0x91, + 0x8f, 0xe5, 0x26, 0x07, 0xab, 0xa9, 0xc5, 0xd0, 0x3d, + 0x95, 0x41, 0x26, 0x92, 0x9d, 0x13, 0x67, 0xf2, 0x7e, + 0x11, 0x88, 0xdc, 0x2d ), + GENERATOR ( 0x4a, 0x1a, 0xf3, 0xa4, 0x92, 0xe9, 0xee, 0x74, 0x6e, + 0x57, 0xd5, 0x8c, 0x2c, 0x5b, 0x41, 0x41, 0x5e, 0xd4, + 0x55, 0x19, 0xdc, 0xd9, 0x32, 0x91, 0xf7, 0xfd, 0xc2, + 0x57, 0xff, 0x03, 0x14, 0xdb, 0xf1, 0xb7, 0x60, 0x0c, + 0x43, 0x59, 0x3f, 0xff, 0xac, 0xf1, 0x80, 0x9a, 0x15, + 0x6f, 0xd8, 0x6e, 0xb7, 0x85, 0x18, 0xc8, 0xec, 0x4e, + 0x59, 0x4a, 0xe2, 0x91, 0x43, 0x4c, 0xeb, 0x95, 0xb6, + 0x2e, 0x9a, 0xea, 0x53, 0x68, 0x80, 0x64, 0x69, 0x40, + 0xf9, 0xec, 0xbd, 0x85, 0x89, 0x26, 0x97, 0x67, 0xaf, + 0xb0, 0xad, 0x00, 0x1b, 0xd4, 0xfd, 0x94, 0xd3, 0xe9, + 0x92, 0xb1, 0xb4, 0xbc, 0x5a, 0xaa, 0x92, 0x80, 0x89, + 0x3b, 0x39, 0x05, 0x6c, 0x22, 0x26, 0xfe, 0x5a, 0x28, + 0x6c, 0x37, 0x50, 0x5a, 0x38, 0x99, 0xcf, 0xf3, 0xc1, + 0x96, 0x45, 0xdc, 0x01, 0xcb, 0x20, 0x87, 0xa5, 0x00, + 0x8c, 0xf5, 0x4d, 0xc2, 0xef, 0xb8, 0x9b, 0xd1, 0x87, + 0xbe, 0xed, 0xd5, 0x0a, 0x29, 0x15, 0x34, 0x59, 0x4c, + 0x3a, 0x05, 0x22, 0x05, 0x44, 0x4f, 0x9f, 0xc8, 0x47, + 0x12, 0x24, 0x8e, 0xa8, 0x79, 0xe4, 0x67, 0xba, 0x4d, + 0x5b, 0x75, 0x56, 0x95, 0xeb, 0xe8, 0x8a, 0xfa, 0x8e, + 0x01, 0x8c, 0x1b, 0x74, 0x63, 0xd9, 0x2f, 0xf7, 0xd3, + 0x44, 0x8f, 0xa8, 0xf5, 0xaf, 0x6c, 0x4f, 0xdb, 0xe7, + 0xc9, 0x6c, 0x71, 0x22, 0xa3, 0x1d, 0xf1, 0x40, 0xb2, + 0xe0, 0x9a, 0xb6, 0x72, 0xc9, 0xc0, 0x13, 0x16, 0xa2, + 0x4a, 0xe1, 0x92, 0xc7, 0x54, 0x23, 0xab, 0x9d, 0xa1, + 0xa1, 0xe5, 0x0b, 0xed, 0xba, 0xe8, 0x84, 0x37, 0xb2, + 0xe7, 0xfe, 0x32, 0x8d, 0xfa, 0x1c, 0x53, 0x77, 0x97, + 0xc7, 0xf3, 0x48, 0xc9, 0xdb, 0x2d, 0x75, 0x52, 0x9d, + 0x42, 0x51, 0x78, 0x62, 0x68, 0x05, 0x45, 0x15, 0xf8, + 0xa2, 0x4e, 0xf3, 0x0b ), + PARTNER ( 0x7d, 0x06, 0x60, 0xbf, 0xce, 0x9e, 0x72, 0x63, 0xf6, + 0xbf, 0xc7, 0x9b, 0xca, 0x58, 0xfe, 0xca, 0x71, 0x31, + 0x07, 0x7d, 0x31, 0xa9, 0x64, 0xdd, 0x3a, 0x2a, 0x9a, + 0x49, 0x34, 0x20, 0xfb, 0x49, 0x62, 0xbe, 0x61, 0xae, + 0xfa, 0x37, 0x63, 0x53, 0x54, 0x56, 0x36, 0x71, 0xf6, + 0x61, 0x86, 0xe8, 0x09, 0x6e, 0x6e, 0x88, 0x81, 0x60, + 0xce, 0x1d, 0xdc, 0xf1, 0xb5, 0xfe, 0x4b, 0xf5, 0xc9, + 0xf6, 0xc9, 0x9a, 0x63, 0x01, 0x5d, 0x17, 0x23, 0xad, + 0xcd, 0x8a, 0x2a, 0xeb, 0xd4, 0x84, 0x7a, 0xb0, 0x22, + 0x89, 0x6f, 0x81, 0x07, 0x11, 0x4c, 0xbc, 0xf3, 0x4e, + 0xa3, 0x24, 0x35, 0xd5, 0xa2, 0x68, 0x9f, 0x73, 0x56, + 0xd3, 0x89, 0x4a, 0xea, 0xfd, 0x9a, 0xd8, 0x0b, 0xaa, + 0xb0, 0xfd, 0xd2, 0x67, 0x15, 0x40, 0xa5, 0x9b, 0x2f, + 0xc7, 0x89, 0xfd, 0x0b, 0xe4, 0x15, 0x43, 0x57, 0xdf, + 0x8d, 0x7f, 0x99, 0x6f, 0x20, 0x76, 0xe9, 0x63, 0xfa, + 0x59, 0xd5, 0xff, 0xd9, 0xfe, 0x8a, 0x00, 0x6c, 0xe0, + 0x9c, 0x05, 0x2e, 0xbe, 0xbc, 0xc6, 0xdb, 0x71, 0xb7, + 0x77, 0x8f, 0xb0, 0xd3, 0x03, 0x0f, 0x18, 0xad, 0x26, + 0x04, 0xd3, 0x15, 0x2a, 0x20, 0x7f, 0x66, 0x25, 0xa6, + 0x31, 0x21, 0xbd, 0x02, 0x17, 0xa4, 0x42, 0x48, 0x56, + 0xd2, 0x16, 0x7a, 0xf8, 0xfe, 0xa3, 0xe7, 0x7d, 0x20, + 0xeb, 0xe1, 0x61, 0x24, 0xd0, 0xe7, 0x4f, 0x95, 0xfb, + 0x8b, 0x5e, 0xd6, 0x19, 0x44, 0x71, 0x52, 0xe4, 0x88, + 0x3d, 0x00, 0xfc, 0x2e, 0x3d, 0x14, 0xf2, 0x6a, 0x72, + 0x4f, 0xde, 0x7e, 0x87, 0x0f, 0x19, 0x6e, 0xc5, 0xa0, + 0x40, 0xbb, 0xba, 0xf4, 0xda, 0x12, 0x03, 0x4e, 0x5c, + 0xbf, 0x94, 0x62, 0x4e, 0x2d, 0xc2, 0x80, 0xb1, 0xd4, + 0x44, 0x57, 0xc8, 0x82, 0x0d, 0x3c, 0x48, 0x5a, 0x80, + 0x23, 0xd6, 0xbb, 0x44 ), + PRIVATE ( 0xc7, 0xb3, 0x46, 0x82, 0x6d, 0xd8, 0xd9, 0x40, 0x6d, + 0xda, 0xbf, 0x92, 0x6e, 0x71, 0xee, 0xf1, 0xec, 0xe5, + 0xbb, 0x78, 0xd4, 0x32, 0x12, 0xc5, 0xa0, 0x38, 0xd8, + 0x35 ), + PUBLIC ( 0x78, 0x04, 0x58, 0x69, 0x39, 0x2d, 0x56, 0xf3, 0x52, + 0x07, 0x12, 0xa6, 0x8f, 0x29, 0x46, 0x6d, 0x8a, 0x89, + 0xcf, 0x41, 0x91, 0x92, 0x50, 0x4c, 0x45, 0x3c, 0x22, + 0x4d, 0xbc, 0x9a, 0x0f, 0xfd, 0x29, 0x7d, 0x6c, 0xc1, + 0xa3, 0x70, 0xee, 0xe9, 0x39, 0x81, 0x39, 0x9d, 0xa4, + 0xc7, 0x08, 0x97, 0xaa, 0xb4, 0x8f, 0x33, 0x4f, 0x05, + 0xa5, 0x56, 0x27, 0x33, 0xe4, 0xe7, 0x31, 0xc3, 0x7f, + 0x17, 0x38, 0x80, 0x76, 0x00, 0x88, 0xed, 0xb1, 0xe9, + 0x98, 0x6a, 0x5d, 0x43, 0x08, 0x06, 0xd5, 0x14, 0x64, + 0x24, 0xd9, 0x3a, 0x7f, 0xa4, 0xa3, 0x91, 0x65, 0x9f, + 0xf9, 0x66, 0x67, 0x55, 0xe7, 0x5a, 0x14, 0x38, 0x81, + 0x61, 0x13, 0xe1, 0x44, 0x8e, 0x6e, 0x14, 0xb4, 0x6c, + 0xe8, 0xad, 0x79, 0x08, 0xfe, 0xc3, 0xb8, 0xe5, 0x02, + 0x25, 0x72, 0x63, 0xea, 0xba, 0xfe, 0xfc, 0x9a, 0x3b, + 0x9c, 0x64, 0x52, 0x21, 0x50, 0xfb, 0xc2, 0x11, 0xf4, + 0x5e, 0xef, 0x43, 0x35, 0xfb, 0xc6, 0xdc, 0x01, 0xa9, + 0x15, 0x69, 0x43, 0xab, 0xae, 0x05, 0xc3, 0xb1, 0x77, + 0xac, 0x9d, 0x7e, 0x3b, 0xd3, 0xc5, 0x7e, 0xf5, 0x8d, + 0xf9, 0x52, 0x3b, 0xcf, 0xc5, 0x53, 0x2d, 0x67, 0xac, + 0x61, 0x17, 0x4f, 0x6c, 0x9c, 0x09, 0xa9, 0x38, 0x92, + 0xdf, 0xbf, 0x49, 0x0d, 0x15, 0x0b, 0x02, 0xf2, 0x24, + 0x38, 0x56, 0x19, 0xcf, 0x6d, 0xb9, 0x0a, 0x6f, 0x79, + 0x04, 0x2e, 0xf9, 0xef, 0xdb, 0xbc, 0xdb, 0xf2, 0xa3, + 0x8b, 0x01, 0x12, 0xec, 0x40, 0xdc, 0x6b, 0xff, 0x2b, + 0xc7, 0xf5, 0x96, 0x41, 0x78, 0x40, 0xe3, 0xac, 0x4a, + 0xa5, 0xa5, 0xd0, 0x44, 0xe7, 0x1a, 0x87, 0x6a, 0x10, + 0xa2, 0x04, 0xdf, 0x71, 0x34, 0x47, 0xf2, 0x92, 0x0d, + 0x92, 0x18, 0x0e, 0x14, 0x43, 0x18, 0xce, 0x0e, 0x18, + 0xb8, 0x7e, 0xef, 0x6e ), + SHARED ( 0x0d, 0xf1, 0xd1, 0xb0, 0xfa, 0xae, 0x8b, 0x8a, 0xfb, + 0x7c, 0x47, 0x88, 0x48, 0x49, 0xf2, 0x3a, 0x2d, 0x8f, + 0x01, 0xee, 0x47, 0x14, 0x1e, 0x91, 0xd5, 0x49, 0x49, + 0xff, 0x7f, 0xd0, 0xd1, 0x10, 0xac, 0x4c, 0xcc, 0xc6, + 0x7f, 0x42, 0x8a, 0x04, 0xee, 0x64, 0x41, 0xf8, 0x1d, + 0x1a, 0x04, 0x26, 0x3b, 0x99, 0xb5, 0xee, 0xf6, 0x17, + 0x94, 0xfd, 0x3d, 0x58, 0x4b, 0xd7, 0xe1, 0xf4, 0xf6, + 0x10, 0xb8, 0xdc, 0xb7, 0x8d, 0x04, 0x5f, 0x72, 0x13, + 0x19, 0xf6, 0xa8, 0x33, 0x3e, 0x82, 0x8c, 0x56, 0xb1, + 0x97, 0x5c, 0x4f, 0xa3, 0xd3, 0x1e, 0xeb, 0x61, 0xa4, + 0xc2, 0x04, 0x2c, 0xc9, 0x22, 0x6c, 0x6e, 0xee, 0xa7, + 0x5b, 0x66, 0x8b, 0xfb, 0xb5, 0x0d, 0x1b, 0x7c, 0xa1, + 0x88, 0xc7, 0x95, 0x85, 0xa4, 0x4c, 0xe5, 0x38, 0x04, + 0x1e, 0x94, 0x1b, 0x03, 0xe6, 0xcc, 0xa4, 0x36, 0x58, + 0x02, 0xbe, 0x79, 0x89, 0x5a, 0xd2, 0x60, 0x1e, 0x47, + 0xe6, 0x26, 0x35, 0x15, 0x3a, 0x91, 0xef, 0x92, 0xa9, + 0x0f, 0x89, 0x58, 0xd3, 0xf7, 0xd2, 0xc5, 0x03, 0x61, + 0x37, 0x78, 0x51, 0x1e, 0x39, 0x4d, 0x92, 0x8f, 0xdd, + 0xb0, 0x7d, 0xc7, 0xad, 0xf4, 0x34, 0x85, 0x4e, 0x94, + 0x85, 0x19, 0xf1, 0xbf, 0xb7, 0xca, 0x02, 0x3b, 0xb0, + 0x46, 0x45, 0x96, 0xb6, 0x01, 0x0e, 0x38, 0x0d, 0xa5, + 0xd7, 0x13, 0x5c, 0x5a, 0x0d, 0x41, 0xc5, 0x6d, 0x37, + 0x92, 0xf6, 0xdc, 0x5e, 0x09, 0xa4, 0x2f, 0x7e, 0x2a, + 0x94, 0x86, 0xd8, 0x59, 0x0b, 0x01, 0x54, 0x2a, 0xe6, + 0x9f, 0xa1, 0x4f, 0xa5, 0x82, 0xca, 0x73, 0x44, 0x43, + 0x47, 0x05, 0x64, 0x9d, 0x68, 0x78, 0x85, 0xcc, 0x24, + 0x77, 0xaa, 0x4c, 0x08, 0x8d, 0x47, 0x33, 0x95, 0x48, + 0x92, 0x6b, 0x9f, 0x7a, 0x17, 0x13, 0x82, 0x67, 0xf3, + 0xe4, 0x55, 0x89, 0xdb ) ); + +/* KASValidityTest_FFCEphem_NOKC_ZZOnly_init.fax test 0 */ +DHE_TEST ( kasvaliditytest_ffcephem_nokc_zzonly_init_fc_0, + MODULUS ( 0xc7, 0x37, 0xde, 0x69, 0x33, 0x3e, 0xb6, 0xd7, 0xe8, + 0x3f, 0x8c, 0xfc, 0x69, 0xbb, 0x39, 0xcf, 0x2d, 0xd1, + 0xe6, 0xe3, 0xd0, 0x03, 0x9a, 0x3b, 0x25, 0x53, 0x39, + 0x4b, 0xbb, 0xe2, 0x7a, 0x25, 0xa3, 0x5b, 0x76, 0xf4, + 0xd5, 0x6b, 0xb6, 0x95, 0xf3, 0x6e, 0x87, 0x1d, 0x68, + 0x0f, 0x62, 0xfc, 0xce, 0xef, 0x57, 0x0f, 0xd1, 0x24, + 0xfe, 0x55, 0xe1, 0xfe, 0xfa, 0x70, 0xb4, 0x7a, 0x38, + 0x8f, 0x26, 0x4a, 0xa5, 0x28, 0x2d, 0x11, 0x51, 0x62, + 0x17, 0x4f, 0x27, 0xe1, 0x80, 0x3b, 0xf4, 0x2b, 0xde, + 0x54, 0x91, 0xe5, 0x8c, 0xfe, 0x15, 0x0e, 0x76, 0xbf, + 0xbd, 0x83, 0x62, 0xdf, 0xc4, 0xc1, 0x22, 0x39, 0x65, + 0x7d, 0x7c, 0x65, 0x74, 0xb1, 0x81, 0xb1, 0xfc, 0xf8, + 0x8c, 0xc0, 0xc8, 0x91, 0xec, 0xd9, 0xcc, 0x43, 0xb4, + 0x10, 0xa6, 0x50, 0xfd, 0xaf, 0x26, 0xb8, 0x01, 0xdc, + 0x88, 0xa1, 0x3d, 0x67, 0x27, 0xe0, 0x3d, 0x9a, 0x18, + 0xf8, 0x9b, 0x9c, 0xb3, 0xee, 0x80, 0xcc, 0x2d, 0x1e, + 0xb5, 0x33, 0xf4, 0xcc, 0xce, 0x09, 0x4f, 0xb3, 0xe2, + 0x33, 0xde, 0xd0, 0x79, 0x10, 0x5d, 0x1f, 0xbc, 0x21, + 0xdc, 0xa3, 0xf8, 0x72, 0x97, 0xc3, 0x7a, 0xe5, 0x37, + 0xb8, 0x88, 0x71, 0xce, 0xdc, 0xf5, 0x65, 0x89, 0xf1, + 0x13, 0xc9, 0xb3, 0x2a, 0xcf, 0xb5, 0xb8, 0xd4, 0x2d, + 0xf6, 0x03, 0x27, 0x87, 0x14, 0xd8, 0xa6, 0xbe, 0x1c, + 0x7e, 0x74, 0x27, 0x04, 0xf1, 0x27, 0x79, 0xf1, 0x08, + 0x92, 0xca, 0xf0, 0xef, 0x40, 0x4f, 0x06, 0xa0, 0x8c, + 0xff, 0x11, 0x02, 0x97, 0xac, 0x0d, 0x2a, 0x99, 0x93, + 0x6c, 0x15, 0xfa, 0x30, 0xed, 0x7d, 0xee, 0x19, 0xc5, + 0xbb, 0x5c, 0x6d, 0x55, 0x54, 0x2e, 0xa0, 0xb9, 0x4f, + 0xed, 0xe0, 0x32, 0x73, 0xf6, 0x2a, 0x11, 0xc6, 0xd6, + 0xe4, 0x27, 0x9c, 0x19 ), + GENERATOR ( 0x3a, 0xb1, 0xe0, 0x51, 0x9b, 0xcc, 0xc1, 0xa4, 0x6c, + 0xb9, 0x12, 0x4c, 0xb1, 0x1a, 0xcf, 0x3b, 0x42, 0x69, + 0x1a, 0x7f, 0x40, 0x83, 0x10, 0x5f, 0xd9, 0xac, 0xe0, + 0x13, 0x54, 0xec, 0xcb, 0xed, 0x1d, 0x05, 0xb6, 0x4b, + 0x14, 0x94, 0x91, 0xd1, 0x0a, 0x92, 0xdb, 0x1f, 0x56, + 0xea, 0x9d, 0x9b, 0xe2, 0xba, 0x60, 0x31, 0x0c, 0x1e, + 0xe3, 0xf6, 0xa2, 0x7c, 0x20, 0x19, 0x36, 0x32, 0xff, + 0x4f, 0x8e, 0x0e, 0x79, 0xe8, 0xd1, 0x7d, 0x27, 0x51, + 0xa5, 0x78, 0x08, 0x60, 0xd1, 0x52, 0x53, 0x75, 0x49, + 0x42, 0x5c, 0x69, 0x6e, 0x66, 0x26, 0xaa, 0x5e, 0x79, + 0x42, 0x76, 0xf1, 0x86, 0xfd, 0xcc, 0x04, 0x5b, 0x30, + 0x55, 0xec, 0x5b, 0x40, 0xf4, 0xe3, 0xbf, 0xf9, 0x8a, + 0x2b, 0x1a, 0x54, 0x5b, 0x80, 0x19, 0xc4, 0xa1, 0x36, + 0x0a, 0x49, 0x0e, 0xb9, 0xc6, 0xd7, 0x7e, 0xfb, 0x24, + 0x84, 0x26, 0x10, 0xb2, 0xfc, 0xcd, 0x9b, 0xd9, 0x53, + 0xc9, 0xd7, 0x0f, 0x6d, 0xf4, 0xcf, 0xdc, 0xe7, 0xb0, + 0x1a, 0x4d, 0x21, 0x4c, 0x99, 0xb1, 0x1c, 0x5d, 0x28, + 0x09, 0x96, 0xd5, 0x3e, 0xef, 0x09, 0x3b, 0xd5, 0x29, + 0xa5, 0x2d, 0xcc, 0xdb, 0x9c, 0xaf, 0xd9, 0x6f, 0xa7, + 0xc9, 0x28, 0x8b, 0x14, 0xf4, 0x8e, 0x0b, 0x4e, 0x35, + 0xdf, 0x57, 0x22, 0xb2, 0x8a, 0xa0, 0x05, 0xa8, 0x84, + 0xb4, 0x57, 0xcb, 0x12, 0x07, 0x8f, 0x9e, 0x83, 0xb3, + 0xbe, 0xac, 0xb0, 0x3e, 0xf2, 0x47, 0x6b, 0x22, 0x63, + 0xd2, 0x03, 0xa3, 0x3b, 0xc5, 0x17, 0x05, 0xe7, 0xe8, + 0xe6, 0x18, 0xd3, 0x41, 0x94, 0xc7, 0x16, 0xde, 0x38, + 0x0c, 0xb1, 0xb7, 0xeb, 0xe5, 0x55, 0xf2, 0x2e, 0xa6, + 0xe5, 0x61, 0x75, 0xd0, 0x7d, 0x6c, 0xbe, 0xd1, 0xc9, + 0x4c, 0x59, 0xfb, 0x37, 0xb7, 0xdc, 0xd5, 0x6f, 0xf7, + 0xb2, 0xf2, 0xf9, 0x97 ), + PARTNER ( 0x65, 0x6c, 0x5d, 0x99, 0xbc, 0xb5, 0x92, 0x1f, 0xc2, + 0xf7, 0x86, 0x99, 0xd9, 0x8e, 0xe0, 0x46, 0xbe, 0x70, + 0x76, 0x1a, 0x85, 0x7a, 0x9d, 0xfe, 0xad, 0x51, 0x7b, + 0xee, 0x16, 0xe8, 0xae, 0xf9, 0x19, 0x26, 0x04, 0xa8, + 0x29, 0x5e, 0x02, 0xef, 0xd4, 0xfe, 0xf2, 0x43, 0x64, + 0xd0, 0x53, 0x4f, 0xa2, 0x7c, 0x41, 0xef, 0x85, 0x0d, + 0xe2, 0xee, 0xb7, 0x4f, 0x03, 0xb4, 0x7d, 0x59, 0x0b, + 0x8d, 0x32, 0x4c, 0x9b, 0xf9, 0x48, 0x53, 0x7b, 0xa9, + 0xb7, 0x32, 0x1d, 0x1e, 0x51, 0xf2, 0x69, 0x66, 0x44, + 0xf3, 0x9c, 0x78, 0xa9, 0xa7, 0x4a, 0x93, 0x90, 0xb0, + 0xc3, 0x79, 0x36, 0xdd, 0xda, 0x92, 0xfe, 0x71, 0x60, + 0x21, 0xf6, 0xea, 0xb6, 0xa9, 0xbe, 0x9f, 0x42, 0x12, + 0xf7, 0x8a, 0x51, 0x2e, 0x39, 0x43, 0x50, 0xbb, 0xe4, + 0xbf, 0x6a, 0x5f, 0x8c, 0x97, 0x97, 0x74, 0x67, 0xf6, + 0xb1, 0xd9, 0x2a, 0xc9, 0x88, 0xc8, 0x1c, 0xe2, 0x48, + 0x81, 0xba, 0x59, 0x5d, 0x5f, 0x62, 0x8b, 0x25, 0x24, + 0x57, 0x9e, 0x47, 0xc9, 0xdc, 0x88, 0x6a, 0x32, 0x4a, + 0x6c, 0x37, 0xd7, 0x73, 0x38, 0x0b, 0x6a, 0xf6, 0x45, + 0xac, 0x38, 0x49, 0xd0, 0xc7, 0x8d, 0x1d, 0x9e, 0x3a, + 0x4c, 0xce, 0xba, 0x0a, 0xc9, 0x54, 0x6b, 0x9d, 0x9c, + 0xb7, 0x2b, 0xf4, 0x7a, 0x00, 0xd3, 0xd2, 0xfe, 0x79, + 0x8d, 0xb4, 0x6e, 0xe9, 0x38, 0x95, 0xaa, 0x21, 0x2f, + 0x71, 0xe1, 0xd4, 0xa8, 0x21, 0x4a, 0x87, 0x4f, 0xcd, + 0xa6, 0xca, 0x60, 0x4d, 0xc1, 0xa2, 0x28, 0xcf, 0xa9, + 0x55, 0x14, 0xca, 0x24, 0xa9, 0x1b, 0x99, 0xbe, 0x98, + 0x4c, 0x7d, 0xff, 0x5e, 0x05, 0xf9, 0x1e, 0x56, 0x65, + 0x00, 0x22, 0x65, 0xfb, 0x72, 0x30, 0x3f, 0xae, 0xc0, + 0xbb, 0x67, 0xcc, 0xe2, 0x51, 0xa7, 0x06, 0x59, 0x86, + 0x1d, 0x43, 0xbb, 0x35 ), + PRIVATE ( 0x83, 0x27, 0x46, 0xed, 0x42, 0x07, 0xdf, 0xff, 0x0a, + 0x89, 0x19, 0xae, 0x6a, 0xb5, 0x29, 0x2c, 0x37, 0xcb, + 0x8b, 0x26, 0xd4, 0x06, 0x27, 0xee, 0xb2, 0xf6, 0x36, + 0x46, 0x08, 0xc8, 0x34, 0x54 ), + PUBLIC ( 0x88, 0x9d, 0xa3, 0xae, 0x17, 0x79, 0xf8, 0xb6, 0x4e, + 0xc4, 0x56, 0xa8, 0x8d, 0x7a, 0x10, 0x5b, 0xdd, 0x7a, + 0xaa, 0x87, 0x10, 0xa3, 0x3c, 0xcc, 0x7d, 0x80, 0x59, + 0x43, 0x22, 0x91, 0xfc, 0xbf, 0x6d, 0x63, 0x5e, 0x13, + 0xc2, 0x8d, 0x19, 0x94, 0x84, 0xbc, 0x56, 0x45, 0x31, + 0x71, 0x75, 0x2c, 0x81, 0x18, 0x6e, 0x10, 0x1d, 0x2b, + 0x3d, 0x2c, 0xf9, 0x2e, 0x93, 0x6b, 0xed, 0xfa, 0x17, + 0xd2, 0x95, 0x30, 0xca, 0xc8, 0x26, 0x0c, 0x78, 0x8f, + 0x75, 0x87, 0x0e, 0x30, 0x1e, 0x6c, 0x5e, 0xae, 0xc7, + 0x99, 0x37, 0xb0, 0x02, 0x00, 0xda, 0xc1, 0x36, 0x24, + 0x12, 0xdd, 0xe4, 0x2b, 0x8a, 0xca, 0xf9, 0x35, 0x36, + 0x4b, 0x6d, 0xff, 0xa2, 0x51, 0xd1, 0xe6, 0xa0, 0x78, + 0xf3, 0x0f, 0xc3, 0x9e, 0x4e, 0x1c, 0xc6, 0xf6, 0x38, + 0x50, 0x07, 0x18, 0x42, 0x04, 0x8c, 0xe7, 0xdf, 0xeb, + 0x2a, 0xda, 0x96, 0x28, 0xae, 0xe1, 0x7d, 0x55, 0x96, + 0xb0, 0x5a, 0x4e, 0x71, 0xa9, 0xfa, 0x6f, 0x0e, 0x0d, + 0xff, 0xea, 0xe0, 0xca, 0xa2, 0xf9, 0x5a, 0x68, 0x3b, + 0x3b, 0x29, 0x7e, 0x15, 0x6b, 0x90, 0x95, 0x1c, 0x80, + 0xf9, 0x08, 0x03, 0xa8, 0xa5, 0x19, 0x9b, 0xe9, 0xde, + 0xb4, 0xf0, 0x18, 0xb4, 0xcd, 0x3c, 0xaf, 0x0b, 0x4d, + 0x3c, 0xe5, 0xd6, 0x58, 0xe3, 0x0f, 0xe2, 0x72, 0xcd, + 0xe9, 0xd2, 0xc2, 0x57, 0x9a, 0x5e, 0x3f, 0xbd, 0xe2, + 0xaa, 0x3a, 0xd4, 0xe0, 0xae, 0x05, 0x9c, 0xfc, 0x69, + 0xda, 0x62, 0xd0, 0x6f, 0x70, 0x4f, 0xeb, 0x77, 0x09, + 0x1f, 0xac, 0x6f, 0xde, 0x3a, 0xe3, 0xbb, 0x6b, 0x3d, + 0xae, 0xec, 0x9d, 0xd6, 0xbc, 0xf5, 0xb1, 0x69, 0x70, + 0xf2, 0x6e, 0xc8, 0xde, 0x3c, 0xe7, 0x9b, 0x8f, 0x37, + 0xe2, 0x7e, 0xe0, 0x4a, 0x28, 0x0f, 0x28, 0xac, 0xe8, + 0x63, 0xf2, 0xd3, 0x13 ), + SHARED ( 0x26, 0x2d, 0x59, 0xd0, 0x4c, 0x02, 0x1b, 0x84, 0xc1, + 0x16, 0x96, 0xc9, 0x9f, 0xbc, 0x03, 0x50, 0x5b, 0xe5, + 0x6b, 0x76, 0x0d, 0x63, 0xf1, 0x0b, 0xa5, 0x30, 0x9c, + 0x58, 0x60, 0x96, 0x9d, 0xf6, 0xc1, 0x3c, 0x61, 0xb3, + 0x9a, 0x21, 0x37, 0x4c, 0xb3, 0x5e, 0x17, 0x72, 0x60, + 0x50, 0x8a, 0xbb, 0xe0, 0x02, 0xf3, 0x7d, 0x15, 0x67, + 0x76, 0x97, 0x39, 0x85, 0xe4, 0x66, 0x50, 0x91, 0x31, + 0xbc, 0x45, 0xef, 0x0e, 0x32, 0xb9, 0x8b, 0xdb, 0x6c, + 0x18, 0x72, 0x06, 0xde, 0x7b, 0xda, 0xc4, 0xaf, 0x5e, + 0x39, 0x47, 0x85, 0x0e, 0xea, 0x89, 0x7a, 0xd1, 0xe3, + 0xa7, 0x60, 0x73, 0x86, 0xd4, 0xbe, 0x8b, 0xae, 0x01, + 0xf3, 0x3d, 0x28, 0xfd, 0xd7, 0x6a, 0xbb, 0x62, 0x80, + 0xc6, 0x7b, 0xaa, 0xf3, 0x1e, 0xff, 0x52, 0xf2, 0x87, + 0x4c, 0x8b, 0x0e, 0x2f, 0x3c, 0x38, 0x24, 0x13, 0x3a, + 0xad, 0x4a, 0x4f, 0x87, 0x0a, 0x81, 0x8b, 0xdf, 0x69, + 0x14, 0xd7, 0xe0, 0xbe, 0x1e, 0x61, 0x9f, 0x62, 0xa8, + 0x47, 0x78, 0x66, 0x9b, 0xef, 0x83, 0xbf, 0x5d, 0xd9, + 0xdf, 0x5f, 0x29, 0x23, 0x9a, 0x57, 0xc6, 0x35, 0x2e, + 0x70, 0x9e, 0x30, 0x05, 0x83, 0x29, 0xd8, 0xdd, 0xc1, + 0xe6, 0x9b, 0x08, 0x4b, 0x49, 0x53, 0x60, 0xde, 0xe0, + 0xda, 0x83, 0xf4, 0x58, 0xf6, 0x53, 0x49, 0x1b, 0x9b, + 0x77, 0x70, 0x16, 0xe4, 0xd9, 0x14, 0x72, 0xd7, 0xca, + 0xf6, 0x84, 0xd9, 0x79, 0xdf, 0xfa, 0xaa, 0xa6, 0xeb, + 0xa1, 0x5e, 0x63, 0x35, 0x08, 0x8b, 0xff, 0x15, 0x1b, + 0x39, 0xde, 0x8a, 0x20, 0x3d, 0xb8, 0xff, 0x29, 0x03, + 0x4c, 0x98, 0xcc, 0xcb, 0xcd, 0x7e, 0x0b, 0x3b, 0x77, + 0x5d, 0xa4, 0x77, 0x1a, 0xfd, 0xb5, 0x5c, 0x5d, 0x3f, + 0x80, 0xd0, 0xe2, 0xb3, 0x00, 0xdf, 0x14, 0x26, 0x77, + 0x81, 0x95, 0xbf, 0x64 ) ); + +/* KASValidityTest_FFCEphem_NOKC_ZZOnly_resp.fax test 0 */ +DHE_TEST ( kasvaliditytest_ffcephem_nokc_zzonly_resp_fb_0, + MODULUS ( 0x92, 0xac, 0x82, 0xb2, 0x88, 0x5b, 0x17, 0x4d, 0x24, + 0x3d, 0x1e, 0x7d, 0x5b, 0xed, 0x4b, 0x6b, 0xac, 0x61, + 0xbd, 0x71, 0x32, 0x30, 0x1b, 0xe5, 0xf1, 0xe6, 0x69, + 0x43, 0x6d, 0x40, 0x92, 0x95, 0xee, 0x9c, 0x70, 0x05, + 0xe5, 0xa1, 0x6f, 0x84, 0x78, 0xfb, 0x6f, 0xd2, 0x14, + 0xa1, 0x96, 0xc9, 0x8e, 0xf9, 0xe5, 0x91, 0xe4, 0x5c, + 0xd9, 0x33, 0x17, 0x70, 0x3a, 0x2a, 0xec, 0x5b, 0x10, + 0x6e, 0xd7, 0xcc, 0x4a, 0x98, 0x7b, 0xa1, 0xae, 0xf7, + 0xf6, 0x21, 0x34, 0x8f, 0x00, 0x2b, 0xa9, 0x87, 0x55, + 0xe0, 0x10, 0xf0, 0x32, 0x7c, 0xb5, 0x38, 0xec, 0x9b, + 0x1a, 0xec, 0x01, 0xdf, 0xaf, 0x26, 0xf1, 0xac, 0x33, + 0xb9, 0x17, 0xa9, 0x89, 0xbc, 0x73, 0x98, 0xf1, 0x23, + 0x4c, 0x25, 0x52, 0xdc, 0xda, 0xc6, 0x5e, 0x4c, 0xab, + 0xc3, 0x7b, 0x5b, 0x24, 0x91, 0x7d, 0xc1, 0x66, 0x46, + 0x4b, 0x7a, 0xff, 0x26, 0x89, 0x13, 0x88, 0x79, 0xcf, + 0xfa, 0x2a, 0x9e, 0x7c, 0x11, 0xab, 0xff, 0xf1, 0x19, + 0x17, 0x77, 0x9b, 0xad, 0x99, 0xfd, 0x31, 0x69, 0x44, + 0x7e, 0xf5, 0x08, 0x7f, 0xb6, 0x1e, 0x1a, 0x92, 0x95, + 0x55, 0x2d, 0xb7, 0x61, 0xe0, 0x67, 0x87, 0xfa, 0x44, + 0xce, 0x83, 0xdf, 0xd5, 0x22, 0x9f, 0x0b, 0xd1, 0x2a, + 0x86, 0x73, 0xd0, 0xfc, 0x90, 0x55, 0xbe, 0x05, 0x94, + 0x89, 0x8b, 0x5b, 0x03, 0x10, 0xfd, 0xc5, 0x0b, 0xa1, + 0x3f, 0x79, 0x00, 0xd5, 0xff, 0xf1, 0xe8, 0x1f, 0xbc, + 0x90, 0x42, 0xbf, 0x20, 0x4d, 0x5f, 0x0a, 0xaa, 0x69, + 0x66, 0xf7, 0x25, 0xa4, 0xb6, 0xc4, 0x84, 0x8a, 0x8d, + 0x81, 0x68, 0xe3, 0x38, 0x9d, 0x7d, 0xfe, 0xca, 0x6a, + 0xcd, 0x89, 0xde, 0x55, 0xf4, 0x2d, 0x91, 0x1b, 0x74, + 0x9b, 0xb5, 0xb8, 0x2d, 0x61, 0xef, 0x46, 0x32, 0x16, + 0xd6, 0x69, 0xac, 0x73 ), + GENERATOR ( 0x2e, 0xf4, 0x37, 0xfb, 0x4f, 0x8d, 0xed, 0x56, 0x1d, + 0xb4, 0xfe, 0x58, 0x81, 0x53, 0xa3, 0xbe, 0x42, 0x58, + 0x72, 0x70, 0xa0, 0xec, 0xf6, 0x42, 0x05, 0x01, 0x87, + 0x59, 0xeb, 0x0b, 0xc0, 0x60, 0xf6, 0x27, 0x77, 0x29, + 0x7b, 0x3a, 0x99, 0xf4, 0x89, 0x53, 0xfd, 0xce, 0x34, + 0xa0, 0xe9, 0x66, 0xdc, 0x9f, 0x79, 0xbc, 0x3b, 0xee, + 0x43, 0xbe, 0xad, 0x12, 0xd5, 0x93, 0x72, 0x91, 0x30, + 0xef, 0xf2, 0x2a, 0xd7, 0xe8, 0x35, 0x14, 0x79, 0x8f, + 0xbf, 0xfc, 0x9c, 0xcc, 0xb3, 0x4a, 0x87, 0x93, 0xaa, + 0x95, 0xaf, 0xd2, 0x68, 0x62, 0x47, 0xb5, 0xc1, 0x78, + 0x02, 0xc1, 0xd6, 0xe6, 0x46, 0x33, 0x7b, 0xad, 0x88, + 0x11, 0x8b, 0x74, 0x07, 0x44, 0x71, 0x3a, 0xd8, 0x4b, + 0xf4, 0x82, 0xa6, 0x50, 0x36, 0x4c, 0xf0, 0xc4, 0x4b, + 0xe2, 0xe6, 0x42, 0x42, 0xf2, 0x98, 0x98, 0xaf, 0x72, + 0x2f, 0xec, 0xf3, 0x9a, 0x49, 0xab, 0xa0, 0xbe, 0xfc, + 0x3d, 0xb6, 0x8c, 0xc7, 0x89, 0xf6, 0x07, 0x6f, 0xe9, + 0x00, 0x85, 0x6c, 0x01, 0xb1, 0xfd, 0x1e, 0x70, 0xce, + 0x0e, 0xce, 0xae, 0xe1, 0x71, 0x7f, 0xff, 0x94, 0x8a, + 0xd9, 0xd6, 0x6d, 0x76, 0x28, 0x0a, 0x5f, 0x3e, 0x64, + 0xaa, 0x4a, 0x37, 0x39, 0x51, 0xe8, 0x9d, 0xd1, 0x78, + 0x53, 0xe0, 0x81, 0xb6, 0xa8, 0x90, 0xdd, 0x79, 0xb1, + 0x47, 0x81, 0x75, 0x34, 0xf4, 0x6f, 0xc3, 0x8e, 0x7d, + 0xa7, 0x29, 0xc6, 0xd4, 0x5f, 0x5e, 0x25, 0x4f, 0xa1, + 0xe1, 0x90, 0xb3, 0x85, 0xfb, 0xe9, 0xf1, 0x60, 0x65, + 0x4a, 0x14, 0xe5, 0xb3, 0xc2, 0x83, 0x66, 0x7f, 0x5f, + 0x2e, 0x0a, 0xd1, 0x44, 0xbf, 0x10, 0xbf, 0xd3, 0xf9, + 0x12, 0x37, 0x0e, 0x81, 0x24, 0x35, 0x49, 0xac, 0xd4, + 0x48, 0x3d, 0xaa, 0x43, 0xf1, 0x4a, 0xbc, 0x08, 0xfc, + 0xaf, 0xa0, 0xbe, 0x29 ), + PARTNER ( 0x4d, 0x7c, 0xea, 0x09, 0x9b, 0x64, 0xdb, 0x3d, 0xdc, + 0x8d, 0x3f, 0x8b, 0xd8, 0xb1, 0xcb, 0xea, 0xd7, 0xe0, + 0x71, 0xe4, 0x1c, 0x58, 0xcf, 0x08, 0xda, 0x3d, 0xa1, + 0x8c, 0x0b, 0x7c, 0xac, 0x6f, 0x76, 0xa4, 0xd8, 0xa9, + 0x91, 0xec, 0x9e, 0x74, 0x34, 0x52, 0x02, 0x43, 0x55, + 0xa9, 0xa1, 0xf6, 0xd5, 0xc8, 0x67, 0xad, 0x4d, 0x98, + 0xeb, 0x8e, 0xf4, 0x72, 0x95, 0x20, 0xd4, 0x7b, 0x38, + 0x4a, 0xa3, 0x8b, 0x21, 0x9b, 0x85, 0x4d, 0x05, 0xf1, + 0x94, 0x89, 0xa3, 0x34, 0x9d, 0x9e, 0xaa, 0x23, 0x97, + 0xac, 0x7e, 0xb2, 0xe8, 0x7f, 0x9e, 0x1e, 0x59, 0x5e, + 0xe0, 0xea, 0xcf, 0xff, 0x86, 0x56, 0x80, 0x22, 0xc9, + 0x03, 0x6c, 0x05, 0x8c, 0x19, 0x6d, 0xaa, 0x82, 0xdd, + 0x47, 0xab, 0xbf, 0x24, 0xe6, 0xea, 0xb8, 0xc9, 0xc1, + 0x9e, 0x64, 0xd1, 0x7b, 0xee, 0x9b, 0xed, 0x99, 0xd2, + 0x90, 0x3a, 0x4b, 0x90, 0xc3, 0xbb, 0xf5, 0xa4, 0x49, + 0xb5, 0xfc, 0xa8, 0x6e, 0x07, 0x9a, 0x27, 0xfb, 0x0f, + 0x67, 0xa1, 0x14, 0x0e, 0x30, 0x80, 0x4e, 0x60, 0xdf, + 0x01, 0x62, 0x25, 0x8f, 0xb7, 0x18, 0x14, 0x56, 0x5e, + 0x76, 0x14, 0xe9, 0x15, 0x35, 0xdb, 0x52, 0xef, 0x34, + 0x60, 0x22, 0x01, 0x3c, 0x75, 0x01, 0x27, 0x13, 0xe9, + 0x0a, 0xa7, 0x67, 0x05, 0xb5, 0xec, 0x7a, 0x60, 0xdc, + 0xb5, 0xdd, 0xdc, 0xc9, 0x9d, 0xbb, 0x10, 0x7b, 0x3d, + 0xd9, 0x8f, 0x31, 0xff, 0x26, 0xce, 0x7c, 0x78, 0x24, + 0xa8, 0xe7, 0xa5, 0x6d, 0xb4, 0xc2, 0x98, 0x66, 0xac, + 0xac, 0x06, 0x4e, 0xa3, 0x85, 0x63, 0xd6, 0xf7, 0xa4, + 0x53, 0x91, 0xe5, 0x16, 0x74, 0x14, 0xfe, 0x91, 0x61, + 0x95, 0x1f, 0x30, 0xdb, 0xbc, 0x8e, 0xf1, 0xf3, 0x08, + 0x8b, 0x0e, 0x8a, 0xc8, 0x6f, 0xe0, 0x5e, 0x48, 0x19, + 0x6b, 0xe6, 0x70, 0x10 ), + PRIVATE ( 0x68, 0x22, 0xbd, 0xe0, 0x50, 0xfc, 0xdd, 0xf9, 0x0b, + 0xa8, 0xc3, 0x98, 0xd6, 0xc9, 0x73, 0x55, 0x2b, 0xe0, + 0xf8, 0xf4, 0xb4, 0x8c, 0x86, 0xdf, 0xdd, 0xa2, 0xc1, + 0x87 ), + PUBLIC ( 0x1e, 0xab, 0x79, 0xb7, 0x83, 0xe8, 0x74, 0x90, 0xa8, + 0xab, 0x90, 0x24, 0x02, 0xaa, 0xd7, 0x9d, 0x2c, 0x90, + 0x35, 0x2a, 0xf3, 0x54, 0x26, 0x69, 0x1c, 0xac, 0xe0, + 0x73, 0x8f, 0xc9, 0xa4, 0x18, 0xee, 0x4f, 0xfd, 0x9f, + 0xd3, 0x0d, 0x04, 0x8b, 0xa4, 0x38, 0xa4, 0x87, 0xf6, + 0x97, 0x5d, 0x5c, 0x20, 0x22, 0x53, 0xcb, 0xf2, 0xa8, + 0x54, 0xff, 0x36, 0x28, 0xde, 0xf8, 0x1d, 0x92, 0x3a, + 0x88, 0x20, 0x00, 0xa3, 0xd4, 0xdd, 0x4d, 0x8b, 0x16, + 0x7a, 0xdc, 0xcd, 0x71, 0xe4, 0x2e, 0x61, 0xad, 0xae, + 0x02, 0x4f, 0xa1, 0xf6, 0x69, 0x09, 0x9c, 0xd8, 0x0a, + 0xb3, 0x13, 0xb2, 0xd8, 0x44, 0xea, 0xdf, 0xdf, 0x78, + 0x6d, 0xfd, 0x5b, 0x6b, 0x5d, 0x94, 0xe5, 0x47, 0x79, + 0xde, 0xc0, 0x1f, 0xf8, 0x63, 0x39, 0x4c, 0xdd, 0xd3, + 0xe8, 0x5a, 0x91, 0xe7, 0x2d, 0xd6, 0xa4, 0x0b, 0x77, + 0x44, 0xbc, 0xad, 0x37, 0x62, 0x5e, 0xb9, 0x88, 0x7e, + 0x9a, 0x12, 0x5f, 0x7b, 0x61, 0x6c, 0xa7, 0x45, 0x9b, + 0xae, 0x96, 0xd5, 0x29, 0x72, 0x53, 0xdf, 0x39, 0x8e, + 0xd3, 0xfd, 0x0d, 0x1a, 0xf9, 0xf3, 0xbf, 0x66, 0x3a, + 0x2c, 0xdf, 0x36, 0x03, 0xb0, 0x5d, 0x72, 0xe7, 0x27, + 0x65, 0x8a, 0x40, 0x83, 0xfb, 0x49, 0x81, 0xea, 0xb3, + 0x81, 0xcc, 0x99, 0x7a, 0x82, 0x19, 0xd4, 0x5e, 0xd6, + 0xef, 0xa5, 0x5f, 0x64, 0x3d, 0x0c, 0x8e, 0xbe, 0x3a, + 0x7c, 0x32, 0xd0, 0x5e, 0x90, 0x1e, 0xe2, 0x24, 0x40, + 0x2d, 0xbf, 0xfc, 0x1e, 0xa1, 0xa2, 0x6b, 0xbc, 0xba, + 0xdd, 0xc8, 0x01, 0xcf, 0x9f, 0x51, 0x19, 0xb8, 0x3d, + 0x6e, 0xe9, 0x17, 0x74, 0x71, 0x2e, 0x56, 0x76, 0xf1, + 0x67, 0x48, 0xe2, 0x82, 0xa8, 0x44, 0x39, 0x5b, 0x47, + 0xe1, 0xc1, 0xee, 0xef, 0x41, 0x33, 0x1d, 0xdb, 0xcd, + 0xe6, 0x5e, 0x47, 0x5e ), + SHARED ( 0x35, 0xd7, 0xbe, 0xe8, 0x45, 0x91, 0xa8, 0xaf, 0xe9, + 0x29, 0x38, 0xaf, 0x83, 0x18, 0xe2, 0xb3, 0x50, 0xea, + 0x55, 0x03, 0x80, 0xa7, 0x8f, 0x81, 0x70, 0xb8, 0x6d, + 0x47, 0x0a, 0x2b, 0x69, 0x15, 0x4b, 0x6f, 0x96, 0xc9, + 0x7e, 0x46, 0x6d, 0x68, 0x75, 0xf5, 0x8f, 0x41, 0x0f, + 0xa2, 0x94, 0xa6, 0xa8, 0xcb, 0x55, 0x2d, 0x79, 0xf1, + 0xa4, 0xaa, 0x01, 0xf3, 0x01, 0x9c, 0x56, 0x5e, 0xce, + 0xf3, 0xe9, 0x2b, 0xbd, 0xa1, 0x8e, 0xe3, 0xf4, 0x76, + 0x0e, 0x91, 0x95, 0xff, 0x75, 0x72, 0x3b, 0xe3, 0x0b, + 0x86, 0x27, 0xe7, 0xd4, 0x46, 0x17, 0x4c, 0x13, 0x8f, + 0x6c, 0x49, 0x1d, 0x1a, 0xeb, 0x12, 0x36, 0xe4, 0x68, + 0xd0, 0xbe, 0xed, 0x08, 0x95, 0xad, 0x1a, 0x8d, 0xd6, + 0x3f, 0xe9, 0xc5, 0xe5, 0xed, 0x5e, 0xed, 0xbd, 0x34, + 0xb1, 0x67, 0x19, 0x03, 0x31, 0x9e, 0x3d, 0xbd, 0x1d, + 0x6f, 0xac, 0x86, 0x2d, 0xff, 0x0a, 0xa5, 0x82, 0x8d, + 0x4a, 0xb5, 0xc8, 0x28, 0x4b, 0xf7, 0x41, 0x35, 0xf4, + 0x18, 0xd3, 0x9a, 0x92, 0x01, 0xd2, 0x48, 0x3f, 0xbb, + 0x33, 0xb2, 0x83, 0x3f, 0xf0, 0x3e, 0x7c, 0xd1, 0xfb, + 0x1f, 0xda, 0x78, 0x13, 0xaf, 0x84, 0x87, 0x42, 0x02, + 0x95, 0xa8, 0xce, 0xd4, 0xc5, 0x8c, 0xd4, 0x82, 0xf3, + 0x8d, 0xf0, 0x44, 0xcc, 0x3f, 0x88, 0xfa, 0x02, 0x32, + 0x46, 0xd1, 0x0d, 0x22, 0x6c, 0x77, 0x33, 0x73, 0x68, + 0xbb, 0x41, 0x0c, 0x9e, 0x66, 0x80, 0x75, 0x6d, 0x22, + 0x47, 0x37, 0x06, 0xd2, 0xfd, 0xa1, 0xd1, 0x27, 0xcc, + 0x47, 0x0c, 0x24, 0xa7, 0xe7, 0xb6, 0xce, 0x5b, 0x4e, + 0x9c, 0xa6, 0x15, 0x96, 0xc3, 0xd1, 0x0a, 0xb0, 0x98, + 0x38, 0x0a, 0xd6, 0x13, 0xc9, 0xf8, 0x1a, 0x18, 0xcf, + 0xdf, 0x0c, 0x75, 0x78, 0xc1, 0x1c, 0x75, 0xb8, 0x86, + 0x00, 0xb4, 0xca, 0x38 ) ); + +/* KASValidityTest_FFCEphem_NOKC_ZZOnly_resp.fax test 0 */ +DHE_TEST ( kasvaliditytest_ffcephem_nokc_zzonly_resp_fc_0, + MODULUS ( 0xf5, 0x28, 0xaa, 0x27, 0x62, 0xdf, 0x76, 0xd7, 0x80, + 0x2f, 0xea, 0x08, 0x70, 0x05, 0xd7, 0x6f, 0xeb, 0x69, + 0xb7, 0xaf, 0xd9, 0xe7, 0xfb, 0x36, 0x37, 0x15, 0xa1, + 0xb4, 0x4f, 0x09, 0xdb, 0xda, 0x5d, 0x06, 0xa6, 0x3e, + 0x3d, 0x51, 0x2a, 0xbe, 0x9c, 0x68, 0x10, 0xab, 0xba, + 0x2b, 0xfd, 0x48, 0xcf, 0x55, 0x45, 0x13, 0x71, 0x2d, + 0xf2, 0x5f, 0x78, 0x62, 0x65, 0xa7, 0x5f, 0x4e, 0x1d, + 0xac, 0xaa, 0xcb, 0xbe, 0x9e, 0x52, 0x8a, 0x63, 0x46, + 0xa8, 0xbf, 0x38, 0x01, 0x5e, 0xcb, 0xc5, 0x23, 0xd4, + 0xe7, 0x8d, 0x73, 0x8b, 0x88, 0x52, 0xbc, 0xd6, 0x66, + 0x00, 0xdc, 0x43, 0x42, 0x86, 0xc7, 0x8f, 0x6c, 0xca, + 0xe9, 0x92, 0x56, 0x8b, 0x25, 0x8b, 0xb0, 0xd3, 0xb5, + 0x03, 0xe3, 0x7e, 0x7a, 0x95, 0x6e, 0xc1, 0x61, 0x69, + 0x78, 0xc1, 0xe7, 0x98, 0x92, 0x29, 0xe3, 0x99, 0x14, + 0xea, 0x7c, 0x1b, 0xea, 0x39, 0x02, 0xe6, 0x69, 0xdf, + 0xc6, 0x23, 0x68, 0xda, 0x13, 0x74, 0x48, 0x35, 0x0c, + 0x1d, 0xef, 0x54, 0xbf, 0x3e, 0xe2, 0x14, 0x2c, 0x06, + 0x56, 0x01, 0xdf, 0xae, 0x6b, 0xca, 0x07, 0xe9, 0x3c, + 0xc4, 0x63, 0x39, 0x3c, 0xbc, 0x48, 0x3b, 0x2c, 0x72, + 0x72, 0x55, 0x37, 0x88, 0x2e, 0xd5, 0xb9, 0x60, 0x74, + 0xf5, 0x19, 0xfc, 0x73, 0x8f, 0x50, 0x33, 0xb8, 0x7b, + 0x5e, 0xdc, 0xa0, 0xec, 0x4a, 0x34, 0xdc, 0x0f, 0x84, + 0xd2, 0xfa, 0x2c, 0x36, 0x5f, 0x69, 0x1a, 0x8f, 0xc7, + 0xdc, 0x64, 0xbc, 0x08, 0x39, 0xa5, 0x81, 0x3f, 0xef, + 0x9a, 0xf3, 0x85, 0xc6, 0x8e, 0x95, 0x2a, 0xf5, 0x66, + 0xf7, 0x9b, 0x08, 0x02, 0xbe, 0x99, 0x00, 0xcf, 0x08, + 0x38, 0x03, 0x2e, 0xd2, 0x49, 0xaf, 0xea, 0x05, 0x57, + 0x21, 0x54, 0x0c, 0x89, 0x61, 0xe6, 0x30, 0x0a, 0xd5, + 0xde, 0xc6, 0x51, 0xc7 ), + GENERATOR ( 0x08, 0x5c, 0x0a, 0x05, 0x12, 0xc2, 0x41, 0xb5, 0x83, + 0xd4, 0x17, 0x03, 0xed, 0xfb, 0xfe, 0xa2, 0xa3, 0xe8, + 0x63, 0xde, 0xac, 0x68, 0x85, 0x50, 0x97, 0x70, 0x79, + 0x67, 0xe0, 0x97, 0x18, 0x6e, 0xf8, 0x9d, 0x05, 0xc6, + 0x52, 0x27, 0xf5, 0x6b, 0x12, 0xde, 0x61, 0x23, 0xba, + 0xc8, 0x6c, 0x3c, 0x13, 0xd6, 0x80, 0x99, 0x4b, 0xff, + 0xaf, 0x24, 0xb2, 0xa1, 0xed, 0x7f, 0x01, 0xc1, 0x08, + 0xb0, 0x65, 0x93, 0xf2, 0x2f, 0x74, 0xb4, 0xaf, 0x22, + 0x2b, 0x46, 0xe5, 0xfb, 0x89, 0x48, 0x2f, 0xbd, 0x96, + 0xf5, 0x45, 0x1c, 0x9f, 0x45, 0x39, 0x31, 0x36, 0xec, + 0x03, 0x7a, 0xa8, 0x1a, 0x81, 0x24, 0x54, 0x59, 0xec, + 0x01, 0x80, 0x24, 0x51, 0x83, 0x94, 0xf4, 0xd9, 0x36, + 0x59, 0x6d, 0xc5, 0x3c, 0x3d, 0x8a, 0x9f, 0x73, 0x29, + 0x03, 0x71, 0x97, 0x96, 0xc0, 0x45, 0xb6, 0x2f, 0xad, + 0xea, 0x9d, 0xd1, 0xb2, 0xfa, 0xbe, 0xc1, 0x56, 0x0d, + 0xdb, 0x3b, 0x78, 0x0d, 0x96, 0x46, 0xad, 0x0d, 0xd3, + 0x16, 0x8c, 0x07, 0xcc, 0x99, 0x4f, 0x79, 0xee, 0x80, + 0x4c, 0xae, 0x07, 0x57, 0x39, 0x12, 0x51, 0x1d, 0xe0, + 0x50, 0xd0, 0x5a, 0x0d, 0x58, 0xb8, 0x19, 0xec, 0x41, + 0xe7, 0xc1, 0x20, 0x5d, 0xc7, 0x19, 0x9f, 0xc6, 0x5a, + 0x6a, 0x1a, 0x4f, 0xfc, 0xb4, 0xdf, 0x38, 0xd9, 0xb6, + 0x75, 0x72, 0x69, 0x00, 0x34, 0x01, 0xd8, 0x4c, 0x73, + 0x23, 0x85, 0xa5, 0x51, 0x74, 0xf2, 0x7d, 0x4b, 0x49, + 0x3c, 0xb7, 0x10, 0x98, 0x0c, 0x3a, 0xf9, 0x8b, 0xe7, + 0xbc, 0xff, 0x94, 0x67, 0xe8, 0x17, 0x92, 0xf2, 0xd2, + 0xf9, 0xa2, 0xf8, 0xd5, 0xd0, 0xdd, 0xc9, 0xa2, 0x29, + 0x79, 0x01, 0x92, 0xa1, 0x94, 0xda, 0x5b, 0x20, 0x32, + 0xf2, 0xf0, 0xcc, 0x23, 0xff, 0xbc, 0xb2, 0xcc, 0x16, + 0x6f, 0x21, 0x28, 0xee ), + PARTNER ( 0x81, 0x5e, 0x1c, 0x8e, 0x64, 0xbe, 0x67, 0x86, 0xb6, + 0xc2, 0x19, 0x45, 0x06, 0xb6, 0x61, 0x19, 0x9a, 0x56, + 0xc8, 0x07, 0xc6, 0x34, 0x0a, 0x83, 0x69, 0x7d, 0x3b, + 0xc7, 0x53, 0x04, 0xfb, 0xeb, 0x91, 0xbb, 0x15, 0xac, + 0x79, 0x81, 0xba, 0xbb, 0xa2, 0x60, 0xa6, 0x53, 0xe6, + 0x66, 0xb0, 0xda, 0x80, 0x12, 0x04, 0x2a, 0x08, 0x85, + 0x47, 0xf0, 0xfb, 0xd1, 0xef, 0x56, 0xd1, 0xd1, 0xf6, + 0x5e, 0xd8, 0x09, 0xca, 0x85, 0x45, 0x13, 0x9f, 0x57, + 0xd3, 0xeb, 0xda, 0x3e, 0x0f, 0x13, 0xe7, 0xef, 0x19, + 0xbd, 0xed, 0x4a, 0x8d, 0xfa, 0x20, 0x4f, 0x24, 0x57, + 0xa3, 0xd3, 0x92, 0x83, 0x9d, 0xd7, 0x0b, 0xec, 0x53, + 0x8a, 0x29, 0xd7, 0x88, 0x74, 0x70, 0x9f, 0x90, 0x5e, + 0xc2, 0x87, 0x40, 0xbe, 0x15, 0x76, 0x96, 0xaa, 0x86, + 0x2b, 0x57, 0xab, 0x51, 0xef, 0x20, 0x03, 0x36, 0x01, + 0xff, 0x83, 0xba, 0x3f, 0xb0, 0xe8, 0x02, 0x55, 0x67, + 0x4e, 0x0e, 0x6e, 0x72, 0x69, 0xcd, 0x85, 0xca, 0x10, + 0x37, 0x9d, 0x41, 0x8b, 0x73, 0x7d, 0x3c, 0xde, 0xb5, + 0xf3, 0x1f, 0xb0, 0x6f, 0x4a, 0x14, 0x11, 0x45, 0x93, + 0x6a, 0x9e, 0xec, 0x05, 0x51, 0xb4, 0xba, 0x59, 0xc5, + 0x9d, 0x2b, 0x3e, 0xdd, 0xc1, 0x9e, 0xa1, 0x89, 0x78, + 0x27, 0x9c, 0xf1, 0x1f, 0x21, 0xa6, 0x5a, 0x9d, 0x2a, + 0x10, 0x4c, 0xfe, 0x6a, 0xf9, 0x3f, 0xa8, 0xa1, 0xcc, + 0xd1, 0x8b, 0x03, 0x01, 0x7a, 0x8a, 0xb3, 0x19, 0x43, + 0x99, 0x31, 0x92, 0xf7, 0x94, 0xd8, 0x7b, 0xbc, 0x27, + 0x3e, 0xf9, 0x16, 0x0a, 0xf9, 0x54, 0x48, 0x07, 0x83, + 0xcf, 0xb6, 0xa3, 0x7b, 0xa0, 0x85, 0x50, 0x3b, 0xa2, + 0x58, 0x16, 0x9a, 0x8d, 0xab, 0xa7, 0x1d, 0x4c, 0x52, + 0xde, 0x7c, 0x4e, 0x65, 0x47, 0x22, 0x23, 0xc2, 0x02, + 0xad, 0x27, 0x7e, 0x93 ), + PRIVATE ( 0x56, 0x1c, 0x94, 0x8b, 0xe9, 0xd0, 0xd9, 0x68, 0xcd, + 0xfc, 0x6d, 0x27, 0xcb, 0xfe, 0x52, 0xcd, 0xa3, 0x42, + 0xf5, 0x44, 0xf5, 0x6d, 0x57, 0xf1, 0x18, 0x86, 0x63, + 0x26, 0xf7, 0x6e, 0x1f, 0x70 ), + PUBLIC ( 0x3c, 0x24, 0x97, 0x3b, 0x32, 0x96, 0x26, 0x97, 0x59, + 0xce, 0x38, 0xe2, 0xe5, 0x25, 0xc0, 0x09, 0x5d, 0x4b, + 0x5c, 0x34, 0xbe, 0x5d, 0x13, 0xb4, 0x5b, 0x22, 0x2b, + 0x8e, 0x48, 0x9c, 0x27, 0xfb, 0x44, 0x42, 0xf3, 0x2c, + 0xc7, 0x64, 0x66, 0x5e, 0x28, 0xa0, 0x66, 0x55, 0xc7, + 0x1f, 0xb3, 0x7f, 0xd8, 0x75, 0xa9, 0x21, 0xd1, 0x79, + 0x55, 0x1a, 0x1e, 0x4f, 0x2a, 0x90, 0x54, 0xa7, 0x6c, + 0xae, 0x2a, 0x61, 0xd3, 0xcb, 0xec, 0x55, 0xc3, 0xbe, + 0x19, 0x85, 0x3a, 0x54, 0x09, 0xd9, 0xff, 0x91, 0x4b, + 0x93, 0xbc, 0x78, 0xb8, 0xaa, 0x15, 0x25, 0xb9, 0x08, + 0xf3, 0x24, 0x19, 0xa8, 0x8d, 0x77, 0x26, 0xea, 0xd7, + 0x6a, 0x3f, 0x88, 0x95, 0xd6, 0x30, 0x71, 0xa9, 0xb0, + 0xa6, 0x3f, 0xe4, 0x72, 0x8d, 0x19, 0x51, 0x8d, 0x1d, + 0x08, 0x08, 0x81, 0x41, 0xb8, 0x26, 0x9f, 0x0b, 0x0c, + 0xd7, 0x71, 0x12, 0xd4, 0x76, 0xaf, 0x9e, 0xfc, 0xc7, + 0xf5, 0x90, 0xaf, 0x8f, 0xc1, 0xf9, 0xdc, 0x5e, 0x4c, + 0x00, 0xcd, 0x5d, 0xfa, 0x64, 0xa3, 0x3b, 0x2d, 0xf4, + 0xdb, 0x9d, 0x85, 0x94, 0xd8, 0x74, 0x89, 0xbe, 0xa6, + 0xf6, 0xf3, 0x79, 0x58, 0xbf, 0xb5, 0x98, 0xe5, 0x69, + 0x2b, 0x81, 0xbf, 0x11, 0x6b, 0x60, 0x22, 0x7b, 0x62, + 0x52, 0xa6, 0x43, 0x8f, 0x04, 0x9c, 0x5c, 0x44, 0x9b, + 0xab, 0x02, 0x77, 0x40, 0xf8, 0x55, 0x1b, 0xf1, 0xff, + 0xe2, 0x50, 0x84, 0xf2, 0x31, 0xff, 0x64, 0x63, 0x88, + 0xd0, 0x09, 0xba, 0x22, 0x19, 0x32, 0x62, 0x02, 0x9b, + 0xa1, 0x9a, 0xf2, 0x64, 0x3d, 0xd6, 0x79, 0xf2, 0x83, + 0x21, 0x2a, 0x2d, 0x26, 0xad, 0x91, 0x7e, 0xfe, 0x96, + 0x42, 0xc7, 0x48, 0xfc, 0xeb, 0x33, 0xfb, 0x0a, 0x6c, + 0x13, 0x2f, 0x37, 0x8d, 0xad, 0xa2, 0xcc, 0xce, 0xde, + 0x08, 0x6d, 0x8a, 0x31 ), + SHARED ( 0xec, 0x23, 0x09, 0xa7, 0xd2, 0x38, 0xae, 0xb0, 0x67, + 0x14, 0xc2, 0x7c, 0x1b, 0xbb, 0xb1, 0xb1, 0xc5, 0xaa, + 0x3c, 0xdd, 0xdd, 0x76, 0xa4, 0x19, 0xb1, 0xf3, 0x70, + 0x4d, 0xd3, 0x43, 0x7c, 0xd1, 0xf2, 0xc8, 0x84, 0x3f, + 0x35, 0x0d, 0x67, 0x87, 0x2e, 0xe3, 0x25, 0x97, 0x3f, + 0x4e, 0x5a, 0xce, 0x7d, 0x40, 0x6b, 0xe5, 0xde, 0x75, + 0xd9, 0xa9, 0x12, 0x0a, 0xf6, 0x7e, 0x32, 0xf0, 0x29, + 0x1e, 0x77, 0xe7, 0xa3, 0x97, 0x62, 0x49, 0x29, 0xd6, + 0x3d, 0xc0, 0xc4, 0x2e, 0xf8, 0xf4, 0x42, 0xce, 0x89, + 0xa3, 0x9b, 0x19, 0x2f, 0xee, 0x38, 0x6c, 0xe6, 0x83, + 0x01, 0xc4, 0xb8, 0x28, 0xea, 0x91, 0x89, 0x79, 0x83, + 0x46, 0xb6, 0x0f, 0x61, 0x5d, 0xd9, 0x63, 0x91, 0x05, + 0xff, 0xea, 0x6e, 0xc6, 0x1e, 0xe5, 0xe4, 0xa7, 0xd6, + 0x8c, 0xe7, 0x2b, 0xbf, 0x12, 0x81, 0xd6, 0x86, 0x4e, + 0x30, 0x18, 0x1c, 0xe4, 0x19, 0x95, 0x2a, 0x3e, 0xf8, + 0x3a, 0x9a, 0xd7, 0xb2, 0x6f, 0xc7, 0x29, 0x2a, 0xd7, + 0x45, 0xbf, 0xb5, 0x43, 0xe5, 0xc2, 0xea, 0x31, 0x0a, + 0x41, 0x59, 0xe9, 0xd6, 0x60, 0x27, 0x9d, 0x12, 0xc1, + 0xe0, 0x38, 0x50, 0xe8, 0x37, 0xc0, 0x1c, 0x54, 0x2a, + 0x0f, 0x59, 0xad, 0x61, 0xd0, 0x73, 0x10, 0x05, 0xe8, + 0x00, 0x9a, 0x3d, 0x84, 0x06, 0x69, 0x1a, 0xbb, 0x22, + 0xf5, 0xf2, 0xe9, 0x6a, 0xe3, 0x45, 0x78, 0x3c, 0x40, + 0x3e, 0x59, 0xb9, 0xe9, 0x48, 0xad, 0xdb, 0xea, 0x8a, + 0xc7, 0xd7, 0x70, 0x82, 0x10, 0x44, 0xe0, 0x3f, 0x15, + 0xae, 0x6f, 0xc3, 0x67, 0xdd, 0xc8, 0x5b, 0xa6, 0x2a, + 0x26, 0xb4, 0xd9, 0x4d, 0x77, 0x05, 0xf5, 0xec, 0xad, + 0x8a, 0xa2, 0x1b, 0x61, 0x9d, 0x0e, 0x09, 0xf1, 0x24, + 0xbe, 0xe8, 0x65, 0x8a, 0x21, 0x87, 0xf7, 0x02, 0x91, + 0x07, 0x10, 0x5d, 0xbf ) ); + +/** + * Perform Ephemeral Diffie-Hellman self-tests + * + */ +static void dhe_test_exec ( void ) { + + dhe_key_ok ( &kasvaliditytest_ffcephem_nokc_zzonly_init_fb_0 ); + dhe_key_ok ( &kasvaliditytest_ffcephem_nokc_zzonly_init_fc_0 ); + dhe_key_ok ( &kasvaliditytest_ffcephem_nokc_zzonly_resp_fb_0 ); + dhe_key_ok ( &kasvaliditytest_ffcephem_nokc_zzonly_resp_fc_0 ); +} + +/** Ephemeral Diffie-Hellman self-test */ +struct self_test dhe_test __self_test = { + .name = "dhe", + .exec = dhe_test_exec, +}; diff --git a/src/tests/hmac_test.c b/src/tests/hmac_test.c new file mode 100644 index 000000000..5267999e4 --- /dev/null +++ b/src/tests/hmac_test.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + * + * 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 ); + +/** @file + * + * HMAC self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <string.h> +#include <ipxe/hmac.h> +#include <ipxe/md5.h> +#include <ipxe/sha1.h> +#include <ipxe/sha256.h> +#include <ipxe/test.h> + +/** Define inline key data */ +#define KEY(...) { __VA_ARGS__ } + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected HMAC */ +#define EXPECTED(...) { __VA_ARGS__ } + +/** An HMAC test */ +struct hmac_test { + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Key */ + const void *key; + /** Length of key */ + size_t key_len; + /** Data */ + const void *data; + /** Length of data */ + size_t data_len; + /** Expected HMAC */ + const void *expected; + /** Length of expected HMAC */ + size_t expected_len; +}; + +/** + * Define an HMAC test + * + * @v name Test name + * @v DIGEST Digest algorithm + * @v KEY Key + * @v DATA Data + * @v EXPECTED Expected HMAC + * @ret test HMAC test + */ +#define HMAC_TEST( name, DIGEST, KEY, DATA, EXPECTED ) \ + static const uint8_t name ## _key[] = KEY; \ + static const uint8_t name ## _data[] = DATA; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct hmac_test name = { \ + .digest = DIGEST, \ + .key = name ## _key, \ + .key_len = sizeof ( name ## _key ), \ + .data = name ## _data, \ + .data_len = sizeof ( name ## _data ), \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + } + +/** + * Report an HMAC test result + * + * @v test HMAC test + * @v file Test code file + * @v line Test code line + */ +static void hmac_okx ( struct hmac_test *test, const char *file, + unsigned int line ) { + struct digest_algorithm *digest = test->digest; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; + uint8_t hmac[digest->digestsize]; + + /* Sanity checks */ + okx ( sizeof ( ctx ) == ( digest->ctxsize + digest->blocksize ), + file, line ); + okx ( test->expected_len == digest->digestsize, file, line ); + + /* Calculate HMAC */ + DBGC ( test, "HMAC-%s key:\n", digest->name ); + DBGC_HDA ( test, 0, test->key, test->key_len ); + DBGC ( test, "HMAC-%s data:\n", digest->name ); + DBGC_HDA ( test, 0, test->data, test->data_len ); + hmac_init ( digest, ctx, test->key, test->key_len ); + hmac_update ( digest, ctx, test->data, test->data_len ); + hmac_final ( digest, ctx, hmac ); + DBGC ( test, "HMAC-%s result:\n", digest->name ); + DBGC_HDA ( test, 0, hmac, sizeof ( hmac ) ); + + /* Compare against expected result */ + okx ( memcmp ( hmac, test->expected, test->expected_len ) == 0, + file, line ); +} +#define hmac_ok( test ) hmac_okx ( test, __FILE__, __LINE__ ) + +/* Empty key and data */ +HMAC_TEST ( hmac_empty, &sha256_algorithm, KEY(), DATA(), + EXPECTED ( 0xb6, 0x13, 0x67, 0x9a, 0x08, 0x14, 0xd9, 0xec, 0x77, + 0x2f, 0x95, 0xd7, 0x78, 0xc3, 0x5f, 0xc5, 0xff, 0x16, + 0x97, 0xc4, 0x93, 0x71, 0x56, 0x53, 0xc6, 0xc7, 0x12, + 0x14, 0x42, 0x92, 0xc5, 0xad ) ); + +/* "Hello world" */ +HMAC_TEST ( hmac_hw, &sha256_algorithm, + KEY ( 'H', 'e', 'l', 'l', 'o' ), + DATA ( 'W', 'o', 'r', 'l', 'd' ), + EXPECTED ( 0x59, 0x16, 0x8e, 0x30, 0x9f, 0x2c, 0x97, 0xdd, 0x04, + 0xe4, 0x5b, 0xe3, 0xe7, 0x9b, 0xd9, 0xac, 0xb6, 0xd2, + 0x2f, 0xda, 0x65, 0x46, 0xc0, 0x0c, 0x53, 0x92, 0x82, + 0xc4, 0x1e, 0xeb, 0x91, 0x6e ) ); + +/* "Hello world" using SHA-1 */ +HMAC_TEST ( hmac_hw_sha1, &sha1_algorithm, + KEY ( 'H', 'e', 'l', 'l', 'o' ), + DATA ( 'W', 'o', 'r', 'l', 'd' ), + EXPECTED ( 0x9e, 0x29, 0xcf, 0x6d, 0x48, 0x90, 0x49, 0x9e, 0xf8, + 0x5a, 0x31, 0x47, 0x55, 0x7b, 0x1a, 0x45, 0xd5, 0xae, + 0xd1, 0x77 ) ); + +/* "Hello world" using MD5 */ +HMAC_TEST ( hmac_hw_md5, &md5_algorithm, + KEY ( 'H', 'e', 'l', 'l', 'o' ), + DATA ( 'W', 'o', 'r', 'l', 'd' ), + EXPECTED ( 0xc7, 0x1c, 0x0c, 0xd0, 0xdc, 0x24, 0x49, 0xbd, 0xd8, + 0x9d, 0x28, 0xeb, 0x03, 0xbe, 0xf2, 0x04 ) ); + +/* Block-length key */ +HMAC_TEST ( hmac_maxlen, &sha256_algorithm, + KEY ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f ), + DATA ( 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' ), + EXPECTED ( 0xdd, 0x05, 0xcc, 0xe6, 0xd6, 0xaf, 0x91, 0x61, 0x4b, + 0xaf, 0x35, 0x6b, 0x86, 0x0a, 0x05, 0x67, 0x25, 0x22, + 0xf0, 0x54, 0xd2, 0x5f, 0xd7, 0xe1, 0x54, 0x26, 0x01, + 0x16, 0xfd, 0x8a, 0xf3, 0x5b ) ); + +/* Over-length key */ +HMAC_TEST ( hmac_overlen, &sha256_algorithm, + KEY ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40 ), + DATA ( 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' ), + EXPECTED ( 0x4f, 0x0f, 0x42, 0x07, 0xda, 0x84, 0x3d, 0x2a, 0x34, + 0xae, 0x5d, 0xd9, 0x05, 0x1e, 0x96, 0xa8, 0xb6, 0xef, + 0xa1, 0xcd, 0x49, 0x5b, 0xea, 0x30, 0xbf, 0x47, 0x3b, + 0xdc, 0xa9, 0x86, 0xbb, 0x31 ) ); + +/** + * Perform HMAC self-tests + * + */ +static void hmac_test_exec ( void ) { + + hmac_ok ( &hmac_empty ); + hmac_ok ( &hmac_hw ); + hmac_ok ( &hmac_hw_sha1 ); + hmac_ok ( &hmac_hw_md5 ); + hmac_ok ( &hmac_maxlen ); + hmac_ok ( &hmac_overlen ); +} + +/** HMAC self-tests */ +struct self_test hmac_test __self_test = { + .name = "hmac", + .exec = hmac_test_exec, +}; diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index 9b3a744df..de8edc8ad 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -41,12 +41,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Define inline IPv6 address */ #define IPV6(...) { __VA_ARGS__ } +/** An IPv6 test prefix */ +struct ipv6_test_prefix { + /** Prefix length */ + unsigned int len; + /** Prefix mask */ + const char *mask; +}; + /** An IPv6 test routing table entry */ struct ipv6_test_route { /** Local address */ const char *address; - /** Prefix length */ - unsigned int prefix_len; + /** Prefix */ + const struct ipv6_test_prefix *prefix; /** Router address (if any) */ const char *router; }; @@ -61,6 +69,13 @@ struct ipv6_test_table { struct list_head list; }; +/** Define a test prefix */ +#define PREFIX( name, LEN, MASK ) \ + static const struct ipv6_test_prefix name = { \ + .len = LEN, \ + .mask = MASK, \ + }; + /** Define a test routing table */ #define TABLE( name, ... ) \ static const struct ipv6_test_route name ## _routes[] = { \ @@ -116,23 +131,45 @@ static struct net_device ipv6_test_netdev = { .state = NETDEV_OPEN, }; +/** /48 prefix */ +PREFIX ( prefix48, 48, "ffff:ffff:ffff::" ); + +/** /64 prefix */ +PREFIX ( prefix64, 64, "ffff:ffff:ffff:ffff::" ); + +/** /126 prefix */ +PREFIX ( prefix126, 126, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc" ); + +/** /127 prefix */ +PREFIX ( prefix127, 127, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe" ); + +/** /128 prefix */ +PREFIX ( prefix128, 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ); + /** Routing table with only a link-local address */ TABLE ( table_link_local, - { "fe80::69ff:fe50:5845", 64, NULL } ); + { "fe80::69ff:fe50:5845", &prefix64, NULL } ); /** Routing table with a global address */ TABLE ( table_normal, - { "fe80::69ff:fe50:5845", 64, NULL }, - { "2001:db8:3::1", 64, "fe80::1" } ); + { "fe80::69ff:fe50:5845", &prefix64, NULL }, + { "2001:db8:3::1", &prefix64, "fe80::1" } ); /** Routing table with multiple addresses and routers */ TABLE ( table_multi, - { "fe80::69ff:fe50:5845", 64, NULL }, - { "2001:db8:3::1", 64, "fe80::1" }, - { "2001:db8:5::1", 64, NULL }, - { "2001:db8:42::1", 64, "fe80::2" }, - { "fd44:9112:6442::69ff:fe50:5845", 64, "fe80::1" }, - { "fd70:6ba9:50ae::69ff:fe50:5845", 64, "fe80::3" } ); + { "fe80::69ff:fe50:5845", &prefix64, NULL }, + { "2001:db8:3::1", &prefix64, "fe80::1" }, + { "2001:db8:5::1", &prefix64, NULL }, + { "2001:db8:42::1", &prefix64, "fe80::2" }, + { "fd44:9112:6442::69ff:fe50:5845", &prefix64, "fe80::1" }, + { "fd70:6ba9:50ae::69ff:fe50:5845", &prefix64, "fe80::3" } ); + +/** Routing table with unusual prefix lengths */ +TABLE ( table_unusual, + { "2001:db8:1::1", &prefix48, "fe80::1" }, + { "2001:db8:2::1", &prefix126, NULL }, + { "2001:db8:3::1", &prefix127, NULL }, + { "2001:db8:4::1", &prefix128, NULL } ); /** * Report an inet6_ntoa() test result @@ -212,8 +249,10 @@ static void inet6_aton_fail_okx ( const char *text, const char *file, static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file, unsigned int line ) { const struct ipv6_test_route *route; + struct ipv6_miniroute *miniroute; struct in6_addr address; struct in6_addr router; + struct in6_addr mask; struct list_head saved; unsigned int i; @@ -235,13 +274,33 @@ static void ipv6_table_okx ( struct ipv6_test_table *table, const char *file, okx ( inet6_aton ( route->router, &router ) == 0, file, line ); } + okx ( inet6_aton ( route->prefix->mask, &mask ) == 0, + file, line ); /* Add routing table entry */ okx ( ipv6_add_miniroute ( &ipv6_test_netdev, &address, - route->prefix_len, + route->prefix->len, ( route->router ? &router : NULL ) ) == 0, file, line ); + + /* Check routing table entry */ + miniroute = list_first_entry ( &ipv6_miniroutes, + struct ipv6_miniroute, list ); + okx ( miniroute != NULL, file, line ); + okx ( memcmp ( &miniroute->address, &address, + sizeof ( address ) ) == 0, file, line ); + okx ( miniroute->prefix_len == route->prefix->len, + file, line ); + okx ( memcmp ( &miniroute->prefix_mask, &mask, + sizeof ( mask ) ) == 0, file, line ); + if ( route->router ) { + okx ( memcmp ( &miniroute->router, &router, + sizeof ( router ) ) == 0, file, line ); + } else { + okx ( IN6_IS_ADDR_UNSPECIFIED ( &miniroute->router ), + file, line ); + } } /* Save constructed routing table */ @@ -462,6 +521,7 @@ static void ipv6_test_exec ( void ) { ipv6_table_ok ( &table_link_local ); ipv6_table_ok ( &table_normal ); ipv6_table_ok ( &table_multi ); + ipv6_table_ok ( &table_unusual ); /* Routing table with only a link-local address */ ipv6_route_ok ( &table_link_local, "fe80::1", @@ -505,10 +565,27 @@ static void ipv6_test_exec ( void ) { ipv6_route_ok ( &table_multi, "ff02::1", "fe80::69ff:fe50:5845", NULL ); + /* Routing table with unusual prefix lengths */ + ipv6_route_ok ( &table_unusual, "2001:db8:2::1", + "2001:db8:2::1", NULL ); + ipv6_route_ok ( &table_unusual, "2001:db8:2::3", + "2001:db8:2::1", NULL ); + ipv6_route_ok ( &table_unusual, "2001:db8:3::1", + "2001:db8:3::1", NULL ); + ipv6_route_ok ( &table_unusual, "2001:db8:3::2", + "2001:db8:1::1", "fe80::1" ); + ipv6_route_ok ( &table_unusual, "2001:db8:4::1", + "2001:db8:4::1", NULL ); + ipv6_route_ok ( &table_unusual, "2001:db8:4::0", + "2001:db8:1::1", "fe80::1" ); + ipv6_route_ok ( &table_unusual, "2001:db8:4::2", + "2001:db8:1::1", "fe80::1" ); + /* Destroy test routing tables */ ipv6_table_del ( &table_link_local ); ipv6_table_del ( &table_normal ); ipv6_table_del ( &table_multi ); + ipv6_table_del ( &table_unusual ); } /** IPv6 self-test */ diff --git a/src/tests/pccrc_test.c b/src/tests/pccrc_test.c index f4ab573ac..e69493202 100644 --- a/src/tests/pccrc_test.c +++ b/src/tests/pccrc_test.c @@ -467,11 +467,10 @@ peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, uint8_t *pass, size_t pass_len, const char *file, unsigned int line ) { struct digest_algorithm *digest = info->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t secret[digest->digestsize]; uint8_t expected[digest->digestsize]; size_t digestsize = info->digestsize; - size_t secretsize = digestsize; /* Calculate server secret */ digest_init ( digest, ctx ); @@ -479,11 +478,9 @@ peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, digest_final ( digest, ctx, secret ); /* Calculate expected segment secret */ - hmac_init ( digest, ctx, secret, &secretsize ); - assert ( secretsize == digestsize ); + hmac_init ( digest, ctx, secret, digestsize ); hmac_update ( digest, ctx, test->expected_hash, digestsize ); - hmac_final ( digest, ctx, secret, &secretsize, expected ); - assert ( secretsize == digestsize ); + hmac_final ( digest, ctx, expected ); /* Verify segment secret */ okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0, diff --git a/src/tests/tests.c b/src/tests/tests.c index 5dbbac1b1..54694fa45 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -77,3 +77,5 @@ REQUIRE_OBJECT ( zlib_test ); REQUIRE_OBJECT ( gzip_test ); REQUIRE_OBJECT ( utf8_test ); REQUIRE_OBJECT ( acpi_test ); +REQUIRE_OBJECT ( hmac_test ); +REQUIRE_OBJECT ( dhe_test ); |
