From 0965cec53c9e421f900bb050b1c83196a0a5687b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 16:39:40 +0100 Subject: [pci] Generalise function-level reset mechanism Signed-off-by: Michael Brown --- src/include/ipxe/pci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/include/ipxe') diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 933f48530..bf6174c23 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -309,6 +309,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 -- cgit v1.2.3-55-g7522 From cad1cc6b449b63415ffdad8e12f13df4256106fb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2022 15:02:44 +0100 Subject: [intelxl] Add driver for Intel 100 Gigabit Ethernet NICs Add a driver for the E810 family of 100 Gigabit Ethernet NICs. The core datapath is identical to that of the 40 Gigabit XL710, and this part of the code is shared between both drivers. The admin queue mechanism is sufficiently similar to make it worth reusing substantial portions of the code, with separate implementations for several commands to handle the (unnecessarily) breaking changes in data structure layouts. The major differences are in the mechanisms for programming queue contexts (where the E810 abandons TX/RX symmetry) and for configuring the transmit scheduler and receive filters: these portions are sufficiently different to justify a separate driver. Signed-off-by: Michael Brown --- src/drivers/net/ice.c | 986 +++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/ice.h | 565 ++++++++++++++++++++++++++ src/drivers/net/intelxl.c | 14 +- src/drivers/net/intelxl.h | 10 + src/include/ipxe/errfile.h | 1 + 5 files changed, 1569 insertions(+), 7 deletions(-) create mode 100644 src/drivers/net/ice.c create mode 100644 src/drivers/net/ice.h (limited to 'src/include/ipxe') 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 . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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 c8c527d53..82b07833c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -123,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, @@ -575,7 +575,7 @@ static int intelxl_admin_mac_write ( struct net_device *netdev ) { * @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; int rc; @@ -726,7 +726,7 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { * @v intelxl Intel device * @ret rc Return status code */ -static int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { +int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_mac_config_params *config; int rc; @@ -1280,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; @@ -1314,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 */ diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index ad15ca99f..0db16b581 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -925,6 +925,8 @@ struct intelxl_nic { unsigned int vsi; /** Queue set handle */ unsigned int qset; + /** Transmit element ID */ + uint32_t teid; /** Interrupt control register */ unsigned int intr; /** PCI Express capability offset */ @@ -959,6 +961,8 @@ struct intelxl_nic { 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, unsigned int vector ); @@ -970,6 +974,8 @@ 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 ); @@ -978,6 +984,10 @@ 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 ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 81f555725..359e4d2ce 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -215,6 +215,7 @@ 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_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) -- cgit v1.2.3-55-g7522 From 8fc3c26eae8d45a5391a39ee698817449299bd76 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Sep 2022 14:55:26 +0100 Subject: [pci] Allow pci_find_next() to return non-zero PCI segments Separate the return status code from the returned PCI bus:dev.fn address, in order to allow pci_find_next() to be used to find devices with a non-zero PCI segment number. Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 16 ++++++++-------- src/hci/commands/pci_cmd.c | 12 +++++------- src/include/ipxe/pci.h | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'src/include/ipxe') diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 5891e42ff..60d5df112 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -229,9 +229,10 @@ 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 ) { +int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ) { static unsigned int end; unsigned int sub_end; uint8_t hdrtype; @@ -243,11 +244,11 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 ); /* Find next PCI device, if any */ - for ( ; busdevfn < end ; busdevfn++ ) { + for ( ; *busdevfn < end ; (*busdevfn)++ ) { /* 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; @@ -267,7 +268,7 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { } /* Return this device */ - return busdevfn; + return 0; } return -ENODEV; @@ -348,7 +349,7 @@ 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++ ) { @@ -362,8 +363,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 */ diff --git a/src/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c index a2a811aa0..515798baf 100644 --- a/src/hci/commands/pci_cmd.c +++ b/src/hci/commands/pci_cmd.c @@ -58,7 +58,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 +75,15 @@ 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 ); } /* 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,7 +94,7 @@ 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; diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index bf6174c23..bd123679a 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -301,7 +301,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 ); -- cgit v1.2.3-55-g7522 From 56b30364c5db6367279ffe88929f286f15680b40 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Sep 2022 15:04:01 +0100 Subject: [pci] Check for wraparound in callers of pci_find_next() The semantics of the bus:dev.fn parameter passed to pci_find_next() are "find the first existent PCI device at this address or higher", with the caller expected to increment the address between finding devices. This does not allow the parameter to distinguish between the two cases "start from address zero" and "wrapped after incrementing maximal possible address", which could therefore lead to an infinite loop in the degenerate case that a device with address ffff:ff:1f.7 really exists. Fix by checking for wraparound in the caller (which is already responsible for performing the increment). Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 6 +++--- src/hci/commands/pci_cmd.c | 6 ++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/include/ipxe') diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 60d5df112..c5deb08f6 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -352,8 +352,7 @@ static int pcibus_probe ( struct root_device *rootdev ) { uint32_t busdevfn = 0; int rc; - for ( busdevfn = 0 ; 1 ; busdevfn++ ) { - + do { /* Allocate struct pci_device */ if ( ! pci ) pci = malloc ( sizeof ( *pci ) ); @@ -386,7 +385,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/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c index 515798baf..5bae66fbe 100644 --- a/src/hci/commands/pci_cmd.c +++ b/src/hci/commands/pci_cmd.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -79,6 +80,10 @@ static int pciscan_exec ( int argc, char **argv ) { } else { /* Setting is defined: start searching from next location */ busdevfn = ( prev + 1 ); + if ( ! busdevfn ) { + rc = -ENOENT; + goto err_end; + } } /* Find next existent PCI device */ @@ -101,6 +106,7 @@ static int pciscan_exec ( int argc, char **argv ) { } err_store: + err_end: err_find_next: err_parse_setting: err_parse_options: diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 359e4d2ce..9b955e574 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -397,6 +397,7 @@ 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 ) /** @} */ -- cgit v1.2.3-55-g7522 From ff228f745c15594291fd3cbf3c02af27753a3885 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Sep 2022 16:47:04 +0100 Subject: [pci] Generalise pci_num_bus() to pci_discover() Allow pci_find_next() to discover devices beyond the first PCI segment, by generalising pci_num_bus() (which implicitly assumes that there is only a single PCI segment) with pci_discover() (which has the ability to return an arbitrary contiguous chunk of PCI bus:dev.fn address space). Signed-off-by: Michael Brown --- src/arch/x86/core/pcidirect.c | 2 +- src/arch/x86/include/ipxe/pcidirect.h | 14 +++++++++----- src/arch/x86/interface/pcbios/pcibios.c | 14 +++++++++----- src/drivers/bus/pci.c | 34 ++++++++++++++++++++------------- src/include/ipxe/efi/efi_pci_api.h | 13 ++++++++----- src/include/ipxe/linux/linux_pci.h | 16 ++++++++++------ src/include/ipxe/pci.h | 3 --- src/include/ipxe/pci_io.h | 21 +++++++++++++++++--- src/interface/efi/efi_pci.c | 2 +- src/interface/linux/linux_pci.c | 2 +- 10 files changed, 78 insertions(+), 43 deletions(-) (limited to 'src/include/ipxe') diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8226fea..88db90499 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 ); diff --git a/src/arch/x86/include/ipxe/pcidirect.h b/src/arch/x86/include/ipxe/pcidirect.h index decdc8100..394edb2b8 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 ); } /** diff --git a/src/arch/x86/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c index bf812f77f..cf818630a 100644 --- a/src/arch/x86/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -34,11 +34,13 @@ 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; @@ -57,7 +59,9 @@ static int pcibios_num_bus ( void ) { "D" ( 0 ) : "ebx", "edx" ); - return ( max_bus + 1 ); + /* Populate range */ + range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 ); + range->count = PCI_BUSDEVFN ( 0, ( max_bus + 1 ), 0, 0 ); } /** @@ -114,7 +118,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 ); diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index c5deb08f6..7953aaedd 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -233,18 +233,23 @@ int pci_read_config ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ) { - static unsigned int end; - unsigned int sub_end; + 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 ) ); @@ -252,24 +257,27 @@ int pci_find_next ( struct pci_device *pci, uint32_t *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 0; - } + + } while ( ++(*busdevfn) ); return -ENODEV; } 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/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/pci.h b/src/include/ipxe/pci.h index bd123679a..c91baaddd 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -262,9 +262,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 ) diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 2dcdd9b28..91359cec8 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -14,6 +14,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct pci_device; + +/** 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 * @@ -51,11 +65,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include /** - * 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 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 e3c0daa32..300844737 100644 --- a/src/interface/linux/linux_pci.c +++ b/src/interface/linux/linux_pci.c @@ -188,7 +188,7 @@ int linux_pci_write ( struct pci_device *pci, unsigned long where, return rc; } -PROVIDE_PCIAPI_INLINE ( linux, pci_num_bus ); +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 ); -- cgit v1.2.3-55-g7522 From be667ba94822877036f6c80992554ed32314a1f2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 14 Sep 2022 14:29:52 +0100 Subject: [pci] Add support for the Enhanced Configuration Access Mechanism (ECAM) The ACPI MCFG table describes a direct mapping of PCI configuration space into MMIO space. This mapping allows access to extended configuration space (up to 4096 bytes) and also provides for the existence of multiple host bridges. Add support for the ECAM mechanism described by the ACPI MCFG table, as a selectable PCI I/O API alongside the existing PCI BIOS and Type 1 mechanisms. Signed-off-by: Michael Brown --- src/drivers/bus/ecam.c | 265 +++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/ecam.h | 55 ++++++++++ src/include/ipxe/ecam_io.h | 139 ++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/pci_io.h | 1 + 5 files changed, 461 insertions(+) create mode 100644 src/drivers/bus/ecam.c create mode 100644 src/include/ipxe/ecam.h create mode 100644 src/include/ipxe/ecam_io.h (limited to 'src/include/ipxe') diff --git a/src/drivers/bus/ecam.c b/src/drivers/bus/ecam.c new file mode 100644 index 000000000..f7ba2db7f --- /dev/null +++ b/src/drivers/bus/ecam.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @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 ); diff --git a/src/include/ipxe/ecam.h b/src/include/ipxe/ecam.h new file mode 100644 index 000000000..0f0fbf4bf --- /dev/null +++ b/src/include/ipxe/ecam.h @@ -0,0 +1,55 @@ +#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 +#include + +/** 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; +}; + +#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 + +#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/errfile.h b/src/include/ipxe/errfile.h index 9b955e574..2b8adbfd6 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -216,6 +216,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #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_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 91359cec8..35d16f95e 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -58,6 +58,7 @@ struct pci_range { PROVIDE_SINGLE_API_INLINE ( PCIAPI_PREFIX_ ## _subsys, _api_func ) /* Include all architecture-independent I/O API headers */ +#include #include #include -- cgit v1.2.3-55-g7522 From 649176cd608e74ce54d20488a0618b4c6d8be71d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 18 Sep 2022 13:41:21 +0100 Subject: [pci] Select PCI I/O API at runtime for cloud images Pretty much all physical machines and off-the-shelf virtual machines will provide a functional PCI BIOS. We therefore default to using only the PCI BIOS, with no fallback to an alternative mechanism if the PCI BIOS fails. AWS EC2 provides the opportunity to experience some exceptions to this rule. For example, the t3a.nano instances in eu-west-1 have no functional PCI BIOS at all. As of commit 83516ba ("[cloud] Use PCIAPI_DIRECT for cloud images") we therefore use direct Type 1 configuration space accesses in the images built and published for use in the cloud. Recent experience has discovered yet more variation in AWS EC2 instances. For example, some of the metal instance types have multiple PCI host bridges and the direct Type 1 accesses therefore see only a subset of the PCI devices. Attempt to accommodate future such variations by making the PCI I/O API selectable at runtime and choosing ECAM (if available), falling back to the PCI BIOS (if available), then finally falling back to direct Type 1 accesses. This is implemented as a dedicated PCIAPI_CLOUD API, rather than by having the PCI core select a suitable API at runtime (as was done for timers in commit 302f1ee ("[time] Allow timer to be selected at runtime"). The common case will remain that only the PCI BIOS API is required, and we would prefer to retain the optimisations that come from inlining the configuration space accesses in this common case. Cloud images are (at present) disk images rather than ROM images, and so the increased code size required for this design approach in the PCIAPI_CLOUD case is acceptable. Signed-off-by: Michael Brown --- src/arch/x86/core/pcidirect.c | 2 + src/arch/x86/include/bits/pci_io.h | 1 + src/arch/x86/include/ipxe/pcibios.h | 2 + src/arch/x86/include/ipxe/pcicloud.h | 18 +++ src/arch/x86/include/ipxe/pcidirect.h | 2 + src/arch/x86/interface/pcbios/pcibios.c | 2 + src/arch/x86/interface/pcbios/pcicloud.c | 191 +++++++++++++++++++++++++++++++ src/config/cloud/ioapi.h | 2 +- src/drivers/bus/ecam.c | 2 + src/include/ipxe/ecam.h | 2 + src/include/ipxe/pci_io.h | 33 ++++++ 11 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 src/arch/x86/include/ipxe/pcicloud.h create mode 100644 src/arch/x86/interface/pcbios/pcicloud.c (limited to 'src/include/ipxe') diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 88db90499..f4659a1ac 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -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 #include +#include #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 394edb2b8..98c6a2bbb 100644 --- a/src/arch/x86/include/ipxe/pcidirect.h +++ b/src/arch/x86/include/ipxe/pcidirect.h @@ -155,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 6f31ce943..7b7a769e3 100644 --- a/src/arch/x86/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -128,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 . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** @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/drivers/bus/ecam.c b/src/drivers/bus/ecam.c index f7ba2db7f..1d57bd2a0 100644 --- a/src/drivers/bus/ecam.c +++ b/src/drivers/bus/ecam.c @@ -263,3 +263,5 @@ 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/include/ipxe/ecam.h b/src/include/ipxe/ecam.h index 0f0fbf4bf..683d613a0 100644 --- a/src/include/ipxe/ecam.h +++ b/src/include/ipxe/ecam.h @@ -52,4 +52,6 @@ struct ecam_mapping { void *regs; }; +extern struct pci_api ecam_api; + #endif /* _IPXE_ECAM_H */ diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 35d16f95e..4c035b18b 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include struct pci_device; +struct pci_api; /** A PCI bus:dev.fn address range */ struct pci_range { @@ -149,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 */ -- cgit v1.2.3-55-g7522 From 3aa6b79c8d9dd0b4126c966aa83114dd0b9b4120 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 19 Sep 2022 17:47:57 +0100 Subject: [pci] Add minimal PCI bridge driver Add a minimal driver for PCI bridges that can be used to locate the bridge to which a PCI device is attached. Signed-off-by: Michael Brown --- src/drivers/bus/pcibridge.c | 132 +++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/pci.h | 15 +++++ src/include/ipxe/pcibridge.h | 43 ++++++++++++++ 4 files changed, 191 insertions(+) create mode 100644 src/drivers/bus/pcibridge.c create mode 100644 src/include/ipxe/pcibridge.h (limited to 'src/include/ipxe') 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 . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * 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/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2b8adbfd6..1fca74742 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -217,6 +217,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #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 ) diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index c91baaddd..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) 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 +#include +#include + +/** 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 */ -- cgit v1.2.3-55-g7522 From 007d3cb800fd0e4b01be8a76f0cce2c795cfc89b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 9 Oct 2022 15:14:41 +0100 Subject: [crypto] Simplify internal HMAC API Simplify the internal HMAC API so that the key is provided only at the point of calling hmac_init(), and the (potentially reduced) key is stored as part of the context for later use by hmac_final(). This simplifies the calling code, and avoids the need for callers such as TLS to allocate a potentially variable length block in order to retain a copy of the unmodified key. Signed-off-by: Michael Brown --- src/crypto/hmac.c | 96 ++++++++++++++++------------------------------- src/crypto/hmac_drbg.c | 16 +++----- src/crypto/ntlm.c | 19 ++++------ src/crypto/sha1extra.c | 16 ++++---- src/include/ipxe/hmac.h | 40 +++++++++++++++----- src/include/ipxe/md4.h | 3 ++ src/include/ipxe/md5.h | 3 ++ src/include/ipxe/sha1.h | 3 ++ src/include/ipxe/sha256.h | 3 ++ src/include/ipxe/sha512.h | 3 ++ src/net/80211/wpa_ccmp.c | 11 +++--- src/net/80211/wpa_tkip.c | 9 ++--- src/net/pccrc.c | 9 ++--- src/net/tls.c | 51 +++++++++++-------------- src/tests/hmac_test.c | 14 +++---- src/tests/pccrc_test.c | 9 ++--- 16 files changed, 142 insertions(+), 163 deletions(-) (limited to 'src/include/ipxe') 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 @@ -46,94 +46,62 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** - * 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/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/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 +/** 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/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/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/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/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..21f707340 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -458,17 +458,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 +485,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 +526,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 +545,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; @@ -2213,7 +2207,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 +2239,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 +2256,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 +2538,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; diff --git a/src/tests/hmac_test.c b/src/tests/hmac_test.c index 871926f90..5267999e4 100644 --- a/src/tests/hmac_test.c +++ b/src/tests/hmac_test.c @@ -100,26 +100,22 @@ struct hmac_test { static void hmac_okx ( struct hmac_test *test, const char *file, unsigned int line ) { struct digest_algorithm *digest = test->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t hmac[digest->digestsize]; - uint8_t key[test->key_len]; - size_t key_len; /* Sanity checks */ + okx ( sizeof ( ctx ) == ( digest->ctxsize + digest->blocksize ), + file, line ); okx ( test->expected_len == digest->digestsize, file, line ); - /* Create modifiable copy of key */ - memcpy ( key, test->key, test->key_len ); - key_len = test->key_len; - /* 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, key, &key_len ); + hmac_init ( digest, ctx, test->key, test->key_len ); hmac_update ( digest, ctx, test->data, test->data_len ); - hmac_final ( digest, ctx, key, &key_len, hmac ); + hmac_final ( digest, ctx, hmac ); DBGC ( test, "HMAC-%s result:\n", digest->name ); DBGC_HDA ( test, 0, hmac, sizeof ( hmac ) ); 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, -- cgit v1.2.3-55-g7522 From 18b861024ad542bd6df337dfb10a84e04173040f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 4 Oct 2022 15:03:28 +0100 Subject: [crypto] Add Ephemeral Diffie-Hellman key exchange algorithm Add an implementation of the Ephemeral Diffie-Hellman key exchange algorithm as defined in RFC2631, with test vectors taken from the NIST Cryptographic Toolkit. Signed-off-by: Michael Brown --- src/crypto/dhe.c | 133 ++++++++ src/include/ipxe/dhe.h | 19 ++ src/include/ipxe/errfile.h | 1 + src/tests/dhe_test.c | 782 +++++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 5 files changed, 936 insertions(+) create mode 100644 src/crypto/dhe.c create mode 100644 src/include/ipxe/dhe.h create mode 100644 src/tests/dhe_test.c (limited to 'src/include/ipxe') 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 . + * + * 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 +#include +#include +#include +#include + +/** + * 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/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 + +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/errfile.h b/src/include/ipxe/errfile.h index 1fca74742..c3541e8a0 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -400,6 +400,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #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/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 . + * + * 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 +#include +#include +#include + +/** 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/tests.c b/src/tests/tests.c index dde1b2323..54694fa45 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -78,3 +78,4 @@ REQUIRE_OBJECT ( gzip_test ); REQUIRE_OBJECT ( utf8_test ); REQUIRE_OBJECT ( acpi_test ); REQUIRE_OBJECT ( hmac_test ); +REQUIRE_OBJECT ( dhe_test ); -- cgit v1.2.3-55-g7522 From 028aac99a397f591de6cc6f6f2b4763f55aa8962 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:47:06 +0100 Subject: [tls] Generate pre-master secret at point of sending ClientKeyExchange The pre-master secret is currently constructed at the time of instantiating the TLS connection. This precludes the use of key exchange mechanisms such as Ephemeral Diffie-Hellman (DHE), which require a ServerKeyExchange message to exchange additional key material before the pre-master secret can be constructed. Allow for the use of such cipher suites by deferring generation of the master secret until the point of sending the ClientKeyExchange message. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 13 +++---------- src/net/tls.c | 40 ++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 26 deletions(-) (limited to 'src/include/ipxe') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8b03579cc..65608970a 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -48,6 +48,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 @@ -209,14 +212,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,8 +304,6 @@ 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 */ diff --git a/src/net/tls.c b/src/net/tls.c index 89ed6fceb..4fb5b179e 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -616,21 +616,23 @@ static void tls_prf ( struct tls_connection *tls, const 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 ), @@ -1211,6 +1213,10 @@ static int tls_send_client_key_exchange ( 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; @@ -1220,8 +1226,16 @@ 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 ); + tls_generate_master_secret ( tls, &pre_master_secret, + sizeof ( pre_master_secret ) ); /* Generate keys */ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { @@ -1233,8 +1247,7 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { /* 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; @@ -3173,7 +3186,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 ); @@ -3186,11 +3199,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 ); -- cgit v1.2.3-55-g7522 From 80c45c5c71af76e4313c37528d29aa485b247073 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:49:57 +0100 Subject: [tls] Record ServerKeyExchange record, if provided Accept and record the ServerKeyExchange record, which is required for key exchange mechanisms such as Ephemeral Diffie-Hellman (DHE). Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 4 ++++ src/net/tls.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'src/include/ipxe') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 65608970a..672cfbd7e 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -310,6 +310,10 @@ struct tls_connection { 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 */ diff --git a/src/net/tls.c b/src/net/tls.c index 4fb5b179e..a1ffcacd7 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -377,6 +377,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 ); @@ -1867,6 +1868,37 @@ static int tls_new_certificate ( struct tls_connection *tls, return 0; } +/** + * 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 * @@ -2100,6 +2132,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 ); -- cgit v1.2.3-55-g7522 From ea33ea33c0d77b853c39d7b0e8c54f1a6f56b6bc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:54:34 +0100 Subject: [tls] Add key exchange mechanism to definition of cipher suite Allow for the key exchange mechanism to vary depending upon the selected cipher suite. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 2 ++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 2 ++ src/include/ipxe/tls.h | 19 +++++++++++++++++++ src/net/tls.c | 28 +++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 3 deletions(-) (limited to 'src/include/ipxe') 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/include/ipxe/tls.h b/src/include/ipxe/tls.h index 672cfbd7e..80cdd12fe 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 #include +struct tls_connection; + /** A TLS header */ struct tls_header { /** Content type @@ -143,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 */ @@ -385,6 +402,8 @@ struct tls_connection { /** RX I/O buffer alignment */ #define TLS_RX_ALIGN 16 +extern struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm; + extern int add_tls ( struct interface *xfer, const char *name, struct x509_root *root, struct private_key *key ); diff --git a/src/net/tls.c b/src/net/tls.c index a1ffcacd7..b209e0d80 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -734,6 +734,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, @@ -849,7 +850,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 ); @@ -1205,12 +1207,12 @@ 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 ); @@ -1269,6 +1271,26 @@ 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 + * + * @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 * -- cgit v1.2.3-55-g7522 From 6b2c94d3a7d93a8fc47fcb0b895477d4dafca5f0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:55:56 +0100 Subject: [tls] Add support for Ephemeral Diffie-Hellman key exchange Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 1 + src/net/tls.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) (limited to 'src/include/ipxe') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 80cdd12fe..6d6c82de0 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -403,6 +403,7 @@ struct tls_connection { #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/net/tls.c b/src/net/tls.c index b209e0d80..4aa4d9e29 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -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, \ @@ -915,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 @@ -1277,6 +1324,205 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { .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 * -- cgit v1.2.3-55-g7522